# The Skills AI Stopped Teaching Junior Developers

> Source: <https://menelaos.vergis.net/posts/The-Skills-AI-Stopped-Teaching-Junior-Developers>
> Published: 2026-07-30 13:41:12+00:00

## The Skills AI Stopped Teaching Junior Developers

### AI can generate code, but it can't replace the experiences that used to turn junior developers into senior ones.

Jan 01, 1970

Last month I asked a junior engineer on my team to make a small change. They asked Claude to make it, and Claude told them the change was invalid.

So they came back and told me it couldn't be done.

It could. So I said: let's do this one together, in Visual Studio, by hand.

We got the change made. What stayed with me afterward was the session itself, and the discomfort was mine rather than theirs: I had been quietly expecting a whole set of reflexes that nobody had ever given them a reason to build. Years of moving around inside an IDE leave a residue on how you work, and they hadn't had the years, because the job had never asked for them.

And Claude's refusal, I realised, hadn't been a setback. It had been a full stop, because there was no second route into the code for it to be a setback from.

## "So what should I read?"

They'd asked me a version of this question a few weeks earlier: how do I get better at this?

Ten years ago I would have said *Code Complete*. Two years ago I would have said the same thing, maybe with *Refactoring* and *A Philosophy of Software Design* stacked on top. Last month I said learn SOLID, learn DRY, look at Vertical Slice architecture, and go build things. All of that is still true. None of it was the thing that was actually missing.

Because every one of those books teaches you how to write good code. Not one of them teaches you what to do with code that arrived already written.

That isn't a gap in knowledge. It's a gap in ownership.

Nobody ever set out to teach you how to read a codebase. You learned it because every feature you shipped made you walk through it by hand, and the understanding was the residue of the walking.

An agent removes the walking, which is exactly what we want it to do, and the residue quietly stops forming with it. Two years ago you couldn't ship a feature without coming to understand the system by accident. Today you can ship a quarter's worth.

So the thing that has to be trained deliberately now is the part that used to arrive for free:

**Understanding the code.** Not "does it compile" and not "did the tests pass," but being able to trace what actually happens, in code that nobody on the team typed.

**Being at home in what the agent produced.** The moment that diff merges, it belongs to the team. That means being able to read it, judge it, disagree with it, and change one behaviour inside it by hand, rather than regenerating the whole file and hoping.

**Using the IDE as an instrument rather than a viewer.** Visual Studio is for finding things out: following a call chain, inspecting live state, asking who else depends on this. If it's only the place where diffs show up, it's a notepad with syntax highlighting.

## The invisible curriculum

Underneath those three, the specifics are all things nobody ever taught anybody:

**Navigation.** F12 to a definition, Shift+F12 for every caller, Ctrl+T to find a type you half-remember the name of, Ctrl+- to get back where you were, View Call Hierarchy to see who depends on this. Not the shortcuts themselves, the thing they add up to: holding the shape of an unfamiliar codebase in your hands.

**Letting the editor do the work it already knows how to do.** Ctrl+. on a red squiggle rather than going to look up the namespace yourself. Rename refactoring rather than find-and-replace. Generate a method stub from its call site. None of this is intellectually interesting, which is exactly why nobody teaches it and why not having it is invisible until you watch someone work without it.

**Debugging.** Placing a breakpoint as a hypothesis rather than a guess. Reading a call stack upward instead of staring at the top frame. Knowing that the Immediate window can test an idea without a rebuild.

**Test judgment.** Knowing which assertion actually matters. This one only comes from watching your own tests fail to catch your own bug, which requires having written both.

**Diff reading.** Holding another person's intent in your head long enough to disagree with it.

Every one of those was acquired by accident, and the accident has stopped happening. Which means it now needs some deliberate room made for it.

## The 90/10 rule

Banning the agent is not the answer. A junior's velocity with Claude Code is real, it's why we can take the work we take, and slowing them down to simulate 2019 would train them for a job that doesn't exist.

So split it:

**90% AI-accelerated.** Scaffolding, boilerplate, research, routine feature delivery. Full speed, no apology.**10% hand tools.** In that slice, the code that matters gets written by the developer.

That proportion is the entire budget, and it's worth being strict about it, because it's easy to design a beautiful training programme that quietly eats a third of someone's month and then dies the first time a release gets tight.

What they spend it on is their call. This isn't a syllabus.

One clarification that matters more than it sounds: the 10% is about not delegating the thinking, and it has nothing against tooling. Ctrl+. is not cheating. Roslyn analyzers, quick actions, rename refactoring, the debugger, the profiler are the point. This is IDE time, not Notepad time.

AI is a power tool. It makes you fast. But if you've never built anything with hand tools, you won't notice when the power tool is drilling through a load-bearing wall.

## The four chairs the agent is allowed to sit in

"No LLM at all during the 10%" was my first version of the rule, and it didn't survive writing the exercises down. It's too blunt, and it throws away the most useful things the agent does while someone is working by hand. The rule that actually holds is about role rather than abstinence:

**Spec author.** It writes the tests. Allowed, and the most valuable of the four.**Scaffolder.** It writes the controller, the DI registration, the EF model, the method stubs that throw`NotImplementedException`

. Allowed.**Critic.** It reviews what the developer wrote, under strict instructions not to fix anything. Allowed.**Sparring partner.** It explains code and it argues about it, whether the code came from the agent or from the developer. Allowed, on one condition: the developer goes first.**Author of the logic.** Not in this slice. That's what the 90% is for.

That condition on the fourth chair is doing real work. "Explain this code to me" is the fastest way there is to feel like you understand something while outsourcing the understanding, which is the same trade as letting it write the code, moved up one level. Committing to your own account of what the code does and *then* asking is a check on your model. Asking first is a replacement for it.

The arguing half matters more than it sounds, and it's not the same as the critic chair. Critic is "review this, list the flaws." Sparring is "I think this reservation should expire after two hours and here's why. Make the strongest case against it." That trains holding a position under pressure, which is the difference between an agent's refusal arriving as information and arriving as a verdict.

The pattern across all four: the agent can produce anything except the thing the developer is there to learn. It writes the spec when implementation is the skill being trained, and the scaffold when domain logic is. It can argue, once there's a position to defend. What it can't do is hand over the part that was supposed to build the muscle.

## Exercises for building the missing muscles

These aren't meant to be completed in order or in a single week. They're drills, not a curriculum. Pick one that matches the ticket you're already working on and spend your 10% there. Over time you'll revisit the same exercises as your judgment improves.

### Understanding

For getting inside a system nobody on the team typed.

#### Walk the code

**Trains:** comprehension · ownership · debugger fluency

Take a feature the agent built last sprint, and instead of reading it, run it. Breakpoint on the entry point, then step: F10 over the calls that don't matter, F11 into the ones that do. Watch the locals window change. Follow it down to the database and back up again.

Then do it a second time with input that should fail, an empty collection or a missing header, and see which branch actually picks it up.

That's where the mental model comes from. Reading a diff tells you what the code says; stepping through it tells you what the code does, and the two differ often enough to be worth the trip.

Afterward, put the agent in the sparring chair: ask it to explain the same class, and hold its answer against what the debugger showed. Sometimes it describes behaviour you just watched not happen.

Then make a small change by hand. Nothing dramatic: move a boundary, add a guard. The point isn't to prove anything, it's that editing code you've stepped through feels nothing like editing code you've only read.

This is the one to start with, because everything below assumes it.

#### Debugger only

**Trains:** debugging · live state · hypothesis testing

One rule: no `Console.WriteLine`

. Breakpoints only. Pick a real bug from the tracker, ideally in code they didn't write.

- State the hypothesis before setting the breakpoint. "If I'm right,
`quantity`

is already zero by the time we get here." Then go find out. Debugging is a science, and print statements let you skip the part where you form a claim. - Conditional breakpoints instead of pressing F5 forty times to reach iteration 39.
- Read the whole call stack, not the top frame. Most of the answer is two frames up.
- Exception Settings to break on
*throw*rather than on catch, so you see the state that caused the failure instead of the wreckage afterward.

#### The unassisted PR read

**Trains:** code review · reading intent · archaeology

Read an agent-generated PR with the agent turned off.

- Pull the branch locally, break a piece of the domain logic on purpose, and run the tests. If everything still passes, the suite is decorative and the PR goes back for real assertions. The defect is the tests, not the feature.
- Use Shift+F12 and git blame as archaeology: why does this line exist, who else depends on this, what broke last time someone touched it.
- Ask the two review questions, not one. Does it work, and does it speak our language? A method named
`Process()`

is a smell even when it's correct, because it means a concept never got named. (Same argument I made about[Domain-Driven Design and Claude](https://menelaos.vergis.net/posts/Why-Domain-Driven-Design-Is-a-Great-Fit-for-Coding-with-Claude): the vocabulary is the review surface.)

This is the mirror of the critique pass further down. There the agent asks the hard questions and the code is theirs; here the code belongs to someone else and they're the one asking. Same skill, opposite chair.

### Building

For implementing without handing off the judgment.

#### Locked-spec implementation

**Trains:** implementation · IDE navigation · interface design · edge cases

Turn TDD around. The agent writes the tests, the developer writes the code.

Prompt for tests only, and say so explicitly, or it will helpfully hand over the implementation too:

"Write a comprehensive test suite in [your test framework] for this feature, covering the happy path, boundary conditions, null handling, and error states. Do not write any implementation code. Tests and interfaces only."

One rule holds the exercise together: the assertions don't move. If a test looks wrong, that's a conversation, not an edit.

They open the empty service and write it line by line until every check in the runner is green. Interface design gets learned by feel, because inputs and outputs cross a boundary they didn't get to choose. And edge-case realism shows up as a red test for an empty collection they would never have considered if they'd written the implementation first.

#### Skeleton and muscle

**Trains:** domain logic · state modelling

The agent generates the controller, the EF model, the interface signatures, and stubs that throw `NotImplementedException`

. The developer fills in the domain logic by hand: the state transitions, the calculations, the transformations. They skip the setup they'd learn nothing from and keep the part where bugs actually live.

#### Spec first, agent second

**Trains:** specification · defining correctness

The exact inversion of locked-spec implementation. They write three or four failing tests by hand from the feature spec, confirm they fail for the right reason, and *then* let the agent write the implementation against them.

### Judgment

For telling real tests and sound code from plausible ones.

#### The critique pass

**Trains:** self-review · anticipating critique · self-correction

This one bolts onto the end of anything built by hand. Before the PR, they hand their own code back to the agent in the critic chair:

"Act as a principal engineer reviewing this code. List three flaws, missed edge cases, or performance problems, phrased as questions for me to think about. Do not rewrite the code and do not give me the fix."

Then they fix them themselves. Pound for pound this is the highest-leverage prompt on the list, because it puts a developer on the receiving end of a review while the code is still theirs to defend, and "do not give me the fix" is the entire difference between practice and dictation.

#### Sabotage

**Trains:** test quality · assertions

The trap with agent-generated tests is that they assert what the code *does* rather than what the system *should* do. They're tautologies. They pass forever, they push coverage up, and they catch nothing.

One way to audit them is to break their subject and see what notices. The developer opens a feature file the agent wrote and introduces three subtle bugs by hand: `>`

becomes `>=`

, a null check disappears, a boolean inverts. Then they run the suite. Every test that still passes is decorative.

The drill isn't finished at that point, though. They delete those specific tests and write replacements that catch the bug they planted.

#### Delete and rebuild

**Trains:** tests as spec · requirements

Pick one critical domain file, not a random one. Delete `OrderServiceTests.cs`

outright. Then: read the requirements, look at the public interface, and write five tests from your own mental model of how this feature should *fail*, without reading the implementation.

Afterward, diff those five against the ones that got deleted. The gap in either direction is the lesson.

## What keeps this from becoming busywork

**Protect the proportion, not the schedule.** A fixed rotation dies the first week a release slips, but an unprotected 10% quietly becomes 0%. Defend the budget and leave the choice of exercise open.**Real work only.** A ticket someone actually wanted, not a kata invented for the occasion.**No shipping pressure on that task.** The 10% is allowed to be slower than the 90%. That's the entire mechanism.**Role discipline, not abstinence.** The question is never whether they used Claude that afternoon. It's which chair it was sitting in. Spec author, scaffolder, critic, sparring partner: fine. Author of the business logic: that's the 90%, and this isn't it.**You do it too.** If the lead never turns the agent off, nobody believes any of this matters.

## How you'll know it's working

Not metrics. Signals, and they show up in ordinary moments:

- They can explain what a generated implementation does without opening it again.
- They fix a bug in agent-written code by editing it rather than by re-prompting it.
- They hit F12 without being told to.
- They set a conditional breakpoint instead of restarting the debugger nine times.
- They delete an AI-generated test on their own judgment, because they can see it asserts nothing.
- They reject a PR over a name rather than a bug.
- They tell you the agent is wrong, and they're right.

That last one is the signal I'm actually waiting for. The goal was never for anyone to use Claude less. It's for "Claude says it can't be done" to stop being a place where the work ends.

## The bottom line

Seniority has never been about how fast you can produce code. It's about how accurately you understand the state of a system when it breaks.

So keep the 90%. That's where the work ships, and pretending otherwise is nostalgia with a worse delivery date. But hold the 10% honestly: some real work, regularly, where the agent doesn't get to be the author. Debugger open, hands on the wrench.

I'm expanding these into a practical training repository so teams can use them with new hires.
