How to Sort Out Tech Debt and Speed-up Development

Guillaume Renoult
ELMO Software
Published in
7 min readFeb 2, 2022
Photo by Alice Pasqual on Unsplash

At the beginning of your greenfield project, development was easy and fast. Features were flowing to production smoothly. Business was happy because of the speed we could ship quickly to our customers. Then a few months later, you started getting more bugs. You can’t release to production as quickly as at the start of the project. Developers are having issues with fixing a bug without creating another one. More and more friction appears in the group as “simple” changes are actually not that simple anymore. Sounds familiar? Welcome to the fabulous world of technical debt.

Key Summary

  • Understand and define tech debt as a team.
  • Fix tech debt in the code: don’t start cutting corners, sharpen your craft, plan/prioritize.
  • Fix tech debt outside: foster team psychological safety, clarify context, ask why.
  • In return value gets released to production faster with better quality and lower risk of defect.

What is technical debt?

Before we get to the definition of technical debt, we have to understand what cruft is. Historically, cruft is unused code that accumulates in a folder when software is recompiled. By extension, we refer to cruft as leftover, unnecessary or poorly written code.

It is this accumulation of cruft that can lead to technical debt.

The Debt metaphor comes from Ward Cunningham (source):

If we failed to make our program align with what we then understood to be the proper way to think about our financial objects, then we were gonna continually stumble over that disagreement and that would slow us down which was like paying interest on a loan.

Over time, the more we cut corners and favor speed instead of quality, the more cruft we accumulate.

How does technical debt grow?

For Robert C. Martin, technical debt grows when developers make tradeoffs to meet schedules and customers expectations, but he advises that messy code shouldn’t be considered as technical debt. Martin Fowler has a slightly different opinion and believes technical debt grows regardless of if we cut corners or not, and recommends distinguishing between prudent and reckless debt:

We can read these quadrants:

  • Reckless and deliberate: “we go ‘quick and dirty’ because we don’t think we have time to do the right thing”
  • Prudent and deliberate: “we ship quickly but we know we are cutting corners and we plan to action on it”

The difference is with Prudent, you’ll leave a healthy codebase ready for enhancements without needing major refactoring.

  • Inadvertent: “we just ship things without realizing how much we are hurting ourselves”. This can happen because of a lack of skill or experience. As developers learn as they code: with time and retrospect, there is a better design.

At the very beginning of a project, developers tend to favor speed over quality, and it works. Customers see results quicker and there are little to no bugs to fix. However, this only works for a period of time and soon it becomes riskier, harder and more time consuming to add features. This can be visually seen as the design payoff line: if developers have the right design pattern in place from the beginning, speed is consistent throughout the lifetime of the project, as long as code is kept clean.

At ELMO, a team of experienced architects and tech leads supports new projects by providing guidance, tools and opinions: we use our standard pipelines for any new micro service by using Docker, Jenkins and Kubernetes, giving us the speed and consistency to deliver new projects or products.

The Pragmatic Programmer shares the same view and mentions developers should never take shortcuts in the first place, even during high pressure situations such as incidents or tight deadlines. The focus should be on having a clean code base at all times and avoid falling into the broken window fallacy.

Technical debt can also grow because of external factors: the best developers won’t perform well if the environment doesn’t support them. Examples includes:

Cognitive load:

  • Lack of clear requirements,
  • Lack of automation,
  • Ad-hoc meetings, interruptions, etc.

Absence of context:

  • Lack of clear code comments or documentation,
  • Confusing variable names,
  • Unclear git commit messages,
  • Unclear requirements,

Team culture:

  • Pushing developers for speed instead of quality,
  • Developers not being listened to when raising design flaws,
  • Developers not communicating their concerns,
  • Lack of psychological safety,

It is also worth mentioning Conway’s Law and the lack of clear communication will be reflected in the product we build.

How to fix tech debt?

Before jumping to solutions, let’s have a look at who will benefit from solving tech debt. For engineers, adding features and fixing bugs is easier, faster and fun. At a team level, it helps with easier planning, greater velocity and higher morale. Organizations also benefit with increased value stream flow and less friction between teams and groups.

Identify technical debt as a team

Make sure you bring the whole team together, and not only developers. We can start by bringing developers together first, then Product Manager, Business Analyst, Product Owners, etc. The idea here is to gather support around this technical initiative. This support will help keep the codebase healthy by the whole team and not only the engineers.

Organise and plan

You need a clear view of the current state of your tech debt. A prioritized technical debt backlog with estimated and refined tickets, as recommended by Atlassian, is an ideal solution. It brings visibility around what is planned, what will be worked on and what will be deprioritized. It’s also an effective communication tool: looking at the list will tell you what to work on next.

Execute

This is the crucial part where technical debt is reduced. There are different approaches to work on it. Some teams can choose to dedicate a week, a sprint or even a quarter dedicated to working on technical debt.

At ELMO, we go with a different approach and allocate a portion of our work each sprint to technical debt. For instance, 20% of HR Core team’s effort is dedicated to this initiative. We prefer an iterative approach rather than a once off: iteratively working on technical debt raises code quality every sprint rather than once a quarter or once a year. It’s also a great way to keep non-technical team members aware of the difficult parts of the system to solve. For example, if we want to build a new feature on top of technical debt, the team will already be aware of the extra cost of having solid foundations before working on the feature. These technical debt tickets are refined as a team during backlog refinement sessions, the same way as new feature or bug tickets are estimated.

At ELMO, technical debt work is mapped with our Product OKRs. We want to grow our product while maintaining speed and quality and therefore low technical debt. A good example of this approach is the work on our Publish — Subscribe system with SNS and SQS: HR Core, the product that contains employee data, communicates with other ELMO products to process this data, such as paying employees or processing leave requests. As part of our OKRs, we grow this product feature to send more data to different ELMO Products. The HR Core team then prioritizes technical debt tickets related to PubSub to ensure we have the solid foundation to achieve these OKRs while maintaining a healthy codebase. For example, we ensure the calls to trigger SNS calls are centralised in a dedicated class, then injected in calling services instead of being duplicated in each of them.

Learn

As a software engineer, working on technical debt is an invaluable learning experience. It’s a time where we can reflect on the code written 2 weeks, 6 months or 3 years ago and assess if it was a correct solution. Sometimes it was the right solution at that time, but since then the world has changed and we need a different approach. Sometimes we weren’t as strong technically as we are today, and realise that a bunch of if … else statements written a few years ago would have been much better with a factory design pattern.

Keep in mind these learnings for future work. Now that you know which patterns to avoid, what worked well and what didn’t, the next projects will benefit from this experience and you’ll end up with well designed products and low technical debt.

--

--