Thursday, July 17, 2008

Be Bold!

I saw this yesterday on (still occasionally has something worthy of a glance): Don't write from scratch:

Asked what I was not learned in school I would answer: “how to fight the urge to rewrite everything”. To a die hard software writer “rewrite it” seems like the perfect solution to every problem.


So think twice. And then think again. Wouldn’t a tiny update or bug fix do ? You need to add new features and the original system was written in Cobol/Fortran/Delphi and you do not have a clue of these languages ? Perhaps you can add the new stuff with your super-duper framework/language and just integrate it.

Delphi in the same breath as Cobol and Fortran! Oh, the pain!

The Delphi compiler, ironically enough, is one of those C programs that has you itching to rewrite. Of course, it's deeply integrated with the IDE and the debugger, and many millions of lines of customer code expect it to behave in very specific ways. Throwing it away and starting again - not a smart business move.

Unfortunately, the same reasons that prevent you from throwing it away are the same reasons to be conservative about changes; but conservative changes are local changes, local changes are short-sighted changes, and after enough years (or decades) of short-sighted changes, it's very hard to make out enough structure (no matter how long you stare) such that you can make changes that affect the program gestalt without fear of everything crashing down around your ears.

So, here's my advice for all those noble maintainers out there: when you feel like you want to rewrite, put that energy into refactoring some slice of your code base while you're implementing a new feature or fixing a particularly deep-rooted bug. Try to fully understand what it is you're changing; create test cases that verify, both positively (what it does) and negatively (what it doesn't), every aspect of the current or desired behaviour (or both, if you need to live with backward-compatibility switches and knobs).

If you've selected your slice well, and you have the test cases to back it up, you can feel free to really rip it apart and, yes, rewrite it, and spend some time fixing up all the places it integrated with the rest of the code. While you're looking into those integration spots, you'll probably notice patterns in the usage of your slice that haven't been abstracted, due to aforementioned short-sightedness. Go on. Improve the surface API of your slice in view of your now global knowledge of how it's used. Remove the cruft. Eliminate dead code paths - pernicious are those that appear to have effect locally, but have no global observable side-effect due to other effects later in time. Such code competes strongly in the Darwinian evolution of maintenance, because few people have time to confirm their suspicions that it's safe to remove - but checking this gets more costly as time goes by. Be a stern judge of unused functions. Rewrite or remove stale comments. Source control is there for a reason.

Normalization is generally good for your database design, right? Maybe the code could do with some normalization too - splitting it up and arranging the pieces where they belong. Some of the pieces might already exist - and since you'll be moving similar stuff closer together, you might get into one of those Tetris-like micro-reward feedback loops, as the code collapses in on itself. Think of the fun you'll have!

Is your code indented to 10 levels because of exception handling, pre and post conditions, superstitious lock acquiring, etc.? You know there's a simple program in there, struggling to get out. There must be - if you think you could rewrite everything more elegantly, you must surely be able to better express whatever that tiny fraction of code is doing, right? Right? :)

Small changes in code so knotted it makes you want to rewrite it is a foolish economy. Small changes are like small credit-card debts you never bother to pay down. They compound over the years. $100 a month at an introductory rate[1] - you can handle that, right? Well, 20 years of that and you'll owe over a million. If you're not brave enough to reduce the entropy of the code today, consider two things: tomorrow, you (or the next guy) will need to be even braver; and every day you fail to overcome your fear of the code, a little bit of you dies.

Just try to be quick about it, though - don't bite off more than you can chew ;)

[1] For posterity (i.e. after link death), the rate was approx. 27.9% APR


gabr said...

Great post, Barry. I fully agree with you.

Anonymous said...

If the client understood the problem at the time of definition/writing of the original code, do so.

But all to frequently, the client only realizes the REAL problem to be solved when confronted by version 1. In this case, throw the first one away. Don't spend ages beating a screwdriver into a spanner.

It is important to recognize the two different situations.

gabr said...

Not when confronted by version 1, but version 2 :)

Anonymous said...

thank you for a great post! i'm currently shoveling my way through similar stuff right now.

i must say--your approach is exactly the wish i would've had for someone working on the delphi compiler!

thank you!

m. Th. said...

Yes, of course. Small amount of small changes in a constant pace will keep both yourself and your code base in a relative stability giving both yourself and your code base the possibility to go smoothly forward. Also, the human brain isn't used to work on the same task continuously. Doing a light 'multitasking' by alternating maintenance/refactoring, R&D + adding features will maximize the throughput, seen in a strategical, long-therm manner. Also, you can use the fact that 'being there' you have that code slice in your head.

Just my 2c & HTH

Anonymous said...

Great post! We're lucky YOU are working on the delphi compiler!