How to fight technical debt

Some say that the only two things one cannot avoid are death and taxes. That’s not true. For every software team, there’s something much more dangerous than death. It’s technical debt. If you hear about it for the first time, my previous post about where technical debt comes from should come in handy. If it’s your first date with technical debt, I suggest you grab a coffee and read it first.

When it comes to technical debt, there is bad news and good news. The bad one is that it’s unavoidable and every software team needs to face it one day or the other. The good news though is that there are not only ways to prevent it but also to fight it when it’s already there.

Technical debt can be (and should be) fought with on five levels:

Fighting technical debt means measuring different factors and taking different actions at each level. Some are easy, other more complex but they’re all worth dealing with.

 

Code level

1. Code review

It’s a practice of checking every new piece by other team members on daily basis. It focuses only on the consistency and quality of a single piece of code.

2. Code inspection

A regular but less frequent check than code review. Usually takes place every couple of weeks. It allows looking at the entire application or modules in order to point out inconsistencies, wrong styling or other factors.

3. Continuous integration

Automates parts of the development and prevents faulty code from being merged into the existing code base.

4. Pair programming

It’s a powerful quality technique to allow more than one developer to work on a certain feature. One says that two heads are better than one and that’s exactly the value of pair programming, especially when it comes to critical parts of the application.

5. Style guides

Imagine a team of five journalists write two paragraphs each and then glue them together in one article. You would immediately see different styles, wording and formatting. The same goes for code. That’s why using style guides help keep the code coherent and predictable in style. Check the style guide we’re using on the frontend.

6. Documentation

I know, boring. But try once to develop an existing application that has gone through a few pivots and you’ll easily get lost.
Trainings, trainings and trainings. I can’t say that enough.

 

Architecture level

1. Testing environment

Sometimes debugging or improvements can cause problems we can’t predict, so a place where we can actually see our solutions before they go live is crucial and will help us a lot.

2. Architecture backlog

Just like with product backlog, these are ideas to improve the project architecture, performance etc.

3. Structure diagrams

Keep the diagram accessible for all project members (e.g. in project tracking tool such as Jira or in Google Drive) and make it a good reference for any discussions about the architecture or proposed solutions.

4. Documentation

Makes new developer’s life so much easier to dig into the project.

5. Well defined requirements.

When we know how the product is supposed to work, we will know how the architecture is supposed to be designed.

 

Process level

When it comes to processes, I would say from my experience in projects, it’s significant that the entire team knows how we work and who’s responsible for what. It has a lot to do with communication. Communication is the key factor in succeeding so make sure it’s clear and honest.

1. Visualise procedures

Yeah, I know, a lot of diagrams but there’s a lot going on in the process of producing software. If we want to repeatedly make good software, also our processes need to be repetitive.

2. Continuous feedback

Not only in the project team but in the entire organisation. The openness to share the things to improve and to receive feedback is the heart of every self-learning team.

3. Training

Make sure that the team knows the reasoning behind every process. It can be achieved only by learning and constant development.

4. Constant inspection

Review the processes you have in place, the roles in the team and the team performance and be genuinely interested in how to make it better.

5. Transparency

The best decisions can be made only when we have the whole picture. Hiding things from your team or employees will do more harm than good.

 

Organisation level

This is the part that refers to the whole company.

1. Write down responsibilities

Everyone knows what are their responsibilities and the responsibilities of everyone else in the company.

2. Easily accessible and known company’s values

If we know that e.g. our value is high-quality code and product, we know what to focus on in order to act upon these values.

3. Clear communication

I can’t stress that enough. Let’s not make promises we can’t keep or avoid answering questions from a team member. The more clear and transparent we are, the better products we can build.

4. Change and risk management

Holidays, sick leave, team changes… there are a lot of risks each organisation has to handle. Think ahead how people can substitute one another in projects during summer breaks or when the workload is bigger than usual.

5. Five Whys

Even in a good team, people won’t communicate the real problems right away. One of the ways to deal with it is to ask questions starting with Why in order to take down the layers the real problem is wrapped in.

6. Team feedback

Refers to the way we review not only the code but our work in projects (retrospectives) or our attitude as employees (monthly reviews with supervisors).

7. A stable vision of the business

It’s difficult to work in an ever-changing environment. Consistent actions of company leaders and the feeling of going in the given direction will create the sense of stability and trust.

 

Product level

That’s the highest level of dealing with the technical debt. It requires a lot of systematic actions from the entire team, mostly from the project manager.

1. Decisions based on data

When setting the priorities, it’s crucial to base them on real data (users demography, browsers and devices they use, the feedback they give etc.). Without those, every decision we make is like a shot in the dark.

2. Clear priorities

We need to always know what are the priorities, both from our side and the client’s.

3. Tests

Unit tests, integration tests and manual tests are a way not only to avoid technical debt but also to produce high-quality code that is stable and does not affect the previous work.

4. Knowledge about the client

We’re playing on the same team so it’s important to treat the client as a part of it, not as a separate entity.

5. Responsibility for the whole product

Only then the development team can come up with new ideas and the genuine interest to make the product better.

6. Constantly updated backlog

That’s my job – to translate client’s ideas to user stories and plan new development iterations in advance.

7. Constant communication with the client

Daily communication on Slack, video Skype calls or weekly updates make it easy to build the relationship with the client and be able to step into the client’s shoes and make a better product for them.

8. Constant update on the requirements

Requirements change and whenever this happens, the entire team needs to be notified in order to deal with the change. What helps here are update calls before the planning session to confirm the requirements and the project scope.

 

Risk of not fighting technical debt

There’s a theory that the more lines of code we have, the bigger the technical debt is. Of course, following this course, we would have to delete the entire code and make it a Zero Code concept (hi to Inbox Zero) which makes no sense.

What makes sense is taking time to set up the organisation, processes, product development, architecture and code in such a way that we minimise the risk of stepping into a big and smelly surrounding of technical debt.

Years of guerilla development, not following standards and not cleaning the dust after old solutions can make technical debt grow to unbelievable size. It can be big enough to effectively prevent us from developing the product any further. In this case, we can do two things.

One of them is to divide the application into smaller chunks and work on those chunks one by one changing the temporary solutions we implemented five years ago. Unfortunately, we’re not always that lucky to have the opportunity to do it.

The other is to rewrite the product. The thing is, it’s often cheaper than implementing constant changes, constant overwriting different functionalities and refactoring parts of the code. It’s sometimes much more beneficial, but… well, that’s not the easiest decision to make.

Getting ourselves into technical debt is a dangerous (and inevitable) place to be. We’ll get there sooner or later. The debt will be paid. The question is how much do you want to pay for yours.