“Any testing at all would be great for this project but I don’t even know where to start!”
I heard someone say this recently and it’s a predicament I’ve seen many times. The scenario looks like this:
- The system is plagued with production issues
- The team in charge of the system is under immense pressure to ship new features
- There are little or no unit tests.
- Any automated end-to-end tests that might exist are generally ignored by the development team
- There might be some manual testing but it can barely keep up with the rapid changes
So you talk to this team and ask why they don’t have unit tests. You get the following response/s:
- It’s impossible to write unit tests for this code because this code is legacy / special / getting replaced in 12 months / made entirely of views / a customization of another system / changes too much / fundamentally untestable
- We don’t have time to write tests because of deadlines
Of course they don’t bother looking at the end to end tests because they take too long, break all the time, and it’s somebody else’s job anyway. Everyone is stuck in the swamp of technical debt, too busy adding new wheels to the cart to push it out of the swamp.
Bad testing is a form of technical debt
You have a piece of functionality that you need to add to your system. You see two ways to do it, one is quick to do but is messy – you are sure that it will make further changes harder in the future. The other results in a cleaner design, but will take longer to put in place.
Technical Debt is a wonderful metaphor developed by Ward Cunningham to help us think about this problem. In this metaphor, doing things the quick and dirty way sets us up with a technical debt, which is similar to a financial debt. Like a financial debt, the technical debt incurs interest payments, which come in the form of the extra effort that we have to do in future development because of the quick and dirty design choice. We can choose to continue paying the interest, or we can pay down the principal by refactoring the quick and dirty design into the better design. Although it costs to pay down the principal, we gain by reduced interest payments in the future.
If tests are not automated as a system is created, the regression testing cost increases massively because it must be done manually. Not only this, but when a system is created without tests, the system itself is usually built in a very untestable way which makes it much more difficult to add automated tests later on. This is one way that teams often find themselves in a predicament where modifications to their codebase become difficult, risky, and slow.
Where to start
There are three crucial things that have to be done in order to move forward when a team is in this kind of situation.
Quantify and track the technical debt. This includes untestable code, unmaintainable code, lack of automated tests, lack of supporting infrastructure for testing, lack of supporting infrastructure for development workflow, environment management, configuration management – all of the usual suspects that bog down a development team. This also includes lack of training – do the people who need to write tests know how to write tests? Do they know how to write fast, reliable tests? If not, then investing in training is also important.
All of this work needs to be written down, tracked and managed. Too many teams delegate this work to their “spare time”. The only time I ever saw a team with spare time, it was two weeks before the entire company got shut down. It doesn’t exist. Track this work.
Write a technical debt payment plan. This is a “roadmap” for your team to incrementally complete the work required to get the team out of this dangerous situation. Decide what the goals and benefits are for each milestone of your roadmap. Plan to achieve these milestones regularly and set realistic expectations for the timeline.
For example, one goal might be to refactor one critical feature so that it is testable. This will allow developers to write unit tests for this feature, preventing issues reaching production and saving valuable time in the developer workflow. It will also make the code easier for developers to maintain, attracting new developers to the project.
Assign someone to manage the technical debt payment plan. These plans can take years to complete, and they often fall by the wayside if someone doesn’t keep an eye on them. This person needs to be diligent in ensuring that the team continues to regularly include technical debt work alongside their usual feature work. The best person for this job is often the technical team lead, because they should have the leadership skills to negotiate with the product managers about balancing the system’s needs with the business needs.
Never underestimate the importance of having a technical debt payment plan
The worst situation of this type that I ever saw was a team of developers that had to spend 100% of their time just “keeping the lights on” – nobody could add any new features because it was too risky. Things would break often, and mysteriously, and in one instance even cost the company millions of dollars. The developers on this team were so depressed that by the time the lead had finished describing the situation to me, he looked as if he wanted to cry.
It’s incredibly important to be aware of the technical debt that your team accumulates and to ensure that it is kept to a manageable level. And if you truly don’t know where to start, don’t be afraid to ask for help.