One of the twelve principles of the Manifesto for Agile Software Development is: Continuous attention to technical excellence and good design enhances agility. I prefer to take this one step further: Good design doesn’t just enhance agility; good design is required to sustain agility.
Good design is essential to long-term productivity. I’m not confining design to a high-level, architectural perspective; the nuts-and-bolts design of everyday programming counts as well. We need automated unit tests to confirm that the software is executing correctly, without relying on labor-intensive manual testing. Automated tests save time and they allow us to refactor with confidence, providing immediate feedback on the results of our efforts.
The velocity of software projects will decrease as new features are added due to a lack of a good initial design or the lack of refactoring to maintain a good design.
Teams can be productive early on, even when poor design practices have been used because software with a small feature set is easy enough for developers to keep their heads wrapped around. And because small feature sets aren’t performing as much work, the number of automated unit tests will be small. However, as the code base grows, poor design will rear its ugly head and make life difficult.
The same thing can happen if time isn’t taken to refactor well-designed code to reflect the changes that new features introduce. Design isn’t static; the code can drift into a state of poor design through active updates. In any event, poor code design will increase the time and effort required to make changes in the future.
A small function that performs only one specific job is an example of good design. Bob Martin states in his book, Clean Code, that, “Passing a Boolean into a function is a truly terrible practice.” No argument from me. (Sorry, I couldn’t resist the pun!) Why is this so?
Because, as Bob explains, “It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing.” The function becomes longer – making it harder to understand and more difficult to automate. You can’t just call it once and get a result; you have to call it twice, with two difference expected results.
We need to bear in mind that software development is inherently a design process up to the point that the compile button is pressed, and that the source code needs to be considered as an asset. Features in the compiled code are only a piece of the total value of our software.
“...rework in the manufacturing metaphor leads to cost, rework in design leads to value.” – James O. Coplien in the Foreword to Clean Code by Robert C. Martin