Friday, September 21, 2007

 

Side Effect Oriented Development

Product release nears, must be time for another blog post.

Every developer knows (or at least is told) about how side effects are bad. They make program behaviour unpredictable. Simple code modifications can produce new and wonderful bugs. The code base I'm working on has an ancient heritage, and has been worked on a by a number of developers of varying experience and skill. A number of relatively poor design decisions were made very early on, and current developers are suffering.

So I would like to coin the term "side effect oriented development". I know this is a new term, since I Googled it, and Google is all-knowing. 0 hits. It's mine!

What exactly is it?

Side effect oriented development is the tools and mindset necessary to work on code that depends on so many side effects. It is an attitude to fix, remove, and re-design side effect driven code. It is the ability to recognize the dangers of changing that one inconspicuous line that will bring the app to it's knees.

It is also one of the most frustrating environments for a developer to work in.

Imagine code that actually depends on memory leaks. Fix a memory leak, cause a crash.
Imagine being afraid of making even the simplest of fixes, because you don't know what will happen to some other unrelated piece of code.
Imagine no unit tests.

It takes a fair amount of discipline and creativity to deal with code like that.

First, isolate your change to a small area of code at a time, such as a single function. Avoid wholesale changes, as they are more likely to introduce hard to track bugs. Small changes will limit the damage.

Second, analyze the potential impact your change may have. This usually requires good knowledge of the code, and can be difficult to understand. One of the best tools for determining how a particular code change will impact the whole project is an application called Understand for C++. If you are dealing with difficult code, this tool is a must. It allows you to browse a function's calltree, both invocation and call-by. It even has a scripting capability so that you can extend it's functionality. A real life saver, and well worth the investment.

Third, you need to fix the known side effects. Ideally, this means that you modify the code that depended on the old side effect which your brand new code hopefully removed. This can sometimes introduce a cascade effect, requiring more and more code changes throughout the calltree. At some point, you need to introduce a layer where you preserve existing side effect behaviour so that you can limit the damage. You can then go back later and remove your intentional side effect once you have validated that the new code works.

Fourth, verification. This can be difficult if you don't have unit tests, especially if your code is large, monolithic and poorly organized. At worst you should have regression tests that can be run. While not as good as well written unit tests, they can at least give you some comfort that your change hasn't completely borked a section of code you thought was unrelated. Once you are satisfied that your change is correct, go back to three and clean up your intentional side effects.

These are fairly general recommendations. I don't want to go into too much depth, but needless to say, it can quite a chore to fix code like this. I look forward to the day when all of the side effect dependent code is removed.

This page is powered by Blogger. Isn't yours?