What Changes When Writing Code Stops Being the Bottleneck
Engineering in the age of abstractions — where leverage grows and judgment becomes the job.
I got my first real software job while I was still studying — part-time contracting at Deepnote.
It was a pile of firsts: my first startup, my first production codebase, my first time shipping to users I didn’t know. And, more quietly, the first time I learned that software engineering isn’t just “writing code.” At the time, I thought this was simply part of growing up as an engineer — learning how real systems behave once they leave the tutorial path.
In hindsight, it was something else.
It was my first encounter with a pattern that’s becoming impossible to ignore: what actually becomes hard once writing code stops being the bottleneck.
This post is about that shift — and why skills like product sense, taste, clarity, verification, and systems thinking matter more than they ever did before.
Learning to work with abstractions before I understood them
I started in a very 2020s way: I landed straight in React and TypeScript.
My first encounter with React didn’t come after years of JavaScript. I didn’t have muscle memory for DOM APIs, browser events, or the event loop. I more or less teleported into a world where a lot of complexity was already abstracted away — where the system worked as long as you spoke the right language to it.
At first, that felt liberating.
Until I hit the edges.
Something asynchronous behaves strangely. State updates don’t do what you expect. A re-render happens “for no reason.” And at some point you realize that the abstraction is only comfortable as long as it behaves. When it doesn’t, the real question isn’t whether you know every detail underneath — it’s whether you know how to reason about what might be going wrong.
When you don’t have that ability, you can still be productive. You can ship features and make progress. But your productivity is fragile — it depends on the abstraction staying within its happy path. The first time you hit a bug that isn’t “a React thing,” you feel it immediately. Not because the abstraction failed, but because your mental model did.
That experience taught me something I keep coming back to:
Abstractions aren’t the problem. They’re the reason we can move fast.
The risk is treating them as opaque — using them fluently without knowing how to interrogate where they leak, what they assume, or how to recover when they fail.
In hindsight, that experience was a preview of what’s happening now.
We’re building software on top of another abstraction layer — AI — and the leverage is real. In that sense, this isn’t a break from how software has always worked. It’s an acceleration of it.
The power doesn’t come from abandoning fundamentals. It comes from being able to work effectively at the abstraction boundary — knowing when to trust it, when to question it, and how to drill down when you need to. Increasingly, it also comes from using AI itself to surface and fill in those fundamentals exactly at the moment they matter.
The task that taught me what change actually costs
One of my first tasks at Deepnote sounds boring: converting CSS.
Just move styles from one system to another. That’s it. And yes — it was pretty boring.
First it was CSS to SCSS. Then SCSS to Emotion CSS. Because, as anyone who’s worked at a startup knows, these kinds of decisions can change from one week to the next.
With my part-time hours, that migration took me weeks. Not because typing SCSS or Emotion is hard, but because the work was mostly translation — rewriting the same intent over and over in a different shape, keeping everything consistent, checking that nothing subtle broke. It was tedious, mechanical, and surprisingly easy to mess up.
I didn’t realize it at the time, but that was my first close encounter with the real cost of software: not creation, but change.
This is exactly the kind of work AI is good at. It doesn’t get tired translating patterns. It applies transformations consistently, and those transformations live in very well-worn grooves of the training data.
If I did that exact migration today, I’d hand it to Claude Code or Codex and be done in about twenty minutes.
But speed isn’t the interesting part.
When writing code becomes cheap, what becomes scarce?
For a long time, we treated “writing code” as the core skill — the most visible signal of effectiveness — partly because it was the bottleneck.
Good engineers always had other skills, but code output dominated because it constrained everything else. If you couldn’t ship, none of the other strengths really mattered.
AI compresses that gap. People who already combine coding ability with product sense, taste, and systems thinking can now move dramatically faster. And people who had those traits but were previously blocked on raw output suddenly can too.
The bottleneck moved.
With the disappearing middle of software work, the job is increasingly not “produce code,” but deciding what to build, specifying constraints clearly, designing changes that fit the system, verifying correctness, and keeping the system healthy enough that future changes stay cheap.
When generation becomes cheap, the scarce resource becomes judgment — the ability to work at the abstraction layer without losing sight of consequences.
“Judgment” is an annoying umbrella word, so it’s worth breaking it down into the parts that actually show up in day-to-day work — especially once AI enters the loop.
The new bottlenecks
1. Product sense
AI can propose implementations for almost anything. It can generate ten variants before you finish your coffee. What it can’t do is decide which one matters.
It doesn’t know your users, your tradeoffs, your constraints, or your long-term bets — unless you do. What does change is the iteration loop. You can test instincts faster, prototype in hours, and validate “is this even useful?” before sinking a week into it.
The skill isn’t just knowing what to build. It’s using cheap iteration to find out what to build.
2. Taste
Taste sounds subjective, but in practice it shows up in very concrete ways: choosing the simplest shape that will survive, knowing when abstraction is premature, resisting clever solutions, and noticing when a series of small changes is quietly turning the codebase into mush.
AI can generate beautiful-looking code, and it’s surprisingly good at local neatness. But it’s also capable of producing a codebase that feels like it was assembled by a committee of helpful strangers — everything works, nothing feels intentional.
Generation is cheap. Choosing is the work. Taste becomes curation.
3. Clarity
The difference between “AI helped me” and “AI harmed me” is often a single sentence:
“Refactor this orders API” versus “Simplify the orders API by collapsing status, state, and phase into a single status field with a documented enum. Maintain backward compatibility for two releases: keep old fields as deprecated aliases (read and write), add deprecation warnings, and provide a migration guide. Update server and client SDKs, add tests covering both old and new shapes, and include a rollout plan with metrics to track usage of deprecated fields.”
This isn’t verbosity. It’s clarity. Constraints are clarity made executable.
You can’t just request a change anymore — you have to frame it. And that framing is a skill: half technical, half communication, with an extra half of paranoia earned from experience.
To do this well, you need to understand your system deeply enough to articulate what matters — the boundaries, the brittle points, and the things that must not break.
4. Verification
AI can generate code fast. It can also generate wrong code just as fast.
The cost of generation went down; the cost of correctness didn’t automatically follow. So verification becomes the superpower: tight feedback loops, tests that resemble real workflows, documented business logic, and review processes optimized for understanding product changes rather than scanning diffs.
5. Systems thinking
AI is strong at local changes and weak at global consequences. And by that, I don’t mean navigating a large codebase — I mean accounting for the unknowns.
Real engineering lives in consequences: what happens under load, how changes ripple across deployments, whether a new dependency quietly becomes something everyone has to operate, and how today’s decisions shape the team’s ability to move later.
What changes is the speed of change. When you can move quickly, second-order effects compound quickly too. A bad abstraction introduced on Monday can be load-bearing by Friday.
How judgment compounds
All of these bottlenecks live in humans — but whether they actually compound depends on the systems they operate in.
Once AI enters the loop, the question stops being “can we generate code faster?” and becomes: how much of our judgment can we safely turn into automation?
In other words, the challenge isn’t replacing fundamentals — it’s deciding how much of them to make explicit, and how much to let the system carry for you.
This is where codebases — and systems more broadly — start to matter. A codebase isn’t just a pile of instructions. It’s where decisions accumulate: constraints discovered the hard way, tradeoffs that turned out to matter, invariants that must not break.
When those things stay implicit, every AI-assisted change is risky. When they’re encoded, automation starts to compound.
The same principle applies beyond code. Products, workflows, and knowledge bases either absorb automation or fight it. Clear ownership, explicit contracts, and documented reasoning make it possible for agents — and humans — to act without constantly re-deriving context.
In systems like that, automation doesn’t replace judgment. It amplifies it.
Where this is going
Some people will treat this era as a tooling upgrade: the same work, just faster. Others will dismiss it as hype. I think both miss the underlying shift.
What feels real to me is structural. The skill stack changed, the economics changed, and the leverage of individuals and teams changed with it. Writing code is no longer the primary bottleneck, so the work moves upward toward product sense, taste, and constraints — and downward toward verification and systems thinking.
Slapping “AI” onto everything isn’t the answer. But the shift inside software is real enough that it’s hard to imagine it not spilling into every part of our digital work over time.
How do you evaluate an engineering candidate if every task you can give them is a few prompts away? How do you structure systems so AI agents actually work with them? How do you orchestrate coding agents when the bottleneck becomes your ability to context-switch?
I’m going to write in public and watch this evolve first-hand. If that’s your world too, follow along.

