Friday, January 25, 2008

Let The System Design Itself

Designing before you build is a very bad habit.

I'm working on an aleatoric music system right now. (And I do mean right now, at 3:30am Friday morning. I am an insomnihacker occasionally.)

I'm using pretty much exactly the design I had anticipated using. However, when I first started, I created a ton of empty files around this design, the design I basically had in my head from go. I laid out the general object architecture as a first step. It was a disaster. The results were bulky, sloppy, and stupid. Then I remembered Ezra Zygmuntowicz saying twice at Ruby East that he believed all great projects start as hacks. So I stopped using my big dumb Object Architecture™ and wrote up a quick hack. Pretty soon it was working beautifully, and I had thrown away like twenty unnecessary files. It only took a tiny little shard of time after that for me to factor out the quick hack into a few classes and end up with nice, clean, well-factored files - the hallmark of good design, if good design even exists.

That's a real question, by the way. Does good design really exist?

Brian Marick told me that Ron Jeffries once said or wrote this of Kent Beck:

Beck has those rules for properly-factored code: 1) runs all the tests, 2) contains no duplication, 3) expresses every idea you want to express, 4) minimal number of classes and methods. When you work with these rules, you pay attention only to micro-design matters.

When I used to watch Beck do this, I was sure he was really doing macro design "in his head" and just not talking about it, because you can see the design taking shape, but he never seems to be doing anything directed to the design. So I started trying it. What I experience is that I am never doing anything directed to macro design or architecture: just making small changes, removing duplication, improving the expressiveness of little patches of code. Yet the overall design of the system improves. I swear I'm not doing it.

Kent Beck is usually my programmer role model. I don't know why I coded my first sketch of the music system in this backwards way. Just before building this system, I hammered out a quick proof-of-concept version of a system which allows you to program Lego Mindstorms robots in Ruby. I followed Beck's rules and never even considered what the design might look like. That system was harder to write, because it exploits language quirks to enable a DSL, but the process of writing it was much smoother - I didn't have to junk it midway through and start over.

It's possible that good design is kind of like good ethics. There's a reason we say both designs and people can have integrity. Using the language of design patterns to design a system before you build it literally undermines the design's integrity. Just as there are hypocrites in the world doing brutal violence in the name of a man who preached kindness, there are programmers building bulky, inelegant systems while thinking about good designs. But if you're thinking of one thing and doing another, you don't have integrity, and if you're thinking of one system and building another, your design doesn't have integrity.

And it literally happens like that. If you're building design-first, you're saying, "The system will do XYZ - I'll put that here." But just because you know it'll do XYZ, you don't necessarily know how or where, and deciding ahead of time imposes an arbitrary and unnecessary structure on your code. So then you have a file called XyzFile, which is supposed to do XYZ, and then you have other code elsewhere in the system which actually does XYZ, and it's in some other file, because the design you imagined will always be different from the design which emerges. It's like coding in Java. There's the structure of your actual program, and the structure that Java requires you to accomodate. That second structure is just unnecessary mental overhead, and the bigger your system gets, the more wasteful that overhead is. You'll have a structure that emerges naturally, whether you want to or not. It's as inevitable as gravity. You might as well just let the macro follow the micro, like it's supposed to, and get the good design which emerges naturally as a byproduct of that process.

It's important to realize that just because you can forecast what the likely design will be doesn't mean that starting off with that design is anything but a horrible, time-wasting mistake. They're called design patterns, not design templates. Designing to them is bad; seeing them emerge is good.