Project: Quick & dirty

Dijkstra would not like it

Image for post
Image for post
“I mean, if 10 years from now, when you are doing something quick and dirty, you suddenly visualize that I am looking over your shoulders and say to yourself “Dijkstra would not have liked this”, well, that would be enough immortality for me.” — Edsger W. Dijkstra, both leader in the pursuit of simplicity and the abolition of the GOTO statement from programming.
cost (quick) < cost (clean)
  cost (quickAndDirty) 
= cost (quick)
+ cost (dirty)
  cost (quickAndDirty) 
= cost (quick)
+ cost (clean)
cost (quickAndDirty) > cost (clean)

Impact over time

To illustrate this approach and alternatives, we built a model to simulate:

  • the technical debt, which is the sum of all quick code ;
  • the codebase maintainability, i.e. the ability to read/understand it and change it.
  • the delivery pace as the amount of features shipped without regressions.
Image for post
Image for post
Even when coded “cleanly” (debt is zero here), the complexity of the built software increases over time along with the code size (accumulation of new features delivered). Without regular refactoring, the maintainability decreases as the code gets more and more complex and the cost of adding new features in such a poor code increases. The curve of delivery flattens and in the end you cannot deliver anymore (logarithmic scales are used here to better show evolutions over time).
Image for post
Image for post
As the technical debt increases, maintainability decreases half earlier, down to a level where no delivery can be done anymore (flatline of shipping) as the code got too hard to maintain without breaking things. The cost of development curve steepens for the same reasons: because of the poor quality code, it takes longer and longer to produce new features without breaking existing ones.

When to handle technical debt?

Actually the cost of a technical debt can only be accounted to a subsequent development iteration (which makes it even less noticeable at start). This can be the immediate next one:

  cost (quickNDirt1) 
= cost (quick1)
cost (cleanDev2)
= cost (dirty1)
+ cost (dev2)
Image for post
Image for post
With systematic cleanup the debt never increases more than one iteration. Maintainability gets jagged (because of each dirty-then-cleanup cycle) but keeps an acceptable level longer, allowing to deliver longer as well. Cost is sometimes higher tough, as cleaning is more costly (=2) than dirtying (=1).
  cost (quickNDirt1)
= cost (quick1)
cost (dev2)
= cost (dev2)
cost (cleanDev3)
= cost (dirty1)
+ cost (dev3)
  cost (quickNDirt1)
= cost (quick1)
cost (quickNDirt2)
= cost (quick2)
cost (cleanDev3)
= cost (dirty1)
+ cost (dirty2)
+ cost (dev3)
Image for post
Image for post
Not resolving all the debt makes it increase progressively. The cost saved by not cleaning up all the debt (quick dev) is actually increased by the bad consequences on maintainability, which falls earlier, thus impairing the delivery capability more quickly.

Even clean code requires refactoring

But that is not enough. As we saw in the first graph, writing only “clean” code warrants a good delivery but still at an increasing cost, because even clean code requires periodic refactoring so that new features can be integrated, not just as additions (thus adding complexity), but merged into concepts shared by the whole codebase. You can see that as keeping a room clean but, as new furnitures are added in, wanting to move or replace some of them to make the whole thing more practicable.

Image for post
Image for post
Avoiding quick & dirty to avoid debt is not enough: another kind of “implicit” debt with the increasing complexity of the product, and adding periodic refactorings allow to mitigate the loss of maintainability while keeping a good delivery pace, while not making costs worse.
Image for post
Image for post
Quick & dirty, if cleaned up, look similar to the clean code approach when including refactorings, but is a bit more costly and fails in maintainability earlier, because of the time lost in a subsequent cleaning up iteration (instead of writing clean version directly). As a result, delivery is less optimal.

Conclusion

Maintainability is a key factor of project development as it impacts both delivery capability and cost. Poor maintainability also have more unacknowledged effects such as bad stability (i.e. more regressions as the code is getting obscure) and developers productivity (coding is getting harder and more stressful).

— Why refactor it, since we’ll rewrite it from scratch in two years?

— To avoid rewriting it from scratch in two years.

The approach of your choice will dictate how long your product is expected to live.

Written by

Software engineer for three decades, I would like to share my memory. Also author of https://rr0.medium.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store