The Void That Comes With AI-Assisted Programming
I shipped four features last week. Four. In the before-times, that would have been a good month, maybe a good quarter depending on how ambitious they were. I reviewed pull requests, steered an AI through a gnarly authentication flow, refactored a data layer that had been rotting for a year, and wrote what might be the cleanest set of integration tests I’ve ever produced.
And I closed my laptop on Friday feeling like absolute garbage.
Not tired-garbage. Not burnt-out-garbage. Something stranger. Something closer to hollow. I sat there trying to name it and the closest I could get was this: I had accomplished more than I usually do, and I felt like I had done less than I usually do. Those two things aren’t supposed to be true at the same time. But there they were, sitting next to each other, refusing to cancel out.
I’ve been chewing on that feeling for months now, and I think I finally understand what it is. It’s not about AI making me lazy. It’s not about AI replacing programmers, or some looming employment apocalypse, or any of the discourse you’ve already read forty versions of. It’s smaller and weirder than that, and it’s taken me a long time to admit it out loud because it sounds almost embarrassing to say:
AI didn’t just take my job. It took my struggle. And it turns out the struggle was a lot of why I loved this job in the first place.
The old loop
For most of my career, building something followed a shape that never really changed. You wanted a thing. You didn’t know how to build the thing. You went and researched. You tried something. It broke. You debugged it, which is really just a fancy word for staring at your own stupidity until it starts making sense. You tried again. Eventually, usually much later than you expected, it worked.
That loop was miserable in the moment and I wouldn’t trade it for anything.
Because somewhere inside all that misery, you were building something else, something that never showed up in a commit history: you were building yourself. You read the docs so many times you started dreaming in method signatures. You made an idiotic mistake, felt the specific shame of it, and never made that mistake again. You developed a kind of intuition that has no name and can’t be taught directly — the sense, almost physical, that something is going to break in exactly this spot, before you’ve even run the code.
And then, at the end of it, when the thing finally worked, there was a feeling. You know the feeling. It’s not relief, exactly, though relief is part of it. It’s something closer to: holy shit. I built this. Not “this exists now.” Not “this ships now.” *I* built this. My hands, my mistakes, my three hours of confusion, my one good idea that unstuck everything. Mine.
AI changes the shape of that loop entirely. Now it can look like this: you want a thing, you ask, you get an implementation, you review it, you steer it a little, you ship it. The frustrating middle — the part that used to take up ninety percent of the time and, not coincidentally, ninety percent of the learning — has been quietly removed. And nobody warned me that the middle was where I lived.
Building, understanding, and mastery are not the same thing
I think a lot of the confusion around this topic — mine included — comes from treating “programming” as one activity when it’s actually at least three, and AI is not equally good at all of them.
There’s building: can I make this thing exist? This is the one AI has essentially solved. You can describe a mobile app in a paragraph and get something that runs, that has a database, that authenticates users, that looks halfway decent, without you knowing the framework or the language underneath it at all. The distance between “I have an idea” and “I have working software” has collapsed to almost nothing. This is not an exaggeration. This is just what’s true now.
Then there’s understanding: do I actually know how this thing works? This is where it gets uncomfortable. You can ship a Flutter app while knowing almost no Dart. You can wire up Redis without understanding a single one of its tradeoffs. You can implement authentication — sessions, cookies, tokens, OAuth, CSRF protection, the whole miserable rabbit hole — without ever going down the rabbit hole, because something else went down it for you and came back with a working answer. The software runs. The knowledge, the actual mental model of why it runs, doesn’t automatically come along for the ride anymore. It used to be a byproduct of building. Now it’s an entirely separate purchase, and you have to choose to buy it.
And then there’s mastery: could I reason about this system, break it apart, fix it when it breaks in production at 2 a.m., make good architectural calls about it, or build something like it again without the machine holding my hand the entire time? Mastery has not gotten cheaper. It still costs what it always cost, which is time and deliberate friction. And that’s the conflict nobody wants to say plainly: AI is optimized for speed, and mastery is optimized for struggle. Those are not just different goals. They actively compete with each other for your time.
You can spend an evening either shipping five things fast or understanding one thing deeply. AI makes the first option so easy and so rewarding in the moment that the second option starts to feel almost irrational. Why spend three hours actually understanding how a refresh token rotation strategy works when the model can hand you a working one in thirty seconds?
That is a completely reasonable question if your only goal is shipping. It’s a genuinely bad question if your goal is ever becoming the kind of engineer who can be trusted with hard, ambiguous, high-stakes problems. And most of us are trying to be both, at the same time, without ever having consciously decided the tradeoff between them.
The thing that keeps bothering me
Here’s a concrete version of the thing that’s been nagging at me. I recently worked on a desktop application purely coded in Swift. Not a toy, an actual product with thousands of users. And guess what, i know not a single thing about Swift. Nothing at all, except that its used for macOS/iOS apps.
That is genuinely amazing. Five years ago that sentence would have been science fiction. The barrier between “I have an idea for an app” and “I have an app” has essentially been demolished for a huge number of people, and that is an enormous, wonderful, democratizing thing that I don’t want to undersell.
It’s also a little terrifying, and I want to be careful about how I say this, because I don’t mean it as a gatekeeping thing. I’m not saying people who build this way aren’t “real” programmers — that’s a lazy, mean, and honestly kind of cowardly argument, usually made by people trying to protect their own sense of specialness. That’s not what’s bothering me.
What’s bothering me is a much more personal, much more specific question: if the machine can carry most of the implementation weight, what exactly am *I* supposed to be learning while it does that? What is my job, in the moment-to-moment sense, when I’m not the one figuring out why the widget tree isn’t rebuilding? Is my job just… vibes and review? Sometimes it feels like it, and I don’t fully know yet whether that’s a demotion or a promotion.
Missing the craft, not the outcome
I need to say something plainly, because I think it’s the emotional core of all of this and I’ve been dancing around it: I genuinely, actually like programming. Not just the result of programming. The act of it.
I like typing code. I like the specific quiet of thinking through a hard problem with nothing but a terminal and too much coffee. I like debugging, even though I complain about it constantly, because there is a very particular satisfaction in the moment a bug that has tormented you for two hours suddenly makes complete sense, and you feel briefly, stupidly brilliant. I like getting stuck. I like the slow click of an abstraction finally making sense after the third time you’ve read about it. I like putting on headphones and disappearing into a problem for six or eight or ten hours and coming up for air not knowing what time it is.
None of that was ever just a tax I paid to get to the outcome. It was the point, at least as much as the outcome was. And that’s the thing AI quietly takes away without ever announcing that it’s taking it: it gives you the destination while letting you skip the road, and it turns out I liked the road.
So now I’m sitting with this genuinely strange contradiction: I can accomplish more than I ever could before, while experiencing less of the actual thing I loved accomplishing it through. Productivity went up. Some other, harder-to-name resource went down. Nobody put that second number on a dashboard, so for a long time I didn’t even notice it was dropping.
The ten-times productivity paradox
Let me put two versions of the same project side by side, because I think the comparison says more than I can say directly.
Before: I spend two weeks building something. By the end, I know the codebase the way you know a place you’ve lived in for years — not because I memorized it, but because I bumped into every wall in it. I remember the bugs by name, practically. I understand the architecture because I’m the one who argued myself into and out of three different versions of it. I know why the weird decision on line 400 exists, because I made it, at 1 a.m., for a reason that made sense at the time and still kind of does. I struggled with it. I learned actual, transferable things along the way that I’ll use again on some completely different project two years from now. And when it’s finally done: holy shit. I built this.
Now: I spend two days building something. AI writes a almost the whole share of it. I review (an agentic loop here aswell lol), pick up my phone, watch reels. And I ship. The result is, by almost every objective measure, better. More features. More polish. Fewer obvious bugs, honestly, because the model doesn’t get tired at hour six the way I do. I shipped it roughly five times faster.
And I close the laptop thinking: …did I actually build that?
That sentence, sitting quietly at the end of an objectively excellent day of work, is the whole essay. Everything else I’m writing here is just me trying to explain why that sentence exists at all.
We’re measuring the wrong things, and I include myself in “we”
Part of why this sneaks up on people is that we’ve built an entire professional culture around measuring the outputs of programming — commits, PRs, tickets closed, features shipped, velocity, lines of code, release cadence — and basically none of that measures learning. It never really did, but it used to correlate with learning almost by accident, because the only way to produce a lot of output was to go through the friction, and the friction was where the learning lived.
That accidental correlation is breaking. You can now produce an enormous amount of software while barely deepening your understanding of any of the technologies you’re using to produce it. I want to be careful here, because I don’t think this is universally true, and I don’t think it’s true evenly across every developer or every project. Plenty of people are using AI in ways that actively accelerate their learning, by asking it to explain things, by treating it like an infinitely patient tutor instead of an infinitely fast typist. This isn’t a law of nature. But it is a real possibility that the tool makes easy by default, and defaults matter enormously, because most of us, most of the time, take the path the tool nudges us toward without noticing we’re being nudged.
The uncomfortable version of the idea, stated as plainly as I can manage: we might be getting better at producing software while getting less connected to the actual craft of programming. Not because we’re worse people or lazier engineers. Because the incentive structure quietly shifted underneath us, and almost nobody sent out a memo.
The authentication rabbit hole, revisited
Take authentication, because everyone who’s ever built a real product has been through this particular rabbit hole. Historically, wanting login functionality dragged you through HTTP, then cookies, then sessions, then JWTs, then OAuth, then CSRF protection, then password hashing, then middleware, then authorization layers, then refresh token rotation, then all the edge cases nobody warns you about until you hit them in production. It was tedious. It was, in its own grinding way, one of the best educations a backend developer can get, because auth touches almost everything else in a system.
Now you can type: “implement secure authentication with Google OAuth and session-based login,” and the feature simply appears, mostly correct, often genuinely well done. This is fantastic for shipping. I’m not being sarcastic — it is fantastic, unambiguously, if your goal that day is a working login flow.
But if you never stop to actually understand what just happened — if you never crack open what got generated and ask why it works this way and not some other way — you’ve quietly traded an educational rite of passage for a productive afternoon. And here’s the part I keep coming back to: not every feature needs to teach you something. That’s fine. That’s healthy, even. But if every feature gets delegated this way, eventually you have to ask, honestly, what you’re actually learning anymore, and how you’ll know when you’ve stopped.
Code stopped being the scarce resource
I think the deeper shift here isn’t really about programming disappearing. It’s about typing code becoming less important while engineering judgment becomes more important, and those are very different claims.
For basically the entire history of software, writing code was expensive. That expense quietly organized everything else. A good idea had to survive a slow, costly implementation process before you found out whether it was actually good, and a huge amount of professional skill was built around making that expensive process go smoothly.
If code generation becomes cheap — and it has, dramatically, in a way that still doesn’t feel fully real to me — the bottleneck moves. It doesn’t disappear, it just relocates, to things that were always harder to teach and harder to fake: knowing what’s actually worth building. Knowing why. Knowing whether an implementation is merely functional or genuinely good, which are not the same thing at all. Knowing what not to build, which might be the most underrated skill in the entire industry. Architecture. Product sense. Debugging instinct. Systems thinking. Taste.
AI can hand you fifty different implementations of the same feature before lunch. It cannot reliably tell you which one should exist, and it definitely can’t tell you whether the feature should exist at all. That question — should this exist — is stubbornly, permanently human, or at least it feels that way to me right now, and I suspect it’ll stay that way longer than a lot of the more panicked takes suggest.
Maybe I’ve just been using the tool wrong
Here’s where I want to be careful, because it would be very easy to end this essay by declaring AI the villain, and I don’t believe that, and I dond’t think you should believe it either if someone tries to sell it to you that cleanly. I’m not going to pretend hand-written code is morally superior. I’m not going to pretend the old, slow way was always better — a lot of that slowness was just friction, not virtue, and I don’t miss most of it even a little.
The more honest, more personal realization is this: I think I’ve been using a tool that’s optimized for speed as if it were also optimized for growth, and then feeling confused and a little cheated when it didn’t deliver growth as a side effect. That’s not really the tool’s fault. That’s a workflow problem. AI is extraordinary leverage when the goal is shipping. It is not automatically going to hand you mastery, because mastery was never something you could buy at scale — it always required friction, and friction is precisely the thing this tool is built to remove.
Sometimes the goal really is shipping, and I should use AI aggressively and without guilt in those moments. But sometimes the goal is learning, or mastery, or — and I think this one gets dismissed way too fast — just enjoying the act of programming for its own sake. Those goals need a different workflow, deliberately chosen, not stumbled into.
Where this leaves me
The bigger question underneath all of this, the one I don’t think I can answer cleanly, is something like: if AI can strip out most of the friction from building software, what does it actually mean to be a programmer anymore? And the smaller, more honest version of that question, the one that actually keeps me up: if I don’t have to struggle to build things anymore, how do I hold onto the parts of this work I genuinely loved?
I don’t have a tidy answer. I’m working this out in real time, probably slower than I’d like, and probably getting parts of it wrong. But I know what I don’t want.
I don’t want to go back to a world where every feature takes two weeks by default. I don’t want to hand-write every CRUD endpoint again out of some misplaced sense of purity. I don’t want to spend three days manually configuring something a model can set up correctly in twenty minutes. I don’t want to give back the leverage — it’s real, and it’s genuinely good, and pretending otherwise would just be nostalgia wearing a principled costume.
But I also don’t want to forget why I fell in love with this work in the first place. The goal was never to reject the tool. The goal is figuring out how to use it without quietly losing the parts of programming that made programming worth doing before any of this existed.
AI gave me the productivity I always said I wanted. And in the process, almost as a side effect, it forced me to actually confront what I loved about this job to begin with — which, it turns out, was never really the shipping.
I don’t want to stop using AI. I just don’t want to stop being a programmer.