惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
aimingoo的专栏
aimingoo的专栏
IT之家
IT之家
G
Google Developers Blog
爱范儿
爱范儿
博客园 - 司徒正美
Recent Announcements
Recent Announcements
The Register - Security
The Register - Security
J
Java Code Geeks
The Cloudflare Blog
M
MIT News - Artificial intelligence
Apple Machine Learning Research
Apple Machine Learning Research
Microsoft Security Blog
Microsoft Security Blog
博客园 - Franky
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
Vercel News
Vercel News
宝玉的分享
宝玉的分享
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
B
Blog
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hacker News: Front Page
H
Help Net Security
S
Security @ Cisco Blogs
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
Stack Overflow Blog
Stack Overflow Blog
O
OpenAI News
L
LINUX DO - 最新话题
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
Secure Thoughts
Help Net Security
Help Net Security
F
Full Disclosure
博客园 - 叶小钗
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
K
Kaspersky official blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
Scott Helme
Scott Helme

Yusuf Aytas

When Code Is Cheap, Does Quality Still Matter? Why Crouching Tiger, Hidden Dragon Is a Masterpiece Why We Ignore Advice The Mirror Is Part of the Machine When Too Many Maps Overlap on One Person The Work Runs on Different Maps Your Work Introduces You Trial By Fire The Dude Why Headcount Math Lies Capacity Is the Roadmap The Roadmap Is Not the System Torres del Paine W Trek Escaping Status Theater Incentives Drive Everything Scaling Culture Without Dilution What Good Looks Like Why Airport Security Feels Random Why Politics Appear How to Work with Me The Janus Protocol Multi-Horizon Delivery Framework What Good Execution Looks Like Managing Your Manager Why Kingdom of Heaven’s Director’s Cut Is Better AI Broke Interviews Most of What We Call Progress Managers Have Been Vibe Coding All Along Stop Wasting Brainpower Why Over-Engineering Happens Prisoner's Dilemma Climbing No More The Weekly Win Mevlana Candy Brewing Turkish Tea Onboarding Your Engineering Manager Technical Deep Dives Yapay Zekâ Çağında Bilgisayar Mühendisliği Building Remote Teams From Idea to Launch in 2 Weeks Reflecting on Software Engineering Handbook Representing the Business New Manager Survival Guide Take Self Reviews Seriously Chasing Real Respect The Invisible Difference Learning the Johari Window Management is a Lonely Place Simple Task Management AI Balance in Work PIP Manager Insights Engineering Manager Interview Preparation Work-Life Balance as a Manager Bridging the Management Disconnect Tech Hiring Bubble Bursts Traits for EMs Simple Acts of Recognition Matter The Question I Ask Every New Report The Reality of an Employer's Market Bridging Ideals and Reality Hiring Red Flags Why The Godfather Is So Damn Good Subteam Tenets No Fluff Please Losing a Top Performer Balancing Act of Reliability Building Trust in Engineering Teams Ideal Number of Direct Reports Overriding a People Leader’s Decision From Misperception to Promotion Perception vs Perspective Setting Goals From Engineer to Manager Getting Delegation Right Interviewing Your Future Boss Celebrating Our Book in Iceland Operational Skills Needed On Writing Software Engineering Handbook Charlie Munger Quotes Working with Dependencies From Las Vegas to Canyons Navigating Layoffs Handling Competitive Dynamics A Weekend Getaway to Malta Engineering Health Essentials Should Dev Managers Code? Confronting the Life on Pause Winning Eleven Kindness is A Choice Bireysel Katılımcılar ve Yöneticiler Leading from Where You Are The Subtle Art of Listening Coding in Leadership The Power of Consistency The Making of a Leader The Path to Leadership Embracing TikTok Talent Sourcing Journey Leading Self Managing Teams Cracking Coding Bottlenecks
Addressing Technical Debt
Yusuf Aytas · 2022-02-14 · via Yusuf Aytas

Published · 10 min read

A field needs to be added, a rule needs to change, or a customer bug needs a quick fix, and on paper the work looks small enough to finish in a day. Then someone points out that the old service is involved, the tests are unreliable, and the only person who still remembers why the workflow behaves that way is on holiday, which is usually when technical debt becomes visible.

Technical debt occurs when we solve a software problem with our limited understanding of the business at the time. We start building a solution to get feedback as early as possible. Instead of spending additional time on business requirements, we prefer to deliver software early. Therefore, we borrow some time from the future.

By the time we deliver the software, we might have accumulated some burden. As the software grows, our limited understanding, as well as additional factors, become hurdles. We might be okay with those limitations for a while. We can pay interest, where interest is the extra time required to make a change. We then address the debt to cut down the interest we pay. As we deliver new features, we accumulate new debt. The debt will never be zero. Therefore, we should keep the interest payments affordable.

If the debt becomes a huge pile, we might go into arrears. We would then lose the capability to deliver new changes. Typically, arrears happen when everyone is firefighting incidents and bugs instead of building.

Although technical debt does not necessarily suggest poor quality software, the industry has often used it as a metaphor for sloppiness. Thinking of technical debt as an excuse for subpar software is counterproductive. There are three fundamental reasons for technical debt: deliberate decisions, outdated design, and rotten components.

Addressing technical debtAddressing technical debt

Technical Debt Is Not Always Bad Engineering

Technical debt gets used as a nicer word for mess. No tests. Poor design. A shortcut that should have never left someone’s laptop. That kind of work exists, and calling it debt can make it sound more respectable than it deserves.

But real technical debt is not always careless. Sometimes we make a deliberate trade-off because the business needs feedback. Sometimes the design was reasonable when the system was smaller. Sometimes a component rots because everything around it changed. If every ugly part of the system gets called technical debt, the term stops being useful. You can no longer separate a reasonable trade-off from poor engineering discipline. Worse, teams start using the debt metaphor as cover for work that was simply not done well.

Where Technical Debt Comes From

Once we separate technical debt from plain bad engineering, the next question is where it actually comes from. I usually see three sources: deliberate decisions, outdated design, and rotten components.

Deliberate Decisions

One might argue that skipping unit tests is a deliberate decision. Hence, it is part of technical debt. I do not think that framing is useful. There is no justification for such a move when the team immediately pays a harsh penalty. That is not debt. That is just removing a safety net and hoping nobody falls.

The deliberate decisions that qualify as technical debt are usually around business use cases. We might lack clarity about the needs and requirements of a system. Part of the initial development is then about delivering software early, giving people something concrete to react to, and uncovering unknowns.

The developed software might not decently meet expectations. It might still be good enough for the time being. We incur debt. Many more decisions later, we accumulate much more of it.

Outdated Design

The second way to incur technical debt is through design that no longer fits. There is no way to get everything right in the first place. We do our best at the time with the information we have. We make some trade-offs. Nonetheless, those trade-offs might not work out well.

Every time we want to add a new feature, the design can cause friction. Refactoring accidentally poorly designed software might be challenging as well. Similarly, our architecture might not hold itself well against time. What helped us move faster earlier can later cause a loss of productivity. The design may have been reasonable once. The problem is that the system kept changing around it.

Rotten Components

Last but not least, rotten components and services contribute to technical debt. A software component might get more features and gradually gain more responsibility. The complexity comes naturally due to incremental changes to the system. Nevertheless, it can get to a point where the boundaries of the service become unclear.

Adding new features or clearing out bugs becomes time-consuming. People start avoiding the component. Reviews become slower because changes are harder to reason about. Testing becomes defensive because nobody fully trusts the behaviour anymore. Hence, it becomes a debt that we incur as long as we keep it running as it is.

Indicators

We pay interest on accumulated debt through slower delivery, fragile changes, and extra coordination. Before that interest starts blocking new features, we need to reduce the debt.

The hard part is deciding where to start. There is no clean recipe for this. A team can still look for signals that show where the interest is becoming expensive.

  • An old tech stack can slow down development. Aged libraries, unsupported frameworks, and outdated dependencies make ordinary changes harder than they need to be.
  • Complexity analysis can point to areas that might benefit from refactoring. These tools will not make the decision for us, but they can show where the system has become harder to reason about.
  • Frequent incidents around a particular service or component show that the debt is no longer theoretical.
  • Many bugs reported against the same component suggest that it needs servicing.
  • Refusal from the team to work with a particular component is also a signal. When engineers keep avoiding the same area, the system is telling us something.

Even then, not every painful component deserves immediate attention. We do not have infinite budget or time. Some debt is ugly but cheap. Some debt is annoying but rarely touched. The parts worth addressing are usually the ones that sit on the path of frequent change, customer impact, or operational risk.

Paying Down the Debt

Once we get a sense of what we want to refactor, we need a methodology. I prefer continuously addressing technical debt instead of waiting for stop-the-world events. Teams can create technical debt issues for refactorings, improvements, migrations, test coverage, or dependency upgrades. We then classify these as engineering health work.

Engineering health should be 20-25% of overall engineering work. That gives the team a steady budget to keep improving the software stack without turning every cleanup effort into a special campaign. This matters because technical debt rarely becomes painful all at once. It usually gets worse in small increments. A component becomes harder to test. A dependency gets older. A service takes longer to change. The team works around it for a while. Then one day, the workaround becomes the normal way of working.

Although ongoing treatment is preferable, some teams inherit systems with significant problems. In those circumstances, we might need to stop the world and bring the debt back to a manageable amount. Examples include bugs and incidents that the team can no longer address promptly. At that point, we are no longer just paying interest. We are in arrears.

Partner Collaboration

Engineering health work might not seem relevant to product development. Several partners might not understand why we need it, especially when the alternative is visible feature work. The key is to communicate the cost early and constantly. We should point out the likely outcomes of ignoring technical debt: slower delivery, more defects, riskier releases, and more operational noise. The other key is to prevent stop-the-world events. Teams should deliver without disruption as much as possible.

Making technical debt tasks part of routine engineering work is easier than asking for a full stoppage later. If we have to go into stop-the-world mode, then we need to incorporate it into the company’s yearly and quarterly roadmap. Otherwise, it will look like engineering suddenly disappeared into a cave.

Nowadays, most stakeholders understand the concept of technical debt. The hard part is still choosing engineering health work over feature work. Product work usually has a clearer justification. A feature can come with a customer request, a revenue number, or a market deadline. Engineering health work often has to argue from avoided pain.

The angle we should take is velocity and stability. If we do not pay down the debt, we keep paying more interest. Delivery slows down. Releases become riskier. Customers experience more bugs and downtime. At some point, the business pays for the debt anyway, just in a more expensive and less planned way.

Not All Debt Deserves Payment

Not every piece of technical debt deserves immediate attention. Some code is ugly but stable. Some components are painful but rarely touched. Some old services are annoying, but they sit outside the path of active product development. Refactoring those areas might feel good, but it may not change much for the team or the business.

The debt worth paying down is usually the debt sitting on the path of frequent change, customer impact, or operational risk. If a component slows down every new feature, causes repeated incidents, or makes releases risky, we are paying interest often. That is where the work becomes easier to justify.

This is also why technical debt conversations should not be framed around cleanliness alone. Clean code is good, but the stronger argument is cost. How often do we touch this area? How much time do we lose? How many incidents come from it? How much confidence does the team lose when changing it?

Conclusion

Software development requires steady investment and maintenance. Technical debt is part of that work. The goal is not to remove every debt from the system. That is not realistic. The goal is to identify the components and services where the interest is becoming expensive, choose the activities that need immediate attention, and address them as part of the normal engineering routine.

This only works when the work is visible. Technical debt should not stay as a private engineering complaint. We need to communicate the cost to stakeholders, explain the trade-offs, and show how engineering health protects delivery, velocity, and stability. That is how teams keep delivering high-quality software without letting the system rot quietly in the background.

References

Article: The WyCash Portfolio Management System

Article: Managing Technical Debt

Article: Erasing tech debt: A leader's guide to getting in the black

Article: 3 Main Types of Technical Debt and How to Manage Them

Blog Post: Dealing with technical debt

Article: What Is Tech Debt?

Video: Debt Metaphor