
While designing software it’s easy to fall into the trap of iterative development. Iterative development allows us to work quickly, exchanging rigorous requirements-gathering for rapid design and development — and as good developers, it is our responsibility to make sure that the code we create now works well with future iterations of our program. However, I find that code created as part of this process can frequently be too complicated, too generalized, or both. When creating agile software we must keep in mind the requirements of the future, but design strictly for the requirements of the present. If we are snared by the trap of iterative development, we risk wasting time, money, and people on code that may not be useful in the future — or, even worse, code that the client doesn’t want and can’t use.
Unfortunately the desire to extract usable patterns out of our software is a good one. How then can we know when to resist it? Particularly with powerful tools that allow great depth at great speed — such as Ruby on Rails — it is seductive to design beyond the requirements of an applications. Why code specifically when a generic case could ease future development? Why make a particular tool to address just one case when a more general implementation could be released as a gem?
Striking a balance between complexity and specificity is one of the greatest challenges I’ve come across as a Rails developer. As avid software users ourselves, developers and clients always want to make applications better, and that’s a drive that we should nourish — but frequently it can come at the expense of the very application that engendered it. Keeping focused on the original target, limiting scope, and using very specific use-cases has benefitted every application that I’ve worked on, and conversely my personal projects that I’ve begun with very lofty goals usually end in failure.
Part of the reason behind this is that we all want to create Rails 2.0, or MySpace, or Facebook, or whatever, all in one go (and usually clients want those products plus a little bit extra, making our job even harder). In my case, I actually have a grand dream of creating something like Dwarf Fortress, an adorable ASCII-based game that has more layers than an onion. But these complicated pieces of software weren’t built in a day, or a month, or even a year. Rails 2.0 is the result of 5 years of work, and no amount of clever coding on your part will get your product there in less time. Indeed, the probable result of you lavishing time and energy on it is that you’ll burn out all the more quickly, and be left with something personally unsatisfying and professionally devastating if you owe it to a client.
And nine times out of ten those awesome features you added will end up being unused anyway. Though extracting code and creating general software can feel satisfying, there’s the distinct possibility that the use cases you imagine will never come to pass — or even worse, that the use cases are not helpful to the core business of your product. Does your application really need address book functionality? Will it provide something that the user’s address book doesn’t already provide? In our haste to solve problems better and more efficiently we can find ourselves solving problems that don’t need to be solved.
Let’s consider an example from a personal project of mine. In this project, users can collect money. Initially I just added money as a column on the users table. But then I figured, perhaps the user has multiple accounts and wants to store money separately in each of those accounts. Now I have a new model, account, and the user has to interact with multiple accounts to store money. Perhaps each account has a different password, or the user wants to assign preference levels to them, so that when they make a withdrawal certain accounts are selected first.
All I really wanted — all the application really needs — is for a user to be able to have a representation of money. The rest of this, with accounts and preferences and passwords, is needless complexity. If it were axed and added in at a later date, when and if it became necessary, the only “casualty” would be that my application would be much closer to release: and I think that’s a downside that’s greatly to be desired.
Used sensibly, iteration allows us to design more specific cases first and then generalize them later once we know how they’ll be used. But if we let iteration drive our development, then we can find ourselves stuck in loops, making code progressively more complicated to address problems that may not even exist. In the end you’ve wasted time and energy to no good end.
So my advice to you is this: always keep a laser-like focus on exactly what your product needs. Consign awesome features and code extraction to either your free time or such a time as they become necessary. When you first design a project, keep your goals modest and your code simple. Stay DRY if you can but don’t go looking for problems to solve. Believe me, they’ll crop up rapidly enough, and you’ll be glad you didn’t try to code for them before they appeared.
Remember that the most successful web applications are those that generally try to do less, not more, and continually code towards that goal — then you’ll have escaped the trap of iterative development.
