“Legacy code is code without tests” – Michael Feathers
My dayjob is working on an iOS app, I also have an evening side project building a Mac application. In both cases I am the only developer.
This has some benefits. Mostly, I set my own direction for the design of the code. Mostly, I get to make my own estimates. Mostly, I don’t need to explain my thinking to anyone else, I just get on and do it.
This also has some drawbacks. Mostly, I have to set my own direction for the design of the code. Mostly, I have to meet my own estimates. Mostly, I don’t get to explain my thinking to anyone else, I have to just get on and do it.
Lately, I’ve been writing legacy code. I’ve just been running from one feature to the next, trying to hit my own self-imposed deadlines and estimates. Trying to get code out the door to provide features for my customers as quickly as possible. As soon as something seems to basically work, I ship it and move on to the next thing.
This is often how a startup functions, getting a “working prototype” out the door so people can see the potential for the real thing. Getting something in front of potential customers, or potential investors. In that case, it’s often a necessary thing. It’s sometimes called ‘Spike and Stabilize’.
Of course, one big outcome from this approach is lots of bugs. And in particular bugs that are really hard to track down, because this is legacy code. The end result is a piece of software that does the immediate job required of it, but isn’t really doing the best for the customer in the long term, and doesn’t satisfy me at all.
The temptation then is strong to do a complete rewrite. “I’ve learned how to do it now. I’ll keep the customers working along on the sketchy prototype and I’ll write the real version in parallel.”
Now I have two problems.
Once again, I have to hit my self-imposed deadlines. “They’re waiting for this. I said I could do it, every hour its not shipped is an hour I’ve spent re-implementing my own mistakes. Go faster!”
But it’s actually worse on the rewrite. By this point I have an entire application to replicate and so I’ll usually spend a long time up front in whats usually called “Analysis Paralysis”. You see I know from history that I will probably code myself a working prototype and end up with an unmaintainable bunch of legacy code, and so I spend a lot of time before I start, planning a design for the entire application, terrified of writing that first line of code. I am convinced that if I just think hard enough about it, I’ll get it right this time. Spoiler alert: I don’t.
It wasn’t always this way.
There was a time when I would approach each feature in isolation, and build it right. Working through the possible error cases until it was solid, taking time to experiment with an API until I understood it well enough to use it properly in that situation. Taking time to think, and act deliberately, and still have a measurable indicator of progress. Understanding that I own my own estimates and that quality wasn’t negotiable.
“How would you program if you had enough time?” – XP Maxim
Back before I was an Objective-C programmer, I was a Ruby Programmer. And before that I was a Java programmer. And when I was a Java programmer I wouldn’t have dreamt of writing code this way, because I was a firm believer in Extreme Programming, aka XP.
XP says that every line of code we write should be production software, and it’s better to build 5% of of the features, where each one is 100% complete and production ready than it is to build 100% of features where none of them can really be relied on.
XP also assumes from the outset that there’s no way we can possibly think hard enough about the problem up from to get it right. It says that we cant figure out the optimal design for a system until we’ve built it. Even if we’ve built it before.
Since thats both of my current strategies dismissed, what should I be doing instead? How did I write software before ?
Using TDD, I wrote small pieces of code, testing as I went, and refactored the system at regular intervals to ensure the design was as clean and simple as it needed to be for the currently implemented feature set.
When I needed to use a new or unfamiliar API, I would take as much time as required to understand it, rather than just cargo-culting from StackOverflow or installing a gem or cocoapod that seemed to do the job.
When I built my software I’d work from the business objects out, rather than from the controller layer in.
“I wish I could take a year off being a programmer so I could learn how to be a programmer” – Gavin Montague
So here we are. I feel the last 3-4 years of my life as a developer have been a steady eroding of the things I know make good software, and made me a good programmer. It’s been too easy for me, in Rails and Cocoa, to work my way to a 60% solution through wizards and generators and templates and libraries and then be stuck, unable to build that last 40% because I don’t truly understand my own software. It’s too easy to program by co-incidence instead of deliberately.
It’s time for a reboot, and it starts here
“I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach” – Thoreau, Walden