For development teams, there is little more satisfying than starting an application from scratch and watching the final product evolve piece by piece over months of hard work. Maintaining legacy applications, on the other hand, is notorious for being difficult and yielding depressingly little reward. No team sets out to create bug-ridden applications that constantly crash or that are exceedingly difficult to change. Rather, the goal of every codebase is to fulfill requirements and be correct, reliable, and easy to change. Teams ensure that their projects meet these standards through comprehensive acceptance criteria, code reviews, and testing, but at times constraints like deadlines make it necessary to compromise the overall quality of a deliverable. These compromises are referred to as “technical debt”.
“Technical debt is the amount of effort required to refactor brittle software architecture and restore confidence in its security and correctness.”
In the best of circumstances, teams deliberately take on technical debt in order to deliver new features to the end user ahead of the competition. Frequently, however, teams accumulate their debt unwittingly. If developers lack experience or discipline, they produce code that is difficult to understand or test, fits poorly into the existing architecture, and cannot be easily refactored. Alternatively, if user stories are incomplete or lack clarity, even the most well-written code may fail to satisfy requirements, with the result that additional time must be spent fixing incorrect behavior. In the beginning, it may seem as if the project is progressing strongly, but if a team does not take its accrued debt seriously or address it quickly enough, the results can be disastrous.
Since technical debt is a decrease in the overall quality of a codebase, it is not surprising that it manifests as increased downtime, increased bug reports, slowed development time, and in the worst case, security exploits. Entire development cycles that should have been spent implementing new features are instead spent fixing bugs or environment issues. Poor architecture and test coverage make it nearly possible to add or change functionality with confidence that other parts of the application will continue running according to spec.
“The oft neglected consequence of technical debt is that to the team itself.”
Courtesy of the increased number of defects and missed deadlines, the business loses confidence in its developers. While project leads do their best to protect and defend their team members, they still must pressure those they manage to improve performance. In turn, developers become discouraged by the constant need to justify their slow progress in addition to the demoralizing frustration of fighting a brittle codebase. Since employees who disdain their work are less likely to put their best efforts into it, the project further suffers.
As is true of many problems, the first step to recovery is admitting you have a problem. The entire team is responsible for assuming accountability for technical debt, from the management to the developers. It is not sufficient for developers to acknowledge the issue; they also need buy-in from the rest of the team to put other features on hold in order to resolve it. Once the whole team agrees to pay down the existing debt, the team leads can begin creating tasks to improve test coverage, refactor problematic code, improve accessibility, or address security concerns. In addition, the leads should draft a plan to prevent future technical debt.
Team leads play a crucial role in both recognizing the symptoms of technical debt and providing the necessary guidance to reduce it. As individual team members’ strengths and weaknesses become clearer, the lead can turn mistakes into learning opportunities and guide the team to a better development process.
There are a number of ways technical leads can improve their team’s workflow:
- By establishing a budget for code quality and test coverage
Developers should know what is expected from their work before it can be considered complete, such as the target branch coverage or the quality of inline documentation required.
- By helping the business craft user stories that are clear, comprehensive, and easy to convert into tests
On the one hand, clients and quality assurance testers are concerned only with the end-to-end behavior of a feature. On the other hand, such a high-level overview of a feature is rarely sufficient to describe all of the necessary functionality that developers need to implement. Team leads can help bridge this divide.
- Through mentoring
Both pairing and code reviews are excellent platforms for helping team members grow. Instead of refactoring poor design themselves, leads should help junior developers refactor their own code. This requires enough time that it is not always possible, in which case leads can find other ways to minimize the effects of inexperience. For example, if a team consists predominantly of junior developers, then it is simply not reasonable to expect perfectly-designed code with each pull request. In such cases, the lead should encourage team members to focus on a good test suite, and then balance manual refactoring with mentoring sessions as time allows.
- By encouraging code re-use across teams and applications
Within large companies especially, teams often solve the same problems repeatedly when existing solutions can and should be provided by other teams within the company. InnerSource initiatives not only decrease the amount of code that must be maintained (and therefore the surface area for bugs), they increase the resources available to add new features and improve the user experience.
While strong methodology is important, it is not sufficient to reducing unintended debt to a manageable state.
Projects will always have deadlines, and teams will always have members who are less experienced than others. As a result, some technical debt will always be inevitable. However, deadlines and inexperience are not the only causes of technical debt. Teams are comprised of people, people will always make mistakes, and it is impossible to predict the future. While inexperienced developers tend to blame themselves (or others) for those mistakes, experienced developers know instead to look to their tools to prevent some of those mistakes from happening in the first place.
Build-first development is the final guard against technical debt. If developers are required to adhere to style guides by discipline alone, run builds and automated tests manually, and visually scan for security vulnerabilities, then there is not the question of whether that process will fail, but when. Every project should incorporate coverage reports, security scans, linting, and integration verification directly into the build process, which should ideally be run with every pull request. The earlier that hidden technical debt can be uncovered, the less likely it is to hamstring the project at the worst possible moment: in production.
Technical debt may be a natural side effect of every development process, but it does not have to suffocate the team or degrade the user experience. By introducing thorough automated processes and conventions, taking the time to address existing debt, and appointing reliable leads, even teams with limited experience can settle their technical debt and produce healthy, successful applications.
Let SitePen Be Your Debt Advisor
SitePen has years of experience not only resolving technical debt in existing projects, but also launching new projects with all the necessary tooling to prevent technical debt in the first place. Let us know how we can help you set your project on the best possible path.