Blog by Railsware

How to Reduce Technical Debt

Whether you’re looking to improve efficiency, increase competitiveness, or simply stay ahead of the curve, reducing tech debt is a critical step in the right direction. In this article, we’ll offer practical tips on how companies can ensure their products remain high-performing and sustainable in the long term.

What is technical debt?

Technical debt (TD) is the cost of reworking software that is difficult to modify or maintain. It usually happens when the development team takes shortcuts in favor of speed of delivery.

Tech debt is a metaphor and an umbrella term for multiple different types of debt. This makes it tricky to define, pinpoint or track. In this article, we’ll discuss how to prevent the most common types: code debt and architectural debt. And for engineers, the tell-tale signs are 1. A change to the software is unusually hard to implement or 2. The code became more complicated after introducing a simple change.

Artur Hebda

Full Stack Engineer

From a developer’s point of view, technical debt is anything you tell yourself you’re going to fix “later.” But more often than not, later never comes.

There’s still much confusion and debate about what counts as tech debt. So let’s clear that up. From our perspective, tech debt can’t be boiled down to the number of FIXMEs or TODOs in the codebase – it’s more nuanced than that. It’s also not identical to bugs or badly-written code. Bugs can be caused by tech debt, but TD isn’t limited to bugs. And bad or messy code doesn’t always evolve into tech debt.

Why is it important to reduce tech debt?

Left unchecked, technical debt can have a serious impact on your company’s bottom line.

Like financial debt, TD accrues interest over time. The longer it’s ignored, the more costly it becomes. For example, if an engineering team fails to update dependencies regularly, it can lead to unexpected costs down the road. Why? Because when a library or framework deprecates, the team will have to spend unplanned time repairing or replacing it. Over months and years, unmanaged tech debt erodes the quality and scalability of your product. It can also hinder your ability to hire new devs or rely on documentation.

It’s estimated that software development teams waste about 23-42% of their time dealing with the ramifications of technical debt. This drain on resources typically impacts delivery speed. Timelines often get harder to estimate, product improvements get sidelined, and stakeholders grow uneasy with the lack of progress. Not to mention, developers can quickly get burned out when they are constantly working on issues that could have been avoided.

Types of tech debt

When Wade Cunningham first coined the term technical debt in the 1990s, he was mainly referring to code debt. However, the definition has expanded a lot since then. We can now say there are several different types of tech debt, including:

Architectural debt occurs when we make sub-optimal decisions about the system architecture. For example, you build your web application using a low-code platform (e.g WordPress). But later, when applying user feedback, you realize that your product requires way more customization than the platform can offer. If you have an online store, for example, you may have avoided this if you started out using frameworks or leading B2B e-commerce software solutions – but this gets trickier in the case of other products.

Code debt. This is the classic definition of tech debt. It refers to the consequences of writing code that is difficult to interpret, update or maintain. The code might be overcomplicated, inconsistent, or contain errors that can result in bugs or other issues.

Infrastructure debt can accumulate when the infrastructure (e.g. servers, databases) supporting a system is not properly maintained or upgraded. This can result in critical performance issues, such as unplanned downtime or the slow loading time of key product features.

Documentation debt occurs when developers fail to document their code correctly. Missing, incomplete, or outdated explanations can cause confusion when coworkers or new team members come in contact with that code. In bigger systems, doc debt can also make it hard for engineers to interpret how certain processes or features actually work, and how parts of the codebase are connected.

Data debt is incurred when software teams don’t properly invest in data management. Failing to break down information silos can cause data duplication or missing data, making it difficult for product teams to properly connect with users.

Security debt builds up when proper security measures are not considered during the software development process. For example, the team delays fixing a known system vulnerability, which increases the likelihood of an SQL injection attack. Unmanaged security debt can lead to major data breaches and other serious violations. Using VPN browsers, implementing other security measures, including regular software updates, strong and unique passwords, and multi-factor authentication, can significantly reduce the risk of security breaches.

Common tech debt triggers

Business and management causes

Engineering team causes

Intentional vs unintentional tech debt

It’s important to remember that tech debt isn’t always a bad thing. More often than not, it’s an unwelcome burden. Other times, a product actually benefits from taking it on. But regardless of how tech debt is perceived by the team, it can be split into two camps: intentional and unintentional.

Martin Fowler’s ‘Technical Debt Quadrant’ illustrates the difference between deliberate and inadvertent debt and their reckless and prudent counterparts.

How we reduce tech debt at Railsware

As the old adage goes, ‘prevention is better than cure.’ Here are some of the things we do to prevent, track, and tackle tech debt.

In-depth planning

Without a product strategy – and a plan on how to execute it – it’s virtually impossible for an engineering team to make the right decisions at every turn. That’s why having a product roadmap is so important.

A roadmap is more than just a list of tasks to be completed. We consider it a living document that displays the actionable items of a product strategy. Features are clearly prioritized, so that team members know where to focus their efforts from the get-go. At a glance, engineers can learn what the project’s main priorities are and grasp how their code informs the bigger picture.

Meanwhile, the product backlog helps teams keep track of tech debt (among other things). Although it’s easier to ignore code smells, there are benefits to properly recording tech debt each time it crops up. The more visible it is, the less chance it has to fester.

So, engineers should create tickets in the backlog each time they spot an inflexibility in the codebase. Then, whenever there are resources to spare, the team can assess the issue’s priority level and start investigating its root cause.

Focusing on what matters

Not all tech debt is created equal. Oftentimes, it doesn’t make sense to refactor a feature that is rarely modified in the first place. Turn your attention to the code that actually matters. Work on strengthening key product architecture and infrastructure elements with each iteration. It’s better to gradually increase the overall agility of your product than constantly tinker with ‘good enough’ code.

At Railsware, we refactor only when there is an urgent and explicit need for it. That way, we don’t sink resources into fixing non-essential things. At the same time, our engineers are encouraged to make minor, incremental improvements whenever they have a chance. They follow the boy scout rule when making those changes: Leave the code better than you found it.

Making sure engineers are heard

Management should always actively seek out and listen to the perspectives of the engineering team. In our view, engineers are best placed to identify potential technical debt before it occurs and suggest ways to avoid it. More often than not, their expertise can be leveraged to create more sustainable and performant solutions.

At Railsware, we promote an open feedback culture, where team members are encouraged to voice their opinions/concerns with their team (and the wider organization where appropriate). Dedicated Slack channels for squads and guilds are useful, but they shouldn’t replace synchronous communication. Management should make time in the day/week/month for well-planned meetings.

For example, daily standups aren’t the only meetings our Coupler.io engineers regularly attend. Many contribute to big-picture discussions, such as:

Not all engineers are required (or invited) to attend those meetings. But there are always at least one or two members of the team in attendance. This ensures that strategic decisions are made in collaboration with those who know the software best.

Embracing a flat(ish) organizational structure

Job titles and hierarchies often cause unnecessary blockers for agile development teams. However, we believe that the more autonomy developers have, the more empowered they are to build great products. In fact, adopting elements of a holacratic organizational structure has helped us develop a culture of ownership across all of our processes. In our version of holacracy, individuals within teams and guilds work closely to achieve a common goal, instead of relying exclusively on managers to assign roles and distribute tasks.

For example, our Mailtrap development team self-organizes into specialized guilds during each sprint. The main ones are:

In the context of tech debt prevention, this ‘guild’ approach has a couple of key benefits. Firstly, the team gets dedicated time and resources to make system improvements and address red flags in the codebase. Secondly, team members can work on different various parts of the project over several months. This lets them develop a deeper understanding of the project requirements and better contribute to product decision-making.

Investing time in pair programming

Pair programming refers to when two developers collaborate simultaneously on part of the project’s code. At Railsware, pair work helps us solve problems more efficiently and create smarter, more robust solutions. Sessions can run for as long as 2-3 hours, or as little as half an hour – it all depends on the complexity of the issue at hand. However, regardless of how long these sessions last, we believe the time investment is always worth it. When it comes to preventing tech debt, the primary benefits of pair programming are:

  1. Better quality code. With an extra pair of eyes on the code, it’s harder for mistakes or bad habits to slip through the cracks. Errors, bugs, and bad practices are fixed long before they have a chance to morph into tech debt. 
  2. Ability to see the big picture. When engineers work in pairs, they are better equipped to evaluate all sides of a problem and keep the big picture in mind. (For example, the ‘Navigator’ investigates the context, which helps the ‘Driver’ shape the small details of implementation).
  3. Improved knowledge sharing. Team members have more opportunities to bond, share their skills, and develop a shared understanding of the product direction. This helps them to make better day-to-day decisions that support the product’s sustainable growth.

Conducting regular PR reviews

Regular and thorough pull request reviews are a key line of defense against the accumulation of tech debt. The practice enables teams to identify dysfunctional code and other code smells at an early stage.

So, to promote consistency and reliability, it’s important to establish a standard protocol for PR reviews. The more streamlined the process, the easier it will be to identify potential issues, communicate, and resolve them quickly. Here are some of our tips:

Final thoughts

Nobody wants to deal with tech debt. But to prevent it from causing real harm to your product, it’s essential to have a clear understanding of what it is, and how it accumulates. It’s important to make sure that engineers are involved in decision-making processes. Not to mention, giving engineers more autonomy can empower them to take ownership of the technical debt and be more proactive in managing it. Conducting regular code reviews can help teams stay on top of their technical debt and prioritize the most critical issues. Meanwhile, pair programming can also help teams catch issues early on and ensure that code is up to the required standards.

Exit mobile version