An interesting challenge I've found with the term "technical debt" is that engineers can use it as an excuse to work on all kinds of things that might not genuinely be paying down technical debt, taking advantage of situations where the people making the prioritization decisions don't have the hands-on technical experience of the codebase to evaluate if the proposed "improvement" is a good investment of effort or not.
AKA someone needs to be able to push back against "we'll rewrite it in Rust to reduce our technical debt".
The most effective place I've worked had a fixed allocation to technical debt work (25%) and let engineering agree how to prioritise that. Want to rewrite it in Rust? If you get buy-in from the rest of the technical team to say that that's more important than sorting out the database schema or whatever you can do that, but you'd better be able to do it piece by piece and keep delivering business value the whole time.
Theoretically someone who understood both the business and the technical side of things ought to be able to weigh up individual tasks from both sides and figure out which was higher priority. But in practice that seems very hard - you'd need both intimate knowledge of both domains and immunity to organisational politics. Explicitly adjusting that high-level dial (so maybe the 25% becomes 50% when it's a codebase you want to publish/reuse, or 0% when it's an EOL project that you're just running until it falls apart) might be the best you can do in practice.
Worked in a similar setup as a PM and really liked it. As you said, it can be somewhat apples-to-oranges comparing new functionality with a stream of tech debt opportunities, so it’s helpful to target a ratio, more like stocks/bonds in a portfolio.
We called it “product health” so it could encompass tech debt but also UX debt, performance, cosmetics, etc.
I've been quite frustrated that the term "technical debt" is so misused. It feels like project managers immediately assume technical debt means "the developer wants to play around". I only use it when it means "the features you want can't be implemented, correctly, and under the time constraints".
For what it's worth, I shy away from the term and talk more about approaches and outcomes. It's still baffling when I give a project manager the options:
1. Rewrite and have a working prototype in 2 weeks and solid product in two months. We will be able to use better tech, learn some new things, and the product will match the feature needs.
2. Refactor this month and get the feature you want in a couple months after that. We won't learn as much, but we'll have a cleaner technical product and we'll get some features.
3. Try to implement the feature now. It will be late, broken, and other features will break. We will spend all of next quarter patching it. We won't learn anything new. It will slow us down on the next feature.
Some of this is a product of job-hopping culture and of having boom-time teams (technical and management) with too many early-career members. Some of it is also just the perpetual back and forth of competing interests.
That said, for those who do good work and develop long-term trust with long-term colleagues, addressing technical debt is often “poke at this while I sip coffee” work that get chipped away at gradually and without a lot of paperwork, or that gets quietly slipped in alongside other feature work.
When you’re having to pitch the work to someone, you’ve often already failed the pitch. The debt payment is already due.
But if your peers come to trust you, then they learn not to reject a PR that cleaned up this module a little more than strictly necessary or that included a commit that revisited an adjacent abstraction.
To get there, though, you need to work on healthy, stable teams with experienced colleagues rather than in the mad churn that seems to dominate a lot of work cultures these days.
> I've never had an issue where a coworker rejected a PR, even when doing a massive refactor that isn't strictly necessary.
As an engineer, I have rejected massive refactors in unrelated PRs: heck, I have rejected massive PRs period.
They always:
- are hard to review
- bundle a bunch of unrelated changes together
- hard to accept piecemal
- risky to test and deploy
- hard to revert
- hard to pivot according to new learnings along the way
- hard to improve
- have lots of review iterations
- slow down all the other work (conflicts, remerging effort for all the other changes in flight...)
- usually "one way doors" (relates to hard to revert)
When these are done as small incremental steps where we improve one thing at a time, none of the above hold, and coupled with a good CI/CD pipeline, take less time.
I know that many engineers believe there are things that can't be done incrementally like that, but I've always been able to give them a plan for any "impossible-to-split" refactor/rewrite.
I think you are offering bad options: refactoring is something that comes with any new work. You don't have to justify it, but include it in your estimates.
Obviously, the risk here is that you include non-neccessary refactoring work in it, and then people stop trusting you.
And finally, there is a hack-it-together approach, but I always try to keep that outside the core product to make it clear this is throwaway effort (if you can have another deployment, that's ideal).
But honestly, it is engineers job to find that hard to reach balance: keep improving the code, and keep delivering value.
That's the hard part of software engineering, and we should all embrace it.
> refactoring is something that comes with any new work. You don't have to justify it, but include it in your estimates.
Sometimes it's too much work to do ad hoc. Oftentimes people won't go out of their way to refactor. Having the fair discussion can make it real and important.
If the team can't talk about refactoring, it's an unhealthy team. Managers who want to act like maintenance of a project isn't something that should ever be their concern don't deserve a paycheck.
> But honestly, it is engineers job to find that hard to reach balance
This attitude is bullshit. It's everyone's job. High level balance is more of a concern for management. Low level balance is the more of a concern for engineers. High and low level balances can work for or against each other. Management that just pushes their responsibilities down the hierarchy aren't pulling their weight.
Sure, it is everyone's job and they should certainly openly talk about it, but no manager can go and do it for an engineer.
A great engineer can find an incremental value with any refactoring they do: otherwise, they are extremely likely to refactor for the wrong future. I've seen this play out a number of times.
And the root cause is always exactly the same: engineers can't design code for the future that's not here today or at most, tomorrow. When they think they've done it, a new future comes and that code is even harder to refactor because it prematurely catered to cases that never materialized.
But that's exactly why managers need to understand and accept that refactoring is software engineering, and engineers need to do it continuously and keep delivering value while they do it.
And while CTOs, Eng Directors, architects and technical leaders might be "managers" in a sense, to me they are still all engineers, and they are the ones ensuring technical direction enables a healthy project while satisfying business goals.
Non-technical managers are there to bring clarity to business requirements, but they don't need to know exactly how sustainable technical excellence (or at least health) is achieved, the same way engineers don't need to know how user research or user testing that proves something works or not, is performed.
The issue here is offering up #3 “It will be late, broken and other features will break” means you are not delivering anything of value, so it is not an option. Offering #3 just communicates “this can be done at a cost that is not relevant to you” as opposed to “there is no way to achieve the stated outcomes”
Offering #3 is absolutely not a business or management problem, this is something you are doing wrong.
If engineers do that, then they don't need to use the term "technical debt" anyways. They can just claim a feature takes twice as long and then play around 50% of the time.
So in reality, it's not "technical debt" that's the issue, it's that engineers don't have the right incentives from the beginning.
Well, when developer productivity is measured against PR rate or number of commits, I guess this is what happens. Also, it’s debatable if someone should be managing a team of engineers if they don’t have a honed bullshit detector to catch this sort of things.
AKA someone needs to be able to push back against "we'll rewrite it in Rust to reduce our technical debt".