AI Pair Programming and the Junior Developer Skill Gap

Photo by Michael Surran via Wikimedia Commons (CC BY-SA 2.0)
Not across the board. It raises what a junior can ship in their first week and leaves the ceiling of what they understand untouched, so the risk is concentrated in one skill: debugging from first principles. Syntax recall was never the hard part and losing it costs nothing, but losing the read-hypothesise-test loop costs a great deal.
Five of them decay separately: finding the first stack frame that is your own code, stating a cause that can be proved wrong, designing the cheapest experiment that separates two causes, bisecting when no hypothesis survives, and noticing that your prediction and the output disagree. An agent will do the first two, mechanise bisecting, and almost always skip experiment design because it was asked for a fix.
Before running or asking anything, the junior writes three lines: the suspected cause, the evidence in the trace, and the check that would disprove it. Then they run that check, and only then paste the trace plus their three lines to the agent and ask which line is wrong. Predicting is what builds the mental model; watching a fix appear does not.
Ask why, not whether the tests pass — an agent will iterate until they pass, and a default value planted at the throw site goes green faster than a fix at the cause. Three questions do most of the work: what was your first guess and what killed it, which frame in the trace is ours, and what would have failed if you had been wrong. A pause on the first question is the signal, not a reason for a lecture.
The 2025 Stack Overflow Developer Survey reports that 84% of respondents are using or planning to use AI tools, and 51% of professional developers use them daily. Trust moved the other way: 46% actively distrust the accuracy of AI tools against 33% who trust it, and the most-cited frustration, at 66%, is solutions that are almost right but not quite. That combination is exactly why verification skill matters more now, not less.

Photo by Michael Surran via Wikimedia Commons (CC BY-SA 2.0)
Key Takeaway
AI pair programming does not erode a junior developer's syntax, which was never the hard part; it erodes the debugging loop of reading a trace, stating a falsifiable cause, designing the experiment that kills one hypothesis, bisecting, and noticing the model was wrong. Preserve the loop by predicting the outcome before running anything.
The pull request was three lines long and the tests were green. A TypeError in an invoice formatter had been fixed by defaulting the discount field to zero, and every row downstream rendered again. I asked the junior who opened it why that value had been undefined in the first place. The answer came back without embarrassment, because it did not feel like an admission: the agent had suggested it.
That fix was probably correct on the order it was tested against, which is what makes this hard to talk about. This post is about the one skill AI pair programming genuinely puts at risk — debugging from first principles — the five sub-skills it decomposes into, the drill I now use instead of explaining fixes, and the questions a reviewer has to ask to tell a learned fix from a borrowed one. Adoption and trust figures come from the 2025 Stack Overflow Developer Survey; the self-assessment figure comes from METR's randomised trial.
The skill an agent erodes is not syntax. A junior who cannot remember the argument order of a reduce callback was never blocked by that: the documentation was one browser tab away in 2015 and is one prompt away now. What juniors used to practise dozens of times a week, without ever calling it practice, was something else entirely — the loop between a symptom and a cause.
That loop got practised by accident, because for a long time there was no alternative route through it. A stack trace appeared, and the only way past it was to read the thing, guess, test the guess, and be wrong a few times before being right. An agent removes the monopoly by offering a second route, and the second route is faster on almost every individual bug. Nobody chooses the slow path forty times a week when a fast path is sitting right there, which is why this is a structural problem rather than a discipline problem.
Written out, the loop is short enough to audit. Each step is a separate skill, and each one decays separately once something else performs it for you.
An agent will happily do the first two, mechanise the fourth if you hand it a test command, and skip the third almost every time — it proposes a fix rather than an experiment, because a fix is what it was asked for. The fifth it cannot do at all, since the fifth happens inside a head that made a commitment. A junior who skipped straight to the fix has no commitment on record, so there is nothing for the output to contradict.
Ask a junior who has been pairing with an agent for six months to read a stack trace aloud and you learn quickly whether they have ever really read one. The common failure is not confusion. It is reading the top line, recognising the words, and stopping there.
TypeError: Cannot read properties of undefined (reading 'toFixed')
at formatCurrency (/srv/app/node_modules/@acme/invoice-kit/dist/format.js:41:28)
at Array.map (<anonymous>)
at buildRows (/srv/app/node_modules/@acme/invoice-kit/dist/rows.js:88:19)
at renderInvoice (/srv/app/src/invoice/render.ts:48:29) <-- first frame we own
at handler (/srv/app/src/routes/invoice.ts:22:11)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
// The stack proceeds from the most recent call to earlier ones (MDN), so:
// frame 1 = where it threw. Read it to learn what toFixed wanted: a number.
// frame 4 = the first frame we own. That is where the undefined was handed
// over, and the only line in this list we are allowed to change.
//
// The three-line fix defaults the field to zero at frame 1's doorstep.
// It turns a loud crash into a silent, wrong invoice: discount 0.00 on every
// order the query forgot to join. Green tests, incorrect PDFs.Which end you start from is a choice with a rule behind it. Start at the top when the error type itself is unfamiliar, because the top frame is the only one that tells you what the failing call actually wanted. Start from the bottom when the top frames are all inside a dependency, because your own program's last decision is the only thing in that list you are allowed to change. Neither habit is difficult to acquire. Both are invisible if a working fix arrives before the trace has been read.
The most valuable ten seconds in debugging are the ones spent turning a suspicion into a sentence that could be wrong. Two hypotheses that both explain the same symptom demand different experiments, and the experiment worth running is whichever one kills a hypothesis rather than flattering it.
// H1: discount is undefined because the query omits the join for orders
// created before the pricing migration.
// H2: discount is undefined because the DTO strips zero-valued fields
// on serialisation.
//
// Both explain the crash. Neither is worth arguing about, because one line
// at the boundary between them settles it:
console.log(JSON.stringify(rows[0]));
// discount present here -> H1 is dead, the loss is downstream in the DTO
// discount absent here -> H2 is dead, the loss is upstream in the query
// When nothing survives contact with the evidence, stop guessing and bisect.
// Any check that exits non-zero on the bug will do:
git bisect start HEAD v3.11.0
git bisect run npm test -- invoice.spec.ts
// About nine checkouts across four hundred commits, and no intuition required.Bisecting is the same discipline, mechanised. Once no hypothesis survives the evidence, a check that exits non-zero on the bug lets git halve the search space repeatedly — roughly nine checkouts across four hundred commits, instead of four hundred readings of a diff. Juniors reach for it rarely, and not because the command is obscure: it only feels worth setting up after you have accepted that your intuition is exhausted, and accepting that is itself a practised move.

Explaining a fix to a junior teaches far less than it feels like it does, because a clear explanation is pleasant to receive and costs nothing to accept. The reversal that works is to make them commit to an outcome first: prediction, then observation, then an account of the gap between the two. It is the predict-observe-explain sequence used in science teaching to surface misconceptions, and the mechanism is identical in a terminal — a wrong prediction that has been written down is a misconception with an address, so the output has something to correct.
# 1. WRITE THE PREDICTION DOWN. Before running anything, before asking anything.
# Three lines in a scratch file nobody reviews:
#
# cause: renderInvoice passes undefined for orders with no discount row
# evidence: the first frame we own is render.ts:48, not the library frame
# test: log rows[0] at render.ts:47 and expect no discount key
# 2. RUN THE EXPERIMENT YOU JUST WROTE. The experiment, not the fix.
node --enable-source-maps dist/invoice.js 8842 2>&1 | head -20
# 3. NOW bring in the agent. Paste the trace AND the three lines, and ask which
# of the three is wrong. A prediction gives a model something to correct;
# a bare stack trace gives it something to guess at.
# 4. Score it out of three: cause, evidence, test.
# A wrong prediction you wrote down is worth more than a right fix you
# watched appear. Only the mismatch changes what you believe.The agent is not excluded from this drill. It is moved. It goes from producing the fix to marking the prediction, which is a better use of it in every way: paste the trace and your three lines, ask which of the three is wrong, and the explanation you get is aimed at what you actually believed rather than at undefined values in general. The junior still gets the answer in under a minute. They just pay one minute of commitment for it first.
Ask for the prediction in the pull request, not in a meeting. Three lines in the description — suspected cause, the evidence in the trace, the check that would disprove it — cost the author two minutes, survive review, and make the difference between a learned fix and a borrowed one visible to whoever reads the PR next year.
None of this is a decline story, and any post that treats it as one is wrong about the last two years. Juniors joining now are better off on nearly every axis I can observe directly.
The honest summary is that AI pair programming raised the floor of what a junior can deliver and left the ceiling of what they understand exactly where it was. The distance between those two lines is the whole problem — and it is a far better problem than the one it replaced.
So the goal is not to withhold the tool, which would be both unenforceable and unkind. It is to keep one narrow activity deliberately manual: a couple of bugs a week where the agent may explain anything and fix nothing.
The 2025 Stack Overflow Developer Survey puts 84% of respondents using or planning to use AI tools in their development process, and 51% of professional developers using them daily. Sentiment moved the other way over the same period: more developers actively distrust the accuracy of AI tools, 46%, than trust it, 33%, and positive sentiment fell from over 70% in 2023 and 2024 to 60%. The most-cited frustration, at 66%, is solutions that are almost right but not quite. The second, at 45%, is that debugging AI-generated code takes more time.
Read those figures together and the training priority stops being a matter of opinion. Two thirds of the industry's daily annoyance is a class of output that looks correct and is not — which is exactly the output that only a practised verification loop catches, and exactly the output a junior with no loop will merge. Rising usage alongside falling trust is not a contradiction. It is a description of a workplace where verification is the scarce skill.
METR's randomised trial is the sharpest warning here, and note that it studied experienced maintainers rather than juniors: 16 developers across 246 issues, on repositories they had contributed to for years, took 19% longer with AI tools allowed. They had forecast a 24% speedup, and still believed afterwards that they had been sped up by 20%. If people with years on a codebase misjudge their own throughput by that margin, a junior's confidence that they understood a fix is not evidence of anything.

Reviewing a junior's AI-assisted pull request means asking why, not whether the tests pass. Tests passing is now the cheapest property a diff can have — an agent will iterate until they do, and a default value planted at the throw site reaches green faster than a fix at the cause. In the invoice case it reached green while quietly printing a zero discount on every order the query had forgotten to join, which is a worse outcome than the crash it replaced.
Three questions do most of the work, and none of them are adversarial. What was your first guess, and what killed it? Which frame in that trace is ours? What would have failed if you had been wrong? An author who debugged the problem answers all three in a sentence each. An author who accepted a suggestion pauses on the first, and the pause is the entire signal — not a cue for a lecture. The agent suggested it is a perfectly reasonable thing to have done; it is only a problem when nobody asks the next question.
The rule I hold to now is narrow enough to actually keep: the agent may explain any bug, and may fix any bug I am not trying to learn from. For the two or three a week that I am, the prediction goes down first — cause, evidence, disproving check — and the agent only gets to mark it. Reviewers hold the other end of the same rule by asking what was ruled out, because a fix nobody can account for is technical debt that passed its tests.
Sources and further reading