Technical Debt and Code Refactoring: A Path to Sustainable Software

In this article we discuss technical debt, they type of technical debt, and the impact it can have on business operations.

Merriam-Webster defines debt as “something owed; an obligation; a state of being under an obligation to repay someone or something in return for something received.” Most of us in the consumer world generally regard debt as a bad thing. Which is not a bad thing, by the way.

However, in the world of finance, debt is simply another tool—a short-term strategy to absorb additional costs plus interest to aid in achieving a larger goal or helping to bring about an outcome more quickly, understanding that accounts will eventually need to be settled. Technologists make similar strategic trade-offs in the world of software development. And, just as in the financial world, the longer you take to pay back the debt, the more the costs of that choice increases. Fail to pay off the debt promptly can quickly shift its nature from valuable tool to a burdensome obligation.

What is Technical Debt?

The term was originally advanced by a developer named Ward Cunningham, one of the authors of The Agile Manifesto. In a 1992 report, he offered the following perspective that led to the coining of the term:

“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."

More practically speaking, technical debt (also known as code debt) refers to the consequences of prioritizing development speed over quality, resulting in software that ultimately needs to be refactored (more on this later) to improve performance without fundamentally altering the functionality. Essentially, it’s a catch all term used to describe bugs, old code, and missing documentation that either intentionally or unintentionally accumulates over the course of development. It typically occurs when a team needs to build and release a new feature as soon as possible, so they take shortcuts in order to hit the fast delivery timeline. Technical debt is not uncommon in dev shops and, when managed appropriately, usually has limited impact on the company.

However, like most decisions related to custom app development, it should always be carefully evaluated and appropriately managed in order to avoid negative consequences. As it relates to applications, technical debt can result in everything from small bug issues to system-wide crashes. It’s important to document and acknowledge its existence, and then work it into the product roadmap alongside new features so that it doesn’t become a problem.

Identifying Technical Debt

As previously mentioned, many people tend to look at technical debt as either intentional or unintentional, reckless or prudent. Others argue that it shouldn’t be categorized by strategy, but rather by its nature. For example, architecture debt, code debt, documentation debt, design debt, etc. The truth is technical debt can be categorized in whatever way is most beneficial to a company so long as the debt is known, documented, and a plan has been developed to address it.

Evaluating the Impact of Technical Debt on Project Progress

It’s important to remember that technical debt in custom app and web app development is very common and is not always a bad thing. As an analogy, most people wish they could buy a new home with cash, but taking out a loan is typically needed. Most app development companies wish they had comfortable timelines and unlimited budgets to build the perfect application, but that’s rarely the case. In order to meet other strategic business objectives, absorbing prudent and deliberate technical debt may be regarded as a necessary evil.

Furthermore, for new features and future applications, valuable customer feedback may be collected after launch that allows the development team to build an even better solution than originally planned. Documenting and evaluating the impact of technical debt, which is essential in determining if absorbing it in the first place was a good decision, will certainly inform any future refactoring requirements, as well.

Examples of Technical Debt

There are three basic categories of technical debt:

1. Legacy Code

Legacy code is extremely common for companies with applications built over five years ago. Much like updating the OS version on your mobile device, coding frameworks are generally updated every 1-3 years. Once these versions are deprecated or lose long-term support, they no longer receive updates and security patches. The app can continue to operate as expected and have no known bug issues, but it will be vulnerable to malicious security threats.

2. Frankenstein Code

Also common amongst legacy systems are applications composed of multiple coding languages and frameworks. This is usually the result of different developers working on the system at different times and having different skill sets. It may also be due to the use of superior frameworks that were unavailable when the app was first released to create new features. These applications are usually harder to maintain and scale. Development teams must have mixed skill sets to maintain the different code frameworks, which is inefficient. Plus, scaling the app can be challenging if the frameworks don’t blend well and a harmonious way to grow isn’t possible.

3. Architecture/Infrastructure Debt

This type of debt is very common for young businesses that build software on a budget, often needing to cut some corners to deliver the minimum viable product efficiently. However, as that company grows over the next 1-3 years, the architectural decisions made during the MVP phase of the project now need to be addressed so the application can scale appropriately and/or be as secure as possible.

The Importance of Code Refactoring

Some types of technical debt may require brand new code, but others may simply require refactoring or enhancing the existing code. Furthermore, refactoring doesn’t mean you’re completely rewriting that code. Rather, it refers to restructuring code without changing its original functionality, restructuring app architecture for better maintainability and scalability moving forward, or updating code from the time it was originally written to align with current best practices.

Creating a Refactoring Strategy and Prioritizing Tasks

When new features are added to an app, the team must consider how it will impact, and interact with, existing functionality. The same is true for refactoring code. Eliminating technical debt is very important, but how and when you do it must be evaluated and discussed among the team in order to ensure it’s done as efficiently as possible.

Furthermore, that refactored code needs to be in sync with the company’s strategy and/or goals for the application in the future. For example, code refactoring is common when updating to the newest version of code. But sometimes integrations with third-party libraries and other tools will break when the code is updated, so they must be restored. It’s important to identify and plan for this type of unintended activity so as not to impact other work scheduled during a sprint.

Balancing Refactoring with Feature Development

One of the biggest challenges when it comes to technical debt is remembering to prioritize it alongside new feature development. New features are always the shiny and more exciting option and typically deliver the greatest satisfaction in the client’s mind. However, that shine can tarnish (sometimes suddenly) and everything from minor irritations to substantial pain and suffering if technical debt goes unresolved. And while less apt to earn the attention of leaders outside the technical development team, those business leaders also need to understand the importance of eliminating technical debt so that it’s not a point of contention when sprint planning. In our experience, this can be one of the toughest balancing acts in custom app development when managing a product roadmap.

Good debt documentation can assist with future planning way in advance so that it’s accounted for and not added into a sprint in a rush, which can cause pain for other stakeholders and clients.

Following best practices are essential for making the codebase maintainable moving forward. You never want one developer to own all of the institutional knowledge for an application, and rarely will one developer be working on the project during its lifetime. Not only will clean code reduce the risk of tech debt, but it will allow future developers to quickly familiarize themselves and contribute to the product in the correct way.

Applying Design Patterns for Scalable Solutions

Another easy way to avoid unintentional technical debt is to follow best practice design patterns when designing the application and new features. Design patterns are common design principles that provide a way for developers to write code that is more organized and maintainable. Plus, these patterns also make the application more adaptable as new features are added in the future.

Encouraging Code Reviews and Continuous Improvement

Any development team that abides by agile and lean methodologies should constantly be on the lookout for ways to eliminate waste and improve processes and products. Peer reviews are an excellent way to identify quick wins, but it can also be helpful to get outside input. One effective approach is the use of code analysis tools to identify areas for opportunity to improve coding techniques and new best practices. It’s important to recognize the dynamics around code reviews and CQI protocols. It’s not intended to be a witch hunt or an attempt to scorn developers for their mistakes, but an opportunity to double check the work and learn new skills.

Sustainable Software is a Process, Not an Event

Technical debt and refactoring often play important roles in the successful creation, modification, and long-term viability of any custom app, web app, or other software development product. Evaluating and strategically managing both can help ensure your path to success is as smooth and free from obstacles as possible.

At AppIt Ventures, we believe so strongly in technology’s potential to enhance client impact and improve people’s lives that we are willing to share in the up-front risks of development. Helping you strategically manage technical debt and ensuring you understand the role of refactoring in your project are more ways we do just that.

Talk to our team to scope your next project.