October 18, 2007

...Learn TDD with Codemanship

Formal Code Inspections in a Post-Agile World

In the old days, when computers ran on steam and a "firewall" was something you used to keep mammoths out of your office, some people used to write their programs down on scraps of paper before they went to the considerable effort and expense of carving them into stone tablets.

Even though computer programs were relatively small, they could still be pretty complicated and error prone. As God discovered when he accidentally carved "Thou shalt not cover thy neighbour's arse" into a stone tablet for his pal Moses, correcting mistakes once they'd been committed to executable code was even more expensive. So much more, in fact, that early programmers discovered that it was much more cost-effective to invest time and effort into checking the logic on their scraps of paper before they whipped out their chisels.

So they came up with various techniques to test the logic of the code they were about to write. Like hunting with spears or cave painting, these skills have been largely lost to us - which is a shame, because the actual need for that kind of verification hasn't really diminished over time. Okay, so these days you don't have to wait 6 hours for a compiler to tell you that you missed off some curly braces, but there are still a whole heap of things that can be wrong even when our programs our syntactically perfect.

I'm in favour of resurrecting some of these ancient practices, updated with a sufficient peppering of "Agile" and "OO" and maybe even a splash of "Web 2.0", because I know how much you kids like that sort of thing.

One such practice is formal code inspections. Yes, I know. Even the name sounds like slow torture. Developers don't like the "i" word. It conjures up images of dimly lit, airless rooms full of stoney-faced "engineers" obsessing over reems of documentation and source code listings and pointing out trivial nonsense like impromper use of naming conventions, non-standard formatting of nested if... statements and the obligatory digs about the lack of comments in the code.

Well, I'm with you on that one - I think those kind of inspections are nonsense, too. You can program a tool like FxCop to take care of a lot of that sort of thing - or train some monkeys: whichever is easiest for you.

A formal code inspection is more like manual unit testing. You have a specification of what the code is supposed to achieve (don't you?) You can dream up test scenarios based on those specifications. You can get a source code listing and manually execute each test case, calculating the effect of each executable code statement using your minds (if you don't have a mind, you can borrow someone else's.)

Not with me yet? OK. Here's a toy example. I've been asked to write a chunk of code that will calculate the rental price of a holiday villa based on a set of inputs. The spec I've been given reads:

The rental price of a villa is calculated as follows:

Out of peak rental season:

* For Villas with 1-2 bedrooms, the base price per night (i.e., not including supplement for swimming pool) is $100
* For Villas with 3-4 bedrooms, the base price per night is $150
* For Villas with 5-6 bedrooms, the base price per night is $180
* For Villas with more than 6 rooms, the base price is the base price for 6 rooms plus an extra $20 for every extra room

A swimming pool supplement is added to the base price, and is calculated as the area in metres squared of the pool per night. (e.g., a 5m x 5m pool adds a supplement of $25 a night)

During peak rental season, there is an extra charge of $10 per bedroom per night.

The cost of renting is the final price (including pool supplement and peak season charges) multiplied by the number of rental nights.

I go away and jot down some pseudocode on the back of an envelope that I believe will satisfy this spec.

I would like to verify my logic, so I dream up some test cases to use in simulated execution:

1. The price of a 2 bed villa for 7 nights with no pool, out of peak rental season should be ($100 + $0 + (2 x $0) x 7 = $700
2. The price of a 4 bed villa for 3 nights with a 10m x 4m pool in peak season should be ($150 + $(10 x 4) + $(4 x 10)) x 5 = $1,150

...and so on. (Question: how many test cases do you think would give adequate coverage of the logic?)

I then substitute the test data into the code and step through it line by line:

The benefit of simulated execution like this, when applied in a focused, targeted way on your most critical logic is two-fold:

1. Unlike unit testing, you actually read the code while it's being executed, which tends to draw your attention to all sorts of new test cases and can help to achieve much higher coverage.
2. It can be applied before you write the code, and at multiple levels - e.g., it can be applied to the UI workflow design, or to business processes. This means you can shift verification into "upstream" activities and eliminate more bugs at a considerably reduced cost, compared to finding and fixing them in delivered code.

I'm not suggesting you apply it to all your code - though I'd say that was for politically pragmatic reasons rather than genuinely practical considerations. You might choose to target inspections at the most complex logic, and/or at the most heavily dependend upon logic (i.e., the most widely reused).

Certainly I'm a big advocate of doing such simulated executions of interaction logic (e.g., UI storyboards) for every single reachable scenario. It may seem like a sledgehammer to crack a nut, but I have found over the years that the number of requirements misunderstandings that get picked up and resolved - and the cost of leaving those problems until UAT - vastly outweighs the up-front effort.

You can approach inspections - whether they be of UI, business process, code or other kinds of logic - in a totally iterative and test-driven way, and be fairly Agile about it, too. I don't advocate documenting the outcome of every inspection, just as long as the problems identified are being addressed. And I don't advocate spending weeks or months at the start of a project verifying your specifications. This kind of technique can be applied as and when needed throughout an Agile development process (e.g., UI storyboarding when a new user story is picked up by developers during an iteration).

Of course, my example is very simple and not OO or anything shiny and space age like that. But you can do simulated execution in an OO way, too. Remember object diagrams? You can model the state of an OO system during the execution of a test scenario, and show how the objects change at each step in the process.

Applied in a smart way, formal inspections can make a big difference to the quality of your software and to the cost of delivering it. Highly recommended.
Posted 13 years, 9 months ago on October 18, 2007