Ready, set, go! You started your project with a team you trust. All the planning is done, the team knows your goals and gets to work. The first artifact is ready for testing after a two-week kick-off sprint. You are amazed by how quickly first changes are implemented. You give feedback and adjust some requirements for the next sprint. After a few weeks your desired changes are made and some more features are implemented.
Development goes on like this for a few months, but you recognize some glitches. Features take longer, bugs are introduced which take noticeably more time to get fixed than in the beginning. Team members seem to be less motivated, people leave and you have find replacements for these vacant positions.
Velocity declines further, it seems hard to get people on board, and even though you had a very good feeling about the newly hired developers, they do not perform the way you expect them to. It gets much harder to keep up with deadlines and people get fed up with the rising pressure.
More people are leaving and with this large number of resignations you also loose your last most-incorporated developer, which has been part of the project since the beginning. Suddenly the productivity drops to a point where you have to think deeply about how to continue…
This is a short depiction of how the lifecycle of a software project could look like. In the first few weeks when you are starting off with a new team, everyone knows the goals from principle and is in the loop seeing the code grow. They know a lot about the components and usually see what is to be done immediately. This applies to implementing new features and to fixing bugs.
Everyone was enthusiastic about the speed of development, but perhaps due to a timely deadline for first prototypes, decisions were made to defer documentation and tests. But over the course of the project these decisions where not reiterated.
No human being can keep all the decisions in its head. This leads to overhead because documentation and specifications in form of tests are still missing. Now, when people leave, you loose the staff that knew about the system, meanwhile complexity grows further.
Newly hired staff is not able to gain the same profound understanding of the system with all its irks and quirks. Until you loose the last person that has been with the project from the beginning.
This process is called accumulation of technical debt. And what our team missed is to work actively against it.
As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it. — Meir Manny Lehmann 1
Common causes of technical debt are:
- Imprecise specifications, where requirements are still being defined during development before any design is done. This might speed up development but has to be reworked later on.
- Business pressures, where the client puts pressure on the development team to release early.
- Lack of process or understanding, where businesses are blind to the concept of technical debt, and make decisions without considering the implications.
- Lack of test suite, which encourages quick and risky band-aids to fix bugs. This erodes the system’s architecture if not designed properly.
- Lack of documentation, where code is not documented sufficiently. Making it harder for new people to jump into the code and be productive.
- Lack of alignment to standards, where industry standard features, frameworks, technologies and best practices are ignored.
- Lack of communication & collaboration, where knowledge is not shared across your team and efficiency suffers.
- Lack of ownership, when outsourced software efforts result in bad code, which has to be refactored or rewritten in-house.
- Delayed refactoring, when parts of the code become obsolete or inefficient, because of requirements that evolved over time. The longer refactoring of these parts of the code is delayed and new features are implemented on top of them, the more technical debt accumulates.
- Last-minute specification changes, which are implemented without sufficient design, documentation and checks, because of shortage in time or budget.
Per definition you incur debt with the first code you ship. Some debt is necessary to keep velocity, but only if it is addressed as quickly as possible. The longer you postpone working against your debt, the harder it becomes to eradicate it again.
Ward Cunningham (pioneer in both design patterns and extreme programming) first described the analogy debt with complexity of software systems:
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise. — Ward Cunningham 2
So we see technical debt is inevitable, but we can address it.
Types of Technical Debt
The worst scenario is the one where team members are not aware of the debt they create, but it is perfectly fine when it is the result of a conscious decision to achieve a certain outcome and is addressed as soon as possible.
Martin Fowler differentiates the following types of technical debt based on two dichotomous categories: reckless vs. prudent and deliberate vs. inadvertent:
|deliberate||“We do not have time for design”||“We must ship now and deal with consequences (later)”|
|inadvertent||“What is Layering?”||“Now we know how we should have done it”|
In our scenario in the beginning of the post, many things were ignored. The development slided unwittingly into deliberate but reckless debt and became aware of it too late.
- Quality Assurance — via tests, continuous integration
- Collaboration — resulting in knowledge being isolated and only accessible by the most senior developers
- Documentation — leaving new developers with only the code
- and perhaps a few more …
The title of this post plays with the notion that developers who are more productive than others are sometimes called 10x developers, but in terms of technical debt I would argue, that they are only 10x because they accumulate technical debt, borrowing the productivity from tomorrow causing your future team to run at 1/10th the velocity.
To avoid the sudden wakening, you have to anticipate the debt as you create it. And furthermore, you have to train your team to do the same. Plan and hire accordingly. Try to build a team that builds software for the people after them and not for the money or the deadline alone.