Practical Elegance

"Elegance is not a dispensable luxury." - Edsgar Dijkstra

Be Willing To Let Go; or, "The Big Rewrite"

Is it possible that software is not like anything else, that it is meant to be discarded: that the whole point is to always see it as a soap bubble?

Alan J. Perlis

One of the most nerve-wracking things you can tell a manager is that it’s time to rewrite a major software component, system, or application. In fact, it’s considered (in some circles) such a mortal sin that Joel Spolsky addresses “The Big Rewrite” in a post entitled Things You Should Never Do, Part I.

In many cases, he’s absolutely right. If you have a working-ish production codebase, throwing it out the window can seem a bit like throwing out the baby with the bathwater. But from an engineering standpoint, software is weird. On the one hand, you have the engineering side: If a bridge is just working-ish, the assessment of a structural engineer might be that it must be torn down and a new bridge built in its place. Software is much more ethereal and abstract; of course, we can continue to patch the old application, and extend it, and patch it, and sacrifice virgins to Great Cthulhu, and keep it chugging along. For a while.

Eventually, however, with a typical enormous, legacy codebase, you just end up mortgaging massive technical debt.

Refinancing and Bankruptcy

A large, enterprise-y system will usually be so entrenched in technical debt that we can (metaphorically) view it as being double- and triple-mortgaged. Test coverage is spotty (if it even exists), and more time is spent dealing with regressions than actually implementing new features in a sane manner.

Eventually, technical debt piles to the point at which it’s time to claim bankruptcy. And the problem is that the beancounters financing software projects (or IT managers who have no business managing software projects) see it as bankruptcy in the traditional sense.

If the original project was poorly architected, rife with Demeter violations and cross-dependencies, coupled with lousy (or no) test coverage, this might be a somewhat accurate assessment. But sometimes a project with a sensible class hierarchy, composable elements, and a modular structure needs “The Big Rewrite”, and it becomes difficult to explain what this really entails.

Reusable code

I’m thinking about this in terms of a Rails app that I built some time ago, which really should have been considered a “throwaway prototype”, except that the business needs dictated it went into production right away. It works, but… lousy test coverage, bizarre performance issues, and regression issues make each new deploy a new headache.

It took me some time, but I’ve finally made the argument for the “big rewrite.”

Now, in the mean time, I’ve created a REST API for the backend as part of an SOA for the systems involved. And I’ve demonstrated that much of the model layer from the “deprecated” application was reused (or improved) for the API that I’m using to abstract the intrface to the legacy system the inital application was designed to interface with.

The “big rewrite” entails rebuilding the tools in the old application to work through the API instead of directly hitting the DB (an Oracle monstrosity with ~500 tables and an incomprehensible collection of package functions and stored procedures). But the code was reusable in a new context, and the “rewrite” is really a protracted refactoring and rebuilding of the data layer.

That Lisp Thing

The quote at the head of this article was taken from the preface to the second edition of The Structure and Interpretation of Computer Programs, aka the “Wizard Book.” SICP uses the Scheme dialect of Lisp for its examples, and Lisp is a beautiful language for writing composable code, as well as disposable code. It simply makes sense to write small, composable functions, and to use those functions to build abstractions for higher-level programming. This is what Paul Graham calls Programming Bottom-Up.

I think when Rails programming is done well, it has a similar bottom-up aspect, in that one starts with the data model, then deals with the controllers, and the views are the final consideration. Certainly not everyone approaches Rails development from this perspective, but I think the best Rails apps exhibit this approach. The Windows development model of “Visual Foo” and “Big Design Up Front” are the anithesis, and I think this is why most Windows programs are terrible, and why Windows programmers are (in my experience) more likely to be okay with terrible code.

Lewis’s First Rule

I’ve never even thought to come up with a “rule” of software design before, but this is the most important lesson I’ve learned in my 26 or so years of hobbyist, amateur, and professional software development. I think it’s more important than any single design pattern (although many of the patterns in the Gang of Four book embody this rule), architectural principle, or other aphorism (excpet, of course, for Hanlon’s Razor, but that extends beyond software). Lewis’s First Rule is simply this: All application code must be either composable or disposable. By composable, I inherently imply “reusable.” This is at the heart of the Go4 directive to “favor composition over inheritance.” But I also want to insist that getting rid of code is okay. It’s not only okay, it’s downright desirable. There’s no greater feeling than a git commit that reads 2 files changed, 4 insertions(+), 232 deletions(-). It’s even better when entire files have been deleted.

Don’t Fear The Rewrite

A rewrite is only a scary proposition when you’re working with a codebase that never had a guiding architectural principle to begin with, has no test coverage, and has never seen a refactoring; it’s just years (maybe decades) of cruft, layered like a forest floor, with fresh leaves falling upon detritus, duff, finally decomposing into the humus that forms the core of the system, where only earthworms dare to tread.

Building long-lasting software is an admirable goal, but it’s foolhardy to think we can do this at the application level, especially as the rate of technological progress continues to exponentiate. When you do something really good, rip it out of the application, build a library, and include that. Wash, rinse, repeat. Software should be composable, or disposable. There is no in-between.