AI Makes Code Cheap, But Engineering Judgment Expensive

A new engineer joined our team recently. Smart, capable, armed with AI agents that could scaffold an entire service in an afternoon. Within the first week, they opened a pull request with a note: “Why is this service structured like this? I asked Claude and it suggested a cleaner approach.”

They were not wrong. The suggested architecture was cleaner, more idiomatic, easier to test. If we were building it today, we would probably do it that way.

But we did not build it today. We built it two years ago, with a different team, different constraints, different tools. And this is just how software works. Every codebase is a fossil record — layers of decisions made under conditions that no longer exist. Constraints that have been lifted. Team compositions that have changed. Tools that have been replaced.

The interesting thing is not that old code looks outdated. Of course it does. The interesting thing is that it works. It shipped. It solved the problem it needed to solve at the time. And now someone with better tools looks at it and wonders why anyone would build it that way — the same way we will look at today’s AI-assisted code in five years and wonder the same thing.

This is the nature of the industry. Every generation of engineers inherits decisions they would not have made, made by people who did not have their tools. And every generation leaves behind decisions the next one will question. It is not a bug. It is how software evolves.

So in an industry where today’s best practice is tomorrow’s tech debt — what actually matters? What survives?

Architecture Is About People

Early in my career, I thought architecture meant drawing boxes and arrows. Pick the right pattern — microservices, event-driven, hexagonal — and the system would be good. I was wrong.

The best architecture I have ever worked with was not technically impressive. It was a set of services that happened to match exactly how our teams were organized. Each team owned one or two services, had full control over their deploy pipeline, and rarely needed to coordinate with other teams for routine work. New features shipped fast. On-call was manageable. People were happy.

The worst architecture I have worked with was technically beautiful. Clean separation of concerns, elegant abstractions, thoughtful use of design patterns. But it was built by one team and handed to three. No one understood the boundaries. Every feature required changes in four services. Deploy coordination became a full-time job.

Conway’s Law is not just an observation — it is a warning. Your system will eventually mirror your organization, whether you design it that way or not. You can fight this, and you will lose. Or you can embrace it and let your team structure inform your service boundaries.

I learned this the hard way in the early days of a startup. Small budget. A team of mostly junior engineers, myself included. No luxury of senior developers who could just “do the right thing” by instinct.

The conventional wisdom would say: keep it simple, use REST, figure out contracts later. But I knew what would happen. With junior engineers on both sides, the frontend and backend would slowly drift apart. Someone would rename a field and forget to tell the other side. Someone would assume a response shape that never existed. We would spend half our time debugging integration issues instead of building features.

So I made a bet that felt heavy at the time: ConnectRPC with Protocol Buffers, end to end. One shared schema that generated types for both frontend and backend. Strict contracts enforced by the compiler, not by code review. If you changed the API shape, both sides knew immediately — not three days later when QA found a broken page.

It was more setup than a simple REST API. The team had to learn protobuf, understand code generation, get comfortable with a less familiar toolchain. Some people questioned whether it was overkill.

But here is what happened: junior engineers who would have spent hours debugging mismatched JSON fields were instead caught by the compiler in seconds. API changes became mechanical — update the proto, regenerate, fix the type errors, done. Code reviews could focus on business logic instead of “did you match the response schema?” The strict framework did the job that senior engineers would have done through experience and discipline.

A team of seniors might not need this. They have the habits, the instincts, the muscle memory to keep things consistent without strict tooling. But I did not have a team of seniors. I had the team I had, and the architecture needed to work for them — not for some ideal team that did not exist.

This reframing changed how I think about scalability. When someone says “this system needs to scale,” I used to think about traffic, throughput, database sharding. Now my first question is: how many people need to work on this simultaneously without stepping on each other?

Traffic scaling is largely a solved problem. Throw money at your cloud provider. But team scaling — enabling ten developers to be as productive as they were when there were three — that is an architecture problem. And it is the one that actually determines whether your product succeeds or fails.

Two-Way Doors

Jeff Bezos has this concept of one-way doors and two-way doors. One-way doors are decisions that are nearly impossible to reverse. Two-way doors are decisions you can walk back through if they do not work out.

Most technical decisions are two-way doors. Your choice of programming language, web framework, state management library, CI/CD tool, even your cloud provider — these feel permanent but they are not. Swapping them is expensive, sure. It takes effort and time. But it is doable. Companies do it all the time.

The real one-way doors in software are fewer than you think, but they matter more than anything:

Your public API contract. Once external consumers depend on it, every change is a negotiation.

Your core data model. Migrating a live database with years of data is not just a technical problem — it is a political one.

Your service boundaries. Splitting a monolith is hard. Merging two services is harder. Doing either while shipping features is a nightmare.

Your promises to users. Once you launch a feature, someone depends on it. Removing it means breaking trust.

The problem I see in most teams is a mismatch. They treat two-way doors like one-way doors — agonizing over framework choices in week-long meetings, creating evaluation matrices for decisions that could be reversed in a sprint. Meanwhile, they rush through one-way doors — casually designing a database schema in a pull request description, or shipping a public API endpoint without thinking about how it will evolve.

The skill is not in making the right decision. It is in recognizing which type of door you are walking through. For two-way doors, decide fast, learn fast, adjust. For one-way doors, slow down, write it down, get more eyes on it.

And here is the meta-skill: design your systems so that more doors become two-way. This is what good abstraction is for. Not to make code pretty, but to make decisions reversible. Put an interface between your service and the message broker — now swapping Kafka for NATS is a two-way door. Version your API from day one — now evolving it is a two-way door. Use feature flags — now launching is a two-way door.

The goal is not to avoid making mistakes. It is to make mistakes cheap.

The Values That Compound

I have been writing software professionally for several years now. In that time, I have used more languages, frameworks, and tools than I can count. Most of them are already obsolete or will be soon.

But some things I learned early on have only become more valuable over time. These are the skills that compound — the things that get more useful the longer you practice them, regardless of what technology you are using.

Communication. The ability to explain a technical decision to someone who does not share your context. Writing a clear RFC that a new team member can understand. Giving code review feedback that teaches instead of criticizes. Saying “I do not know” in a meeting instead of hand-waving.

I used to think communication was a soft skill — nice to have, not essential. I was very wrong. The best technical decision, poorly communicated, is worse than a mediocre decision that everyone understands and buys into. Alignment beats optimization. Every time.

Abstraction thinking. Knowing when to hide complexity and when to expose it. When to build a reusable component and when to just copy-paste. When a new abstraction layer helps and when it is just another thing to maintain.

The irony is that junior developers often create too many abstractions (DRY everything!) while senior developers know that a little duplication is healthier than a bad abstraction. The skill is not in abstracting — it is in knowing the cost of each layer you add.

Trade-off reasoning. There is no “best practice” in a vacuum. There are only trade-offs that make sense in a given context. Choosing consistency over availability is not right or wrong — it depends on whether you are building a banking system or a social feed. Picking a monolith over microservices is not outdated — it depends on whether you have two developers or twenty.

The engineers I admire most are not the ones with the strongest opinions. They are the ones who can articulate the trade-offs clearly: “We are choosing X, which gives us A and B, at the cost of C. We accept this trade-off because D.” That sentence is worth more than any architectural diagram.

Empathy. Code is read more than it is written. APIs are used by people who were not in the room when you designed them. Systems are operated by on-call engineers at 3am who have never seen your code before.

Building for the person who comes after you is not altruism. It is engineering. The variable name that saves someone ten minutes of confusion. The error message that tells the operator what actually went wrong. The API response that includes enough context for the consumer to debug their own issue. These small acts of empathy compound into systems that are genuinely good to work with.

AI Raises the Stakes

Everything I have described — team-aware architecture, reversible decisions, communication, abstraction, trade-offs, empathy — has been true for decades. But AI is making these skills more important, not less.

Here is why: AI makes implementation cheap. When you can generate a working prototype in an afternoon, the bottleneck shifts. It is no longer “can we build this?” It is “should we build this?” And “should” is a question that requires all the skills above.

When everyone on the team can produce code faster, the cost of building the wrong thing goes up. Not because the code costs more, but because the opportunity cost is higher. You could have built something else in that same afternoon. The engineers who thrive are the ones who kill features before they get built — who have the judgment to say “this solves a problem nobody has” or “this creates a two-way door where we need a wall.”

AI also changes how teams work together. Code review is different when half the code was generated. You are not reviewing someone’s thought process anymore — you are reviewing output. This requires a different kind of attention. More focus on “does this actually solve the problem?” and less on “is this idiomatic?”

Pair programming with AI is not like pair programming with a person. You do not negotiate approaches. You prompt, evaluate, adjust, prompt again. The skill is in evaluation — knowing whether the output is good enough, catching the subtle bugs that look correct at first glance, recognizing when the AI confidently chose the wrong abstraction.

But the core has not changed. You still need to communicate decisions clearly. You still need to think about abstractions. You still need to reason about trade-offs. You still need empathy for the humans in the system — the users, the operators, the next developer.

If anything, AI amplifies the gap between engineers who have these skills and those who do not. When everyone has access to the same AI tools, the differentiator is not who can prompt better — it is who knows what to build, how to structure it for a team, and which decisions to spend time on.

The Compound Interest of Boring Skills

I keep coming back to that pull request from the new engineer. They were right. The code could be better. It can always be better. That is the easy part.

The hard part is knowing that every codebase you touch was someone’s best answer to a question you never had to ask. And the code you write today, with all your shiny AI tools, will look just as dated to whoever comes next. That is not a tragedy. That is the job.

The most valuable things in software engineering are boring. Communication is boring. Thinking carefully about boundaries is boring. Writing clear documentation is boring. Designing for reversibility is boring. Having empathy for the next person is boring.

But these are the things that compound. They get more valuable every year, regardless of what language you write in, what cloud you deploy to, or how much of your code an AI generates. They are the things that do not change.

And in an industry addicted to change, that might be the most valuable thing of all.

— –

Code has never been cheaper to produce. Knowing what code to write has never been more expensive.

Liked Liked