All posts

Engineering

The New Software Engineer: What Remains After AI Writes the CRUD

A model can now write a CRUD API faster than I can describe the one I want. I type a sentence about a resource, its fields, its validations, and a few seconds later there is a controller, a model, a migration, a serializer, and a passable set of unit tests. The first time it happened cleanly I felt a small drop in my stomach, the kind you feel when a thing you are quietly proud of turns out to be cheap.

That drop was honest, and then it was wrong. Honest, because a real shift is happening and pretending otherwise is a way of getting blindsided. Wrong, because it assumed the code was the job. It never was. I have shipped a fair amount of software, and almost none of the parts that mattered were the parts where I was typing.

This is not a reassurance piece, and it is not a panic piece. I want to be precise about what is changing and what holds value once the cheap thing is everywhere. My claim is narrow and, I think, defensible: the cost of producing code is collapsing toward zero, and as it does, the scarce resource is no longer code: it is judgment.

Coding and engineering were never the same job

We use one word, “developer,” for two different activities, and the conflation is about to get expensive.

Coding is turning a known requirement into working code. You have a spec, a ticket, or a sentence from a designer, and you produce the implementation. It is local optimization: make this function correct, make this endpoint fast, make this test pass. For a long time it was the visible, billable, interview-able core of the job.

Engineering is the larger thing that coding sits inside. It is figuring out which problem is worth solving, discovering the requirement that nobody wrote down, choosing the shape of the system, and deciding what to trade away. It is global optimization across constraints that are partly technical, partly economic, and partly human. The requirement is rarely handed to you intact; most of the work is finding out what it actually is.

AI is automating coding far faster than it is automating engineering. The boilerplate, the ORM models, the validations, the migrations, the docs, the obvious unit tests: these were always the parts closest to mechanical, and they are exactly the parts a model does well and cheaply now. What it does not do is decide whether the feature should exist, or notice that the requirement you were handed will quietly corrupt data on the worst day of the quarter.

So the interesting question is not “what can AI write?” It is: once writing is free, what is still hard? Here is where I think the honest line sits, with the systems I have built as the evidence.

Requirements discovery is still the hardest problem in the building

The hardest bug I ever shipped was not a bug in code. It was a feature that did exactly what the ticket said and was still wrong, because the person who wrote the ticket was describing a workaround for a problem they had not named.

This is the part AI is furthest from owning right now, and it is the part that has always separated good engineers from fast ones. Requirements do not arrive clean. Stakeholders disagree with each other and sometimes with themselves, and the goal stated in the meeting is not the goal that pays the bills. Half of real requirements gathering is sitting with the discomfort of a fuzzy ask and asking what are we actually trying to make true, and for whom, and why now, and then noticing when the answer contradicts the request.

A model can draft an implementation for any requirement you state crisply. The expensive skill is the conversation before there is anything crisp to state: pulling the real constraint out of three people who each see a different corner of it, and knowing the organization well enough to tell which stated goal is load-bearing and which is decoration. That work is made of context the model cannot get from the repository.

Systems thinking is owning the trade-off, not listing the options

Ask a capable model whether to reach for Postgres or Kafka, whether to split the service or keep the monolith. You will get a competent, balanced survey, the kind that used to take an afternoon of reading. And then you still have to decide, because the answer to every one of those questions is “it depends,” and the model does not hold the things it depends on.

When I built ErrSight, the central decision was not how to write any one component. It was the shape of the whole: a Rails monolith for the product surface, and a separate Rust ingestion sidecar (Axum, Tokio, SQLx) for the hot path where exceptions come in by the thousand and latency is not negotiable. The Rails write path was correct and I was fond of it, and it still topped out around 2,000 to 2,500 events a second because the bottleneck was the number of times Ruby talked to Postgres. That split is a trade-off, not a fact: it buys throughput and pays for it in operational complexity, two languages to keep in your head, and a service boundary that has to be designed rather than assumed.

And the bugs that actually hurt did not live inside the Rails or inside the Rust. They lived in the seams between four cooperating programs (the monolith, the sidecar, the SSE service, the SDKs), at the points where one program’s assumption met another program’s reality. A model can write either side of a seam beautifully. Knowing the seam is where the danger lives, and architecting so it stays small and legible, is a judgment that comes from having been burned at one before. The model proposes options; the engineer owns the trade-off, because the trade-off is made of context the model is not standing inside.

When a material gets cheap, value moves to what stays scarce

Cheap steel did not make bridges worthless; it made the judgment about where to put the steel the whole game. Cheap code does the same thing. When a material gets cheap, value migrates to whatever stays scarce around it. The unexpected gift of going back to formally study computer science has been the economics, not the algorithms: economics is the study of how value behaves under scarcity, and it names exactly what is happening to us. The scarce inputs now are the ones a model cannot supply on its own: context about the business, a model of who is harmed when the system fails, the willingness to own the outcome rather than the artifact. The hours stopped being the bottleneck; the judgment about which hours are worth spending became it.

You decide reliability on a calm afternoon, not at 3 a.m.

Most outages I have seen were not coding failures. The code was correct in the sense that it did what it said. The failure was at the level of the system: the payment provider timed out and the retry logic stampeded it, a region went away and nothing was designed to notice, the queue everyone assumed was always there was gone for ninety seconds. These are questions of what happens when a dependency you trusted stops being trustworthy, and they are answered in architecture, long before the incident.

The clearest example from my own work is a pricing decision that was really a reliability decision in disguise. ErrSight has a hard quota ceiling and charges no overages, ever. The naive way to enforce a quota is to count after the fact and bill the difference, which is exactly the design that hands a customer a surprise invoice on the day their service is already on fire, emitting errors by the million. So instead the ceiling is enforced in the hot path with a Postgres advisory lock, and TimescaleDB compression keeps the unit economics honest enough that a hard ceiling is affordable to offer.

None of that is a thing you ask a model to “write.” It is a stance: design for the worst day, decide who absorbs the cost when a system misbehaves, and make the decision while it is cheap to make, on a calm afternoon, not at 3 a.m. with a dashboard the color of a stop sign.

A model will write you insecure code without blinking

A model will cheerfully write you insecure code. Not out of malice. It is completing the pattern you asked for, and “make this work” and “make this safe against someone who wants to hurt you” are different requests. It will hardcode a secret, wave an authorization check through, trust an input it should never trust, and the code will pass every test you wrote, because you wrote the tests as the honest user and the attacker did not.

Adversarial thinking is a posture before it is a checklist: the reflex to look at any new surface and ask how would I abuse this if I wanted to. When I worked on sharing authentication across the Rails and Rust boundary in ErrSight, the whole problem was a security problem dressed as a plumbing problem: two programs, in two languages, that have to agree on who a request belongs to, with a boundary between them that an attacker would love to slip through. Getting that right was not about emitting more code. It was about distrusting the boundary itself, and assuming someone, someday, would knock on it.

This is, of the things on my list, the one I am least willing to claim AI “cannot” do, because tooling here is improving quickly. But owning the question (being the person whose job it is to assume bad faith) is human work right now, and I do not see it leaving soon.

Product thinking is owning the outcome instead of the artifact

There is a question that sits above all the others, and a model has no stake in it: should this exist at all? Is the problem real, will anyone adopt the thing, are we solving the right problem or an easier adjacent one? The model is not on the hook if the feature ships clean and nobody wants it. Humans are.

The no-overage pricing decision was a product decision before it was an engineering one. The advisory lock and the compression existed to serve a product belief: that a developer tool which can hand you a punishing bill on your worst day has misaligned itself against its own users, and that a predictable ceiling is worth real complexity to protect. No model handed me that belief. It came from being annoyed, as a user, by tools that did the opposite.

What gains value, and what quietly loses it

If I sort the skills of this job by how the next few years will treat them, a clean line appears. Some skills get more scarce as code gets abundant:

  • Systems design. Scalability, reliability, performance, the architecture of how parts cooperate and fail. This is the highest-return thing an engineer can be good at, and it is the thing AI helps with but does not own.
  • Communication. Stakeholder management, requirements gathering, and plain clear writing. The bottleneck is moving upstream into the fuzzy human part, and the people who can do conversation and synthesis there become more valuable, not less.
  • Economics. Opportunity cost, incentives, return on investment, how markets and pricing actually behave. The reasoning that makes a no-overage ceiling a sound business decision rather than a generous one is economic reasoning, not coding.
  • Domain knowledge. Finance, healthcare, logistics, security. A model knows general software extraordinarily well, but not your industry’s specific, unwritten, consequential rules. The engineer who deeply understands one domain becomes the bridge between what is technically possible and what the field actually needs.

And some are quietly losing their durable value, which is harder to say out loud:

  • Memorizing syntax and framework trivia. What you used to keep in your head is now a question you ask in plain language. Knowing the exact flag is worth less; knowing what you are trying to accomplish is worth more.
  • Hand-writing boilerplate. This was never the valuable part. It only felt valuable because it took time, and time felt like work.
  • LeetCode-grinding as an end in itself. The ability to reason about a problem still matters enormously, but the ritual of memorizing a hundred puzzle patterns to perform under interview lights ages poorly when the puzzle is the cheapest thing in the room.

None of these “losing” skills are useless, and some are foundations you genuinely need before you can have judgment about the layers above them. The point is narrower: they are losing their durable, differentiating value. They are becoming table stakes, or becoming free.

A day in the work, a few years out

The job of 2030 does not look like a person typing all day, and it does not look like a person idle while machines work. It looks like a shift in where the hours go.

The good version of this engineer uses AI aggressively and is not embarrassed about it. The mental model I keep is a pilot and an autopilot. A good pilot does not refuse the autopilot to prove they can fly by hand; they use it constantly, and are paid for the judgment of knowing when to trust it and when to take the controls back for the situation the automation was never going to handle. They manage the system instead of racing it, and they keep score by whether the business problem is gone, not by whether their code compiles. “My code works” is the artifact. “The problem is gone” is the outcome. The artifact is becoming cheap. The outcome never was.

So more of the day is spent in the fuzzy front of the work: pulling a real requirement out of a vague one, making an architecture call, deciding what to build and what to refuse to build. A meaningful slice is spent reviewing implementations the model produced, reading them the way you read a junior’s pull request, with the specific suspicion of someone who knows where seams hide. Time goes to reading metrics and deciding what they mean, to directing a fleet of agents at well-scoped tasks, to the reliability and security questions that do not have a clean prompt. And yes, some of the day is still hand-writing code: the gnarly, novel, context-soaked part where it is faster to express the idea than to explain it. But it is a slice, not the whole. That day is not a smaller job. It is a denser one.

What is actually scarce now

I had that small drop in my stomach because for a moment I believed the code was the value, and watched the value get cheap. But the code was always the cheapest honest part of the work. The expensive parts never sat on the keyboard: figuring out what to build, deciding how it should be shaped, designing for the day it breaks, assuming someone will try to abuse it, owning whether it was worth doing at all.

Software engineering is not disappearing. Coding is becoming abundant, the way a once-scarce material becomes abundant and the craft moves to what you do with it. As the typing gets cheap, judgment becomes the thing in short supply: the judgment to understand systems, to understand the business, to understand the people the software is for, and to choose well among trade-offs that have no clean answer. The engineers who thrive will not be the fastest typists in the room. They will be the ones who understand all of that better than anyone else, and that is a more interesting job to be left with than the one I thought I had.

Share this post
Written by Jijo Bose All posts