Wednesday, December 8, 2010

How to create cleaner production code

One of the reasons why creating software seems so hard, and why it is so error prone is because we (programmers) try to do too much all at once. The way most programmers write code is very much like a musician trying to compose and perform a piece simultaneously. Management, in the meantime, is interested only in the performance, and is frustrated by the delays and bugs.

How about we decompose the process a bit?

Most people who write software would agree that the work they do every day goes something like this:

(1) An issue comes up -- a new feature is needed, a feature needs to be changed, a bug needs to be fixed

(2) The issue is investigated.

(3) A solution is conceived.

(4) The code is changed, tested and submitted.


Steps (2), (3) and (4) usually are done while looking at the code.

First, the code is run (usually from the debugger) to look into the issue.

Then the code is examined closely for what might be going wrong (in the case of a bug), or how it might be changed (in the case of adding or modifying a feature or fixing a bug). Often mingled with this step are some code changes either to write out debugging information or to try out some ideas.

More often or not, at some point, there is an aha! moment: a realization of what is causing a bug, an clever and inspired fix for a bug, a slick UI change, a devious way to create some new or changed functionality.

Then the code is changed, tested (to varying degrees, depending on the culture of the organization), and submitted to source control.

Task completed.

Nothing really wrong with this process, one might think.

So why does software development have a reputation for taking too much time and for being too error-prone?

I submit that it is because programmers are trying to do too much all at once. If the software end product is analogous to the music you hear from your iPod, then programmers are like composers trying to make changes while performers are laying down the production tracks.

In the case of music, the process works better when it is decoupled a bit. Likewise in software.

How can programming activities be decoupled? Let's start here:

One of the biggest offenses committed in programming is that the initial changes programmers make tend to be included the final changes. The changes that programmers make while they are thinking things through, say, to test out an idea, often aren't completely undone if the idea is abandoned.

In other words, the "production" code usually bears the scars of edits that were made for the purposes of investigating issues and trying out solutions.

How can these scars be avoided?

A long time ago, I learned by accident that it is not particularly difficult to make the same changes twice. The second round goes very quickly, and produces infinitely better code.

Specifically, I had written some code for a CAD application I was working on, and then accidentally deleted the source files. My initial reaction was horror (this was before the days of recycle bins). Then I pulled myself together to recreate the lost code.

To my surprise, it took almost no time to recreate what I had deleted. Even more surprising was how much better the second version of the code looked.

Now I don't recommend regularly deleting the code you write (although you might want to try it as an experiment sometime). I do recommend the following, however:

(1) To investigate an issue (especially if it involve source code changes), and to try out ideas, create a "sandbox" version, where you can code and fix to your heart's content.

(2) Once you have a clear understanding of how you are going to resolve the issue, write it up in some way that will be useful to you and future programmers. Writing it up won't take much time if (a) your organization has some structured way for recording the thinking behind its code, and (b) if you know what you are talking about.

Playing around in the sandbox is a good way to meet qualification (b). For (a), I'll provide some suggestions in a future post.

(3) Get a fresh copy of the production code, and then change it to incorporate only the solution you have decided upon (and which you have proven out in the sandbox code).

Most programmers do not bother with (2) or (3), and it is no surprise that most codebases degrade to nearly incomprehensible states long before their time.

You can't afford to do steps (2) and (3)? Consider another example from my own personal experience:

Some time ago, I was assigned a project to develop a particular graphics algorithm. This algorithm had to be highly reliable, and had to handle a lot of odd situations. A number of my co-workers eagerly provided me with problematic cases, but no one offered any ideas about how to handle them. That was up to me.

I was quite sure I could come up with a very good algorithm. Each Monday, at group meeting, I would assure my manager that the project would be done by "Friday". I just didn't say which Friday.

As it turns out, it took a lot of Fridays, about fourteen to be exact. However, eventually there was a Friday on which the code worked really, really well. It handled all the cases my co-workers could think of, and more.

At this point, I could have just submitted the code, but I didn't. To my manager's dismay, I told him that I was planning to rewrite the code first. A good way to get fired, eh?

What I felt was that the code -- despite its brilliant functioning -- was a mess. I had borrowed some data structures for convenience that really had no business being part of this code. On top of it, my naming convention had evolved over the course of the project, and now there was a lot of inconsistency.

It turns out that it took Saturday and part of Sunday to completely rewrite the code -- on the order of five thousand lines of code. What surprised even me was (a) how easily the whole rewrite went, and (b) how it all ran as well as the original code.

Thinking back, it should not have been a surprise. I knew what I was talking about, I had decided on a naming convention, and all I had to do was crank out what was already in the original implementation. I did copy and paste a certain fraction of the code, refactoring it in the process.

So keep this ratio in mind: the first version took fourteen weeks, the second version about a day and a half. Also keep in mind that you usually don't want to pull this kind of thing on your manager.

On Monday, when my manager finally calmed down, I received an additional assignment related to the project: the company wanted to file a patent for the algorithm. This involved writing a clear explanation of what I had created.

Since I understood the functioning of the algorithm in great detail, it was not particularly difficult to provide explanations. The structure of the documents needed for the patent application made things easier, since my creative options were limited. The on-going feedback from the patent attorney focused my writing on the purpose at hand, namely to explain the algorithm to the patent inspector.

The total time to create all the technical documentation, including hand-drawn sketches of diagrams: about three working days.

Note that writing the production code preceded the documentation of the thinking behind the code. However, I am certain that the patent documentation could have been written before the production code just as quickly.

The moral of this story is: it is highly beneficial -- for the purposes of keeping production code clean, and for leaving a record of one's thinking -- to decouple the step from where one is just working out one's ideas from the steps where production code is changed and the underlying intentions are recorded.

Programming in this fashion will significantly increase the engineering quality of your work. Moreover, no one really needs to know you are doing this.

If you do get caught, you can use this argument: your organization benefits in real economic terms. Your documentation will enable its programming staff to understand the thinking behind the code more easily, and it will be working with cleaner production code, undoubtedly resulting in faster project completion and more reliable software products.