I can generate a full authentication flow with a database schema, API routes, and unit tests in under 30 seconds.
I can refactor a messy component into clean React hooks with one prompt.
I can ask an AI to explain a complex regex, and it will break it down for me line by line.
So why did my last "simple" feature still take three weeks to reach production?
If writing code is now practically free, why does building software still feel so incredibly expensive?
The Paradox of the 30-Second Pull Request
Last month, I was working on a feature that usually takes a few days. I fired up Cursor (or your AI tool of choice), typed a detailed prompt, and watched the code materialize on my screen.
It was beautiful. Clean. Formatted.
I felt like a 10x developer. I opened a Pull Request that same afternoon, feeling incredibly proud of my "velocity."
Then the real work started.
It took my teammate two days to review it. Not because the code was wrong, but because the AI had introduced a subtle edge case in the caching logic that neither of us initially understood.
When we finally deployed it, it broke a legacy system that the AI didn't even know existed.
We spent the next week debugging, rolling back, and rewriting the "perfect" AI-generated code to fit our actual, messy production environment.
That's when the uncomfortable realization hit me:
Writing the code was never the expensive part. It was always the cheapest part.
The Jevons Paradox in Software Engineering
In economics, there's a concept called the Jevons Paradox. It states that as technological progress increases the efficiency with which a resource is used, total consumption of that resource may actually increase, rather than decrease.
AI made writing code cheap. So what happened? We just demanded more code.
When it took a week to write a feature, we shipped one feature. Now that it takes an hour to write the code, management asks for five features in the same week.
But the bottleneck of software engineering was never typing speed. It's not the syntax. It's the cognitive load of context.
AI doesn't know your company's three-year-old architectural decisions. It doesn't know why that one microservice is fragile. It doesn't know the business logic that lives in the head of the developer who left the company two years ago.
So where did the cost go? It shifted to three places we rarely measure:
1. The Review and Trust Tax
Reading code is vastly harder than writing it. When a human writes code, you can ask them, "Why did you do it this way?" When AI writes code, you have to reverse-engineer its logic just to trust it. We are now spending more time reviewing code than we ever did writing it.
2. The Context Debt
AI is a brilliant sprinter, but a terrible architect. It can build a function perfectly, but it doesn't understand how that function breaks the other 50,000 lines of code in your repository. The "integration cost" of AI code is astronomically high.
3. The Maintenance Bill
Generating code is cheap. Maintaining generative code is expensive. AI often writes code that is technically correct but idiomatically weird. It creates abstractions that don't make sense to humans. And six months from now, someone is going to have to read it.
How We Actually Fix This
If code is cheap, we have to stop optimizing for typing speed. We have to start optimizing for decision-making.
1. Move the bottleneck to design.
Don't prompt AI to "build this feature." Prompt it to "suggest three architectural approaches." Spend the saved time deciding which approach is best for your specific context.
2. Protect your review capacity.
Your senior developers are now bottlenecks. If an AI generates 1,000 lines of code, the senior developer's brain is the only thing standing between that code and production. Buy them more time for review.
3. Focus on "Delete" over "Write".
The best use of AI is not to add new code, but to delete old code. Use it to untangle messes, write missing tests, and refactor legacy systems. Use the speed to reduce your maintenance burden.
The uncomfortable truth
AI didn't make software engineering easier.
It just changed the job from "writing code" to "editing code."
And editing requires a deeper understanding of the system than writing ever did.
If you don't understand the code an AI generated for you, you don't own that code. The code owns you. And the debt you are accumulating is going to come due.
Writing code is now a commodity. Understanding what to build, how it fits into the big picture, and how to keep it secure is where the real value lies.
You don't need to be a faster coder anymore. You need to be a better systems thinker.
Now I'm curious, because I know I'm not the only one feeling this shift.
Have you noticed that AI has made you type faster, but your features still take just as long to ship? Where is the biggest bottleneck on your team right now?
Is it the reviews? The testing? The architecture?
Drop it in the comments below. Let's figure out what the new "10x developer" actually looks like.
Top comments (98)
So true. These days I am barely writing any code but doing 99% of time editing and debugging😂
Haha welcome to the club! 😂 We are all slowly transitioning from Software Engineers to Professional AI Code Editors. The 99% debugging part is so painfully real.
"Writing the code was never the expensive part" matches what I keep seeing with agents too: the PR appears in an afternoon, then the review and the legacy edge case eat the week. The Jevons angle is the uncomfortable one — cheaper typing just raises the demand for more features, so the review queue never shrinks. Curious whether your team has tried capping PR size for AI-authored changes, or whether the review tax just moved into smaller, more frequent reviews.
Makayis PR in the afternoon review eats the week is painfully accurate. It perfectly captures the illusion of AI speed.
To answer your question honestly: it's a bit of both, but mostly the latter. The Review Tax just fragmented into smaller more frequent reviews. We didn't strictly cap the PR size initially, so a massive 1,000-line PR just became ten 100-line PRs.
But here's the catch: context switching between ten different AI-generated PRs is its own cognitive load. The total mental energy required doesn't shrink just because the PR is smaller. We eventually had to enforce a hard cap (around 200-300 lines) to force the AI to stay focused on one task at a time.
You're 100% right about the queue never shrinking. If we don't fix the feature demand, we're just optimizing how fast we drown. Have you found that capping PR size actually reduces the overall fatigue or does the frequency still wear you down?
Honestly, the cap on its own didn't fix the fatigue for me. Ten small PRs from ten unrelated prompts is just the big PR with extra context switches. What helped more was capping the scope per task, not the line count: one agent session, one intent, one PR, and I batch reviews into a couple of fixed slots a day instead of reviewing whenever something lands. The smaller diffs are easier to reason about, but the real win was that each one tells a single story. The other thing that cut review time was asking the agent to write a short "what I changed and what I didn't touch" note with the diff, so I'm checking claims instead of reverse engineering intent. Your 200-300 line cap sounds about right as a ceiling, though. Did you enforce it in CI or just as a team norm?
Makayis cap the scope, not the line count is the missing piece of the puzzle. You nailed it ten small PRs from ten unrelated prompts is just a fragmented 1,000-line PR. The context switching is what actually drains the mental battery.
The one agent session, one intent, one PR rule is a brilliant way to force the AI to stay focused. And I love the idea of batching reviews into fixed slots. That prevents your entire day from being hijacked by a constant stream of notifications.
Also, the what I didn't touch note is a game-changer. If the agent declares its boundaries you're just verifying claims instead of investigating intent. That saves hours.
To answer your question: it's a mix of both. We have a CI bot that warns (and sometimes blocks) PRs over a certain threshold, but the real enforcement is a team norm. If someone drops a 500-line PR, we kindly ask them to split it. It’s more about culture than just tooling. How about on your end did you automate the scope check or is it a team agreement?
Mostly an agreement, honestly. Scope is hard to check mechanically: a line count is easy for a bot, "this PR does one thing" isn't. The closest I get to automating it is that "what I changed and what I didn't touch" note. If the agent can't describe the diff in one or two sentences, that's usually the sign it wandered, and I split it before reviewing. A CI warning over a threshold plus a team norm for the rest sounds like the right mix to me.
Makayis if the agent can't describe the diff in one or two sentences it wandered is a brilliant heuristic. I'm stealing that.
You're completely right that scope is hard to automate. Trying to write a linter for this PR does exactly one thing usually just ends in false positives and developer frustration. Using the agent's own summary as a sanity check is a very clever proxy.
Totally agree with the final formula: CI warnings for the hard limits, and team culture for the soft ones. Thanks for the great back-and-forth, this has been super valuable!
Steal away. Thanks for pushing on it, this was a good thread.
Appreciate it Makayis This was one of the most practical discussions I've had on here. Have a great week ahead.
Likewise, thanks Harsh. Enjoy the week.
My experience rhymes with the 30-second PR story. The shift I made that actually helped was forcing the agent to write down its boundary assumptions before the code: which pieces of legacy behavior it thinks it understands, and which it flagged as unknown. Reviewing the assumptions list is fast; reviewing 500 lines for hidden ones is not. The weird part is that it flips the workflow around: the document becomes the deliverable and the code is just its rendering. I now treat an AI-generated PR the way I treat a vendor's API response: the payload is fine, but I only trust it after I have read the contract it claims to honor.
Has anyone tried making the agent's "unknowns" list a required PR template section? I am curious whether it survives contact with real deadlines.
Odinaka, the vendor API response analogy is absolute perfection. The payload is fine, but I only trust it after reading the contract is exactly the mindset we need for AI PRs.
To answer your question about making the unknowns list a required PR template section: I haven't implemented it yet but I think it would 100% survive deadlines if framed correctly. The trick is that reviewers must reject any AI PR that doesn't include the assumptions section. Once the team realizes it actually saves them hours of reverse-engineering it becomes a lifesaver, not a chore.
It flips the workflow exactly as you said: the document becomes the deliverable, and the code is just the rendering. I'm definitely going to try pitching this to my team. Have you tried it on your end yet?
I haven't — I work solo, so there's no team to enforce it on, and my own PRs get the assumptions list informally in my head. What I have done is the reverse: I require it of the tools I use. Before any AI-generated change touches my codebase it has to state what it doesn't know, because the unknowns list is where the blast radius hides. Your enforcement mechanism is the part I'd steal — reviewers rejecting any AI PR without the assumptions section is what makes it survive deadlines instead of surviving only the quiet weeks. If you pitch it, watch the first production incident: that's the real test, not the calm sprint.
Odinaka enforcing it at the tool level is actually a genius move for solo work. You basically built a one-person guardrail system. And 'the unknowns list is where the blast radius hides' is such a killer phrase.
You are 100% right about the first production incident being the real test. Calm sprints make every new process look like a miracle cure, but nothing tests a workflow like a PagerDuty alert at 2 AM. That's when people either stick to the rules or abandon them completely.
I'll definitely keep that in mind when pitching this to my team. Thanks for the great follow-up!
That 2 AM PagerDuty test is exactly the pitch I would use, because it separates a rule people follow when they are fresh from one they follow when they are tired. An optional template section gets skipped at 2 AM, but a hard gate does not: if reviewers reject an AI PR without an assumptions section, the author has to write down what the model did not know before anyone merges it. I would frame it to your team as incident cost, not paperwork — one wrong vendor assumption shipped at night costs more than every assumptions list you will ever write. Does your team already reject PRs for a missing test, so this could ride that same gate?
Odinaka incident cost not paperwork is a brilliant framing. If I pitch it as just another checklist management will ignore it. But if I frame it as preventing an expensive production incident, it suddenly gets priority.
And you're absolutely right about optional vs. hard gates. Anything marked optional might as well be deleted. It has to be a hard block in the CI or the PR template, or it will just get skipped when people are rushing.
To answer your question: we do have a CI gate that blocks PRs if tests are missing or failing. So you're right tying the assumptions requirement to that same existing gate makes perfect sense. It forces the author to explain intent before they even hit merge.
I'm taking this exact playbook to my team next week. Thanks for the back-and-forth, this has been incredibly valuable!
That CI gate is the right anchor, and the gap I would still test is what the gate actually measures: a gate that blocks missing or failing tests still passes a test that exists but asserts nothing, like a test that calls the function and checks only that it did not throw. In my own work, the rule that held was tying the assumption to a named assertion — the author had to state which behavior the test proves, not just that a test exists — because a passing placeholder test is worse than a missing one, since it silences the gate. When you take this to your team, will the gate check that the assumption names a behavior the test asserts, or only that tests exist and pass?
Odinaka, this is a brilliant catch. Passing placeholder test is the exact loophole AI loves to exploit. It will happily generate a test file that imports the function calls it and asserts absolutely nothing, just to make the CI turn green.
To answer your question the CI gate can only enforce the format. It can check if the PR template has the assumptions section filled out and maybe check if the test name matches a specific pattern. But it can't evaluate the quality of the assertion.
So realistically the CI gate will check that tests exist and that the assumption is documented, but the human reviewer becomes the final gate. The reviewer has to verify that the documented assumption actually maps to a real assertion in the code. If it doesn't, the PR gets rejected. I'm definitely going to pitch your assumption = named assertion rule to the team. It bridges that exact gap.
Thanks for pushing this deeper it's a massive improvement to the workflow!
asserts nothing, still green. that's the loophole in one line.
I'd say the biggest bottleneck is review, and specifically review of code nobody on the team can explain. When a person writes a tricky caching path, you can ask them why. With generated code, the reviewer ends up doing the reasoning the author skipped, so the cost moved, it didn't disappear.
Two things that help: keep generated PRs small enough that one person can hold the whole change in their head, and ask the author to write the edge cases in the PR description in their own words before review. If they can't, the PR isn't ready, no matter how clean it looks.
Security review has the same Jevons problem. Five times more code means five times more surface to check, and the threat model doesn't get faster just because the typing did. Systems thinking is the right conclusion.
Kiell this is an absolutely brilliant breakdown. You just added two massive dimensions I didn't fully explore.
First the reviewer ends up doing the reasoning the author skipped' perfectly captures the hidden tax. And your litmus test for PRs is gold: if the author can't explain the edge cases in their own words, the PR isn't ready. I'm 100% stealing that rule.
Second the security angle is terrifying and spot on. I didn't explicitly call out the security surface area, but you're right: AI scales the codebase instantly, which scales the vulnerability surface. The threat model doesn't get faster just because the typing did.
As you said systems thinking is the only way out. Thanks for adding so much value to this discussion!
Steal away 😄 The "explain the edge cases in your own words" test is cheap, and it works on any PR. Thanks for writing the piece, Harsh. The cost framing is what made the discussion worth having.
Thanks for the kind words, Kiell I'm really glad the cost framing resonated with you.
Honestly it's discussions like these that make writing on dev so rewarding. I've already added the explain the edge cases rule to my team's checklist. Thanks again for adding so much value to this thread!
That's great to hear! I'd love to know how it goes with your team once it's been in the checklist for a while, especially whether it catches things in review that the tests missed. Thanks for writing such a thought-provoking piece.
Thanks Kiell I'm really curious about that too. It'll be fascinating to see if human review catches architectural blind spots that the AI-generated unit tests completely ignored.
We often write tests to prove what we did build, but human review with this rule might be the only way to catch what we forgot to build. If it yields any surprising results it might just turn into a follow-up article! Appreciate the great discussion.
"Tests prove what we did build, review catches what we forgot to build" is a great line, and I think it's exactly the gap. Generated tests tend to mirror the implementation, so they can't notice a missing piece. I'd definitely read that follow-up if the results surprise you. Thanks for the great discussion too!
Spot on Kiell Generated tests mirror the implementation is the perfect way to describe that echo chamber. If the AI misunderstood the requirement in the code it will just write a test that validates its own misunderstanding. I'll definitely keep you posted on the follow-up piece. Thanks again for the great discussion!
The v0 shop I took over had RLS switched off in Supabase and a contact form that sent messages nowhere. Owner's take was "what matters is that it works". Was your 3 weeks the feature or cleaning up after the 30 seconds?
Artur RLS switched off in Supabase literally sent a chill down my spine. That is the ultimate AI-generated nightmare.
And the owner's quote what matters is that it works is probably the most expensive sentence in software engineering. It works until it gets hacked, or until the contact form drops a million-dollar lead.
To answer your question honestly: it was a mix of both. The 30 seconds got me the feature, but the 3 weeks was me cleaning up the Context Debt so it didn't turn into the v0 shop you inherited. If we don't spend those 3 weeks fixing the edge cases, the next dev spends 3 months fixing the blast radius.
Thanks for the laugh (and the nightmare fuel)
Going on a sticker for sure. Do you ever actually get those 3 weeks upfront, or only once something breaks?
Haha I'm honored to make it to the sticker😉 honestly: 90% of the time, it's only after something breaks. It's the classic firefighter vs. building inspector problem. Management will happily pay you to put out a fire at 2 AM but asking for 3 weeks upfront to install a smoke detector is suddenly too expensive and slowing down delivery.
The only way I've actually gotten that time upfront is by hiding it in the estimate. You don't call it cleaning up AI debt. You call it Security Hardening or Edge-Case QA. If it sounds like actual work on the roadmap they approve it. If it sounds like maintenance they skip it.
Have you ever successfully pitched the 3 weeks upfront, or is it always firefighting for you too?
Firefighter vs. building inspector is exactly it. On my side it's a bit easier because I run a small studio and quote fixed prices, so the "3 weeks" never shows up as a separate line. It's just part of what a finished site costs: security headers, RLS checks, form handling, the boring edge cases. Clients don't approve maintenance, they approve a site that doesn't break. Where it gets hard is takeovers like that v0 shop. There the cleanup is visible, and I sell it the way you describe, as an audit with a list of concrete risks instead of "refactoring". A screenshot of an open Supabase table convinces faster than any estimate.
This is a masterclass in client management. Clients don't approve maintenance, they approve a site that doesn't break is such a killer line. You're basically packaging the exact same work (Context Debt cleanup) but framing it as business value.
And the screenshot of the open Supabase table is absolutely genius. Show don't tell. A concrete screenshot of a glaring security risk will always win over a spreadsheet of estimated hours. No client wants to see their data exposed to the world.
It makes total sense for a small studio with fixed pricing you just bake the cleanup into the internal cost so the client gets a working product. Thanks for sharing this perspective, it's incredibly valuable for anyone doing client work!
"Masterclass" is generous. It's mostly what's left after losing that argument a few times too many. Thanks Harsh, good thread.
I think the biggest bottleneck is context and verification. AI has reduced the time it takes to write code, but it hasn't eliminated the need to understand the system the code is entering.
The tricky part is that working code doesn't automatically mean correct code. It might pass tests and still break an existing workflow, introduce a security issue, or fail under real-world conditions.
I also think AI should help us reduce complexity, not just produce more features. Sometimes the best outcome isn't generating 1,000 lines of code; it's finding a way to solve the problem with 100.
The developers who stand out won't necessarily be the ones who generate code the fastest. They'll be the ones who can judge the output, understand the trade-offs, and take responsibility for what eventually reaches production.
AI is making code cheaper, but engineering judgment is becoming even more valuable.
Egbuta this is a fantastic breakdown. You hit on something I wish more teams talked about: Working code doesn't automatically mean correct code. Passing tests is just the bare minimum; surviving real-world conditions is the actual bar.
But your point about reducing complexity is the real masterstroke. AI defaults to adding code to solve a problem. It will happily write 1,000 lines to fix a bug that a senior dev could fix by deleting 50 lines. Using AI to simplify systems rather than just inflate them is the true mark of an expert.
And your closing line is the perfect summary of this entire discussion: AI is making code cheaper, but engineering judgment is becoming even more valuable. I couldn't have said it better myself. Thanks for adding so much value here!
Thank you! I really appreciate that. I agree that knowing when to simplify a system is just as important as knowing how to build it. There's still so much to learn as AI continues to change the way we develop software. I'd love to connect with you here on DEV and keep learning from your insights. Looking forward to more discussions like this!
Thank you Egbuta I really appreciate that, and I just followed you here on DEV.
You're absolutely right knowing when to simplify is a superpower. We're all figuring out this new AI era together, and it's conversations like these that help us make sense of it. Looking forward to seeing your thoughts and keeping the discussion going in the future!
absolutely
The review bottleneck exposes a fundamental asymmetry between generation and verification that gets glossed over in developer tooling demos.
When an engineer writes code manually, the mental model of edge cases, state transitions, and invariants is built during the drafting process. The code is simply the artifact left behind by that reasoning. When an LLM generates five hundred lines in thirty seconds, that construction step is bypassed entirely. That reasoning work simply moves downstream into verification.
Verifying third-party code without an explicit model of its boundary assumptions becomes an unconstrained search problem. You are no longer just checking whether the implementation satisfies a ticket. You have to reverse-engineer what invariants the model silently ignored while optimizing for local coherence. Auditing for the absence of subtle edge cases requires exploring the entire state space of the surrounding architecture, which takes significantly more time than writing the initial implementation.
This creates an adverse selection trap in pull request reviews. Because thorough auditing is so exhausting, teams inevitably fall back on superficial checks, relying on green unit tests that were often generated by the very same model to test its own assumptions. The true cost of the code does not disappear from the balance sheet. It simply shifts from visible engineering payroll into hidden tail risk that sits dormant in production until an unmapped integration breaks.
Dean this is one of the most brilliant breakdowns of this problem I've read. You've put words to something I was feeling but couldn't articulate.
The unconstrained search problem perfectly captures why reviewing AI code is so exhausting. When I write code, I'm building the mental model as I type. When AI writes it, I'm dropped into the middle of a maze with no map. I have to figure out what it built and why it built it that way simultaneously.
But the adverse selection trap is genuinely terrifying. The fact that the LLM writes the code and then writes the tests to prove its own code works is a massive blind spot. We are literally asking the student to grade their own exam, and we are trusting the A+ because the paper is neat.
How do you personally escape this trap? Do you mandate that tests for AI-generated code must be written by a human, or is there another way to break that hidden tail risk?
The cleanest way to break the circular dependency is invariant decoupling. You never let the model generate both the implementation and the test assertions in the same context.
Either write property-based tests and boundary invariants first, or use a separate adversarial prompt whose only objective is to find state transitions that violate production constraints. If a human has to review anything, reviewing five hard invariant properties takes a fraction of the time needed to audit three hundred lines of self-congratulatory unit tests.
Dean this is incredibly actionable. Invariant decoupling is exactly the term I was missing. the adversarial prompt' idea is a game-changer. Most of us default to write tests to prove this works which as you perfectly put it just yields self-congratulatory unit tests. Flipping the prompt to find three ways this logic breaks completely changes the AI's objective from being a cheerleader to being a red teamer.
I'm definitely going to start using property-based tests for AI-generated logic. Thanks for dropping this gem of a solution!
Good post, and an engaging thread that has extended the post.
Fix 2 assumes that review capacity is a fixed cost that you have to fund. Reviewing a diff is expensive. It would be cheaper to review a shorter plan before the agent starts generating numerous lines of code. Odinaka and Kiell have the same idea but for the PR and edge-case scenario. By keeping the plan in the repo along with the code, any agent can pick up from where it left off. That will take care of the context debt.
George this is a fantastic synthesis of the whole thread. You're absolutely right Fix 2 (funding more review capacity) is a reactive approach. Reviewing a 1-page plan is infinitely cheaper than reviewing a 1,000-line diff.
Keeping that plan in the repo alongside the code is the ultimate antidote to Context Debt. It essentially turns the plan into an ADR (Architecture Decision Record). When the next agent or developer picks it up, they don't have to reverse-engineer the intent; it's right there in the repo.
It shifts the workflow from generate and verify to plan verify and then generate. Huge fan of this idea. How do you usually format these plans? Just a markdown file in the PR, or a dedicated folder in the repo?
Markdown files in the repo in a folder structure that is a state machine. That works.
Dean Lee's point about the mental model is the heart of it for me. When someone writes code by hand, the edge cases get worked out while typing, and the code is what is left over. With generated code that reasoning never happened, so the reviewer has to rebuild it from scratch, which is why the caching bug took two days to find.
I would add one more place the cost went: deciding what the feature is. Your three weeks started with a detailed prompt, but the legacy system that broke was never in that prompt, because nobody had written down what the change touched and what done meant in production. When that is written first, on one page, both the generation and the review get smaller.
That is also how we scope client work at Ascendra Ventures, one clearly defined problem with a written finish line rather than hours. Cheap code made that page worth more, not less. Which part of your three weeks would it have removed?
This is such a sharp addition. You are absolutely right: the biggest missing piece wasn't just the code review, it was the lack of a defined 'finish line' and impact analysis before generation even started.
To answer your question about my three weeks: writing that one-page scope would have completely eliminated the week I spent debugging the legacy system. If we had explicitly written down 'what this change touches,' we would have realized the AI lacked the context of our old system. It also would have cut the review time in half, because the reviewer would have had a clear baseline to measure the AI's output against.
I love the way Ascendra Ventures scopes client work with a written finish line. It completely flips the script: cheap code makes the scoping phase worth more, not less. Thanks for sharing this perspective, it's going straight into my workflow.
The 30-second PR that takes two days to review is exactly the trade. AI moved the cost from typing to understanding, and understanding still runs at human speed. On my own side projects the expensive bits are never the code. It's store reviews, platform rules, and the one edge case real users find on day one.
Nilamadhab understanding still runs at human speed is a brilliant way to put it. I might have to steal that line!
You're completely right. We get so obsessed with the coding part that we forget the actual expensive bottlenecks of shipping: App Store reviews, platform compliance, and the absolute chaos of day-one user behavior.
AI can write the code for a feature, but it can't predict how a real user will break it in ways we never imagined, nor can it argue with an app store reviewer. The real cost of software has always been outside the editor. Thanks for sharing this!
Steal away 😄 Thanks for writing it, the 30-second PR line is going to stick with me too.