Do it the dumb way first
At the outset of any new project (or feature), it is extremely tempting to start trying to design "good software" from a blank slate. But in my painful experience, this is almost always a mistake.
Two commonly cited acronyms that programmers live by are:
DRY - Don’t Repeat Yourself
and
YAGNI - You Aren’t Gonna Need It
DRY is probably the most often obeyed: instead of copy-pasting a thirty-line chunk of code in ten different places, a savvy programmer will opt to turn that chunk into a function which can be called in those ten places.
YAGNI is less often followed, but is no less important: instead of trying to anticipate every possible future need for the program, just build what is needed now. Experience with software development shows that the chances are good that you don’t entirely have the right requirements for what is needed now let alone future needs!
In fact, experience shows that you’ll probably need to build-it-twice!
These two acronyms are slightly opposed. DRY tends towards creating abstractions and YAGNI tends towards avoiding abstractions unless they’re actually needed.
What I now advocate and try to practice is slightly tangential to both of these (but probably a spiritual cousin to YAGNI): "Do it the dumb way first". I mean, just get in there and write some terrible code that does the task in the most obvious way possible. Copy and paste little chunks. Have crappy function and variable names. Try to stick with it as long as you can bear.
(By the way, if you’re writing a one-off script and it works, you’re now done!)
But what I find is that about three quarters of the way into it (or halfway or sometimes just a quarter of the way), a much better solution occurs to me and I immediately delete most of the dumb code and put in the better solution.
Often, without writing the dumb code, the better solution would not have occurred to me. This is very similar to the principle that it’s much easier to criticize something that exists than it is to create something from scratch.
But more importantly, the "better" solution I would have tried to engineer without first writing the dumb code would have been wrong!
Or worse than wrong: Right 97% of the time!
It’s all about edge cases and changes. I don’t know about you, but I find that most of the challenges in programming aren’t the "happy path" (or, really, the "main problem" being solved) at all. They’re the edge cases that appear when you actually build the darn thing. Or the requirements that change due to unforseen circumstances.
I think this is partially because software is just so danged abstract.
Imagine you’ve been instructed to build a railway and a train to travel that railway to haul cargo. Everything is going great for miles and miles of perfectly flat terrain and then you hit a body of water. It turns out that this is an ocean. The client knew about this, but they didn’t know it would be a problem. They don’t understand why you’re foaming at the mouth ranting about needing airplanes rather than trains.
"Why can’t you just build a bridge?" they ask. "You did that for the rivers."
This level of disconnect would be absurd in physical, real-world projects.
But this stuff happens all the time in software. And it’s not just client requirements with surprises in them. It happens often when you work with a halfway complicated API or always when you’re dealing with something arcane and rife with historical pitfalls like terminal emulators or Web browsers! It turns out that last little body of water is actually an ocean you never knew existed and you simply cannot get there from here.
Until we all start using formal methods and well-defined specs to plan our programs (I’m not holding my breath on either), I think it’s more useful and, frankly, more fun and better for momentum, to write dumb test code that you know you’ll be replacing with something better.
If your dumb train can get from point A to point B without running into any oceans, then your well-engineered train will too.
From another angle, dumb test code is the sketch over which you’ll block out your painting. It’s how you see the shape of the picture before committing to the details. If the sketch looks good when you squint at it from across the room, the finished painting has a chance to look good too.
By the way, the Wikipedia page for DRY lists two alternatives I really love:
WET - Write Everything Twice
and
AHA - Avoid Hasty Abstractions
In particular, AHA is another way of phrasing this entire page: don’t jump into engineering the "correct" solution right away. Write the dumb code first to make sure you’re solving the right problem and have seen the edge cases.
I truly believe that short of absurd "Daily WTF" levels of repetition, nothing is worse than the wrong abstraction! Sandi Metz writes brilliantly about this in The Wrong Abstraction (sandimetz.com).
Also, some repetition just can’t reasonably be avoided. I’ve run into the "not quite the same" problem so many times, I’ve lost count.
Back to computer programming.
Counter-point: tiger-style