Dear past Jenna,
I know you're used to dealing with large, complex, legacy codebases riddled with technical debt. And for the longest time, that was kind of your thing. You'd unwind them, figure out how to insert a feature here without breaking functionality there, or discover that customers had come to depend on something that was technically a bug.
I recently saw this post on X (it's no longer a tweet on Twitter, but that's a story for another day):
The replies and reposts were all over the place, but a number of them went like this:
The technical debt is the whole codebase now.
The thing is, both might be right: AI makes technical debt easier to fix. Agents can refactor repetitive patterns, migrate dependencies, add tests around legacy behavior, and tackle cleanup work that might otherwise sit in the backlog forever.
Codex, go fix all the technical debt in this repo.
A reasonable request with no follow-up questions whatsoever.
But AI also makes technical debt incredibly easy to create.
Technical debt isn't just bad code
Technical debt has never just meant bad code, and it isn't necessarily the result of bad engineering. It's often the result of intentional tradeoffs we made at a specific point in time (except for that one guy who just refused to write unit tests).
But a lot of it comes from perfectly reasonable decisions that became wrong as we learned more about how the system would actually be used and how it needed to evolve.
Codebases grew. Requirements changed. Customers started relying on buggy functionality (congrats, it's a feature now). Systems scaled across teams. Teammates came and went. Dependencies aged. The architecture that made perfect sense three years ago became the thing standing in our way today.
We made the best decisions we could with what we knew at the time.
Then we put the software in production and learned more.
Some technical debt is simply the gap between the decisions we made then and what we learned we actually needed later.
Now we can build faster than we learn
It used to take time to turn an idea into a large system. And while we were building, we were also learning.
Remember the friction we talked about before? Some of that friction forced us to pause and ask important questions like, "Should we build this?" Designs changed. Requirements were questioned. Implementation and learning happened somewhat concurrently.
Now, engineering teams can accumulate an entire system before they fully understand what they're building (or what they've built).
AI can compress implementation time. It can't necessarily compress the time it takes to learn what the system needs to become.
I'm not arguing that slow development was good. But some of the time we're eliminating was time we spent learning.
You can't generate production experience
Some of my hardest lessons came from production issues that were nearly impossible to reproduce in a test environment.
Threading and concurrency bugs. Or those bugs that required a very specific alignment of the stars between customer data, system state, timing, and apparently the current phase of the moon (I'm looking at you, VAT tax calculator of 2009).
A design can appear completely reasonable until you put real traffic on it. Real traffic introduces timing problems, race conditions, and interactions you didn't anticipate.
An agent might build a system that performs exactly as expected. It might even anticipate problems we wouldn't.
But neither of us has a crystal ball.
While agents can help us implement faster, that's not the same thing as learning faster.
What if we spent the speed differently?
Instead of:
build → build → build → build → build → ship → observe → learn → change
What if turning five weeks of implementation into one meant you could get to production and start learning sooner?
build → ship → observe → learn → change → repeat
Implementation has gotten much cheaper. But reviewing, testing, validating design decisions, securing, operating, and learning from production haven't gotten cheaper at the same rate.
I ran across Brandolini's law (the "bullshit asymmetry principle") last week:
The amount of energy needed to refute bullshit is an order of magnitude bigger than that needed to produce it.
The code can be perfectly fine and still take much longer to review than it did to generate.
Codex, refactor this module without changing its behavior.
87 files changed.
An agent can produce a giant PR in minutes. Figuring out whether it should even exist, whether it's correct and secure, fits the system, and will be maintainable can still take hours.
And all of this is happening in an environment that's now go, go, go.
The expectation that "agents make you faster, so now you can do more work, all the time" is real. If leadership sees faster implementation and responds by asking for more of everything, then we're pushing more software through engineering processes that haven't experienced the same speedup.
The problem starts when we spend all of that speed on more implementation before we’ve learned enough to know it's worth spending time on.
Spend the speed on learning
I hope AI really does mean we won't have to live with technical debt the way we have in the past. Maybe agents will eventually manage much of it continuously, without us even thinking about it. And how we create, carry, and pay down technical debt may look completely different in a few years. I just don't know yet.
What I do know is that we don't have to spend all the time AI gives us on more code.
We can ship something, find out where we were wrong, change it, and do the whole thing again.
So for now, maybe we use faster implementation to shorten the whole learning loop.
Until the next postcard.
Top comments (26)
Getting to 80% good enough is the part that is practically instant now, but as you get into, we don't really bottle up that win and spend it well
Yeah, I feel like we're all being pushed to just start more work with that 20%, but I also don't think that everyone realizes/acknowledges we're doing this (other than those added pressures to do more, bigger, better, faster, all the time) - to some extent, this is all still so shiny to us.
Thanks for reading Ben!
One thing AI probably makes cheaper is replacing code. What it doesn’t automatically make cheaper is replacing history. Ten years of edge cases, operational knowledge and customer dependencies can still be hiding behind a module an agent can rewrite in an afternoon.
Agreed. Thanks for reading!
"But a lot of it comes from perfectly reasonable decisions that became wrong as we learned more about how the system would actually be used and how it needed to evolve."
This resonated with me beyond just coding, building, and architecture. It's philosophical. Aren't we all doing this all the time? Making reasonable, grounded decisions with the information we have in the moment. I look back at my early builds and think they're silly. I look back at decisions I made in my 20s and those are even sillier.
Great post!
Yes, totally! I feel like we've got the blinders on right now though and forget this sometimes.
Thanks for reading!
The "build faster than we learn" part is where I keep getting burned. I've spent weeks helping untangle prompt templates from a pipeline where someone encoded a data assumption in week one and then everyone just built on top of it. Nobody wanted to touch it because by week four there were six tools downstream that all expected that shape. AI made it trivially easy to add more things on top of the wrong foundation, and that version of debt is somehow even less visible than messy code.
"AI can compress implementation time, not learning time" - agreed, and there's a second asymmetry hiding underneath it: learning doesn't accumulate by default. The lesson production teaches you in week 40 usually lives in a postmortem, a Slack thread, or one engineer's head - and the next build starts from the same priors that created the debt. Faster implementation without retained learning isn't a shorter loop; it's the same loop at higher RPM. You re-make the week-one assumption Tae describes, faster, in three more places.
So I'd give "spend the speed on learning" a storage half: when a decision turns out wrong in production, record it at the moment it resolves - not the week-one symptom, but the triple of what we assumed, what broke it, and what holds now - and make that land in front of whoever (or whatever agent) makes the next adjacent decision. We got a live demonstration of the gap this week: our own monitoring had been announcing a failure for hours while the "learning" sat unconnected in an alert channel; the fix took minutes once someone carried the alert to the code. Debt as "the gap between what we decided then and what we learned later" shrinks exactly as fast as that feedback gets fed back into the next decision - and no faster.
The framing that really lands for me: technical debt as "the gap between decisions we made then and what we learned later" — that's not a code quality problem, it's a learning latency problem, and AI agents can't fix that part.
Where I've seen AI genuinely help on the debt side: running it against legacy code with a characterization-test-first loop. I point a local agent (Ollama + an open model, costs me $0 and the code never leaves my machine) at one gnarly module, have it write tests that pin the current behavior — bugs included — and only then let it refactor. The "congrats, it's a feature now" class of debt survives the refactor because the tests encode it. Without that step, the agent happily "fixes" the bug that three customers depend on.
But your other point is the one that keeps me up: AI makes debt incredibly easy to create. Velocity went up 3x in my side projects, and so did the half-finished abstractions I only half-reviewed. Cheap code means the review bottleneck, not the writing bottleneck, is now where debt accrues.
Curious how others handle this: do you review AI-generated code with the same rigor as a junior dev's PR, or have you built a different gate (evals, mutation testing, something else)?
Most of the thread is about spending the speed better inside engineering. The thing I'd add is that in some places the loop has a floor engineering doesn't control.
Regulated change runs on process time — approval, evidence, anything that moves money or touches customer data. Those cycles are set by other people's calendars, not by how fast the code appeared. So compressing five weeks of implementation into one doesn't put you in production five times sooner. It queues five times as much work in front of a stage that never sped up. And piling work in front of a fixed bottleneck doesn't just fail to help, it makes things worse: bigger batches, longer waits, more work sitting in the queue going stale while it waits.
Which sharpens your conclusion rather than arguing with it. If there's a floor, then more implementation is the single worst place to spend the speed. The interesting spend is on whatever the gate actually consumes, which is evidence. Most of what makes an approval slow is a human reconstructing what changed and why it's safe to ship. That part is generatable now, and unlike code, nobody is flooding that queue yet.
Brandolini's law applies to diffs too. 87 files is cheap to produce and expensive to refute.
The practical guardrail I’ve found is to make the learning loop explicit in the delivery contract: every agent-sized change gets a small production hypothesis, an observable signal, and a rollback or feature-flag path. That keeps “ship sooner” from becoming “merge faster and learn later.” It also makes review cheaper because the question is not only whether the diff is correct, but what evidence will tell us the design was wrong.
This landed for me as a freelancing problem as much as an engineering one. When agents make the first draft cheap, the temptation is to fill the calendar with more build tickets — and that’s exactly when “reasonable week-one assumptions” calcify into debt nobody wants to reopen.
A habit that helps on client work: spend part of the speed budget on a one-page learning brief before the next feature dump. Three lines only: what we assume is true, what production signal would falsify it, and what we’re deliberately not building this week. It forces the observe → learn → change loop you describe instead of build → build → build.
I’ve also started treating giant AI PRs the way Brandolini would: if review energy is 10× generation, the deliverable isn’t “more files,” it’s a smaller change with an explicit hypothesis. Curious whether others gate agent work on a written learning question, or if you only add that after something breaks in production.
Interesting perspective. One thing I'd add is that context matters a lot here — the right approach depends heavily on team size, project stage, and existing infrastructure. There's rarely a one-size-fits-all solution.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.