When Software Stops Needing So Many Software Engineers

In 2023, I wrote a post about pair programming with ChatGPT (Chinese).

GPT-4 could already write a meaningful amount of code. I treated ChatGPT as the driver and myself as the observer: I described the problem, watched it implement, reviewed the code, corrected it, and repeated the loop.

My conclusion at the time was straightforward. AI could write code, but it hallucinated APIs, misunderstood context, and missed edge cases often enough that human review still felt like the last line of defense. As long as a person had to understand the implementation and decide whether the code was actually correct, the software engineer still seemed firmly in the loop.

Three years later, I rarely do that anymore.

This is not because AI became so reliable that I now treat it as a better autocomplete tool. The development process itself has changed shape.

I maintain two long-running side projects: Free4Chat and MyInvestPilot. Most new code in both projects is no longer written by me. More importantly, I usually do not review that code myself either.

One agent implements a change. Another model reviews it. If the review finds problems, the implementation agent fixes them. Tests, builds, and browser checks keep running. If the reviewer says there is no blocker, I usually merge.

Sometimes I could not tell you which functions changed in the PR.

That is a very different relationship with software from the one I described in 2023.

This is not a story about “how I built two products by myself with AI.” I still work as a software engineer in my day job, in an environment much closer to conventional team software development. I simply happen to have two side projects that have been running for years, long enough to observe the same systems before and after coding agents became genuinely capable.

The question is no longer whether agents can write code. For me, it is this:

As more software-engineering work moves to agents, how many software engineers does a software project still need?

Two Non-Trivial Side Projects

It is worth describing the projects first, because the rest of this essay would not mean much if they were just two CRUD demos.

Free4Chat started as an anonymous WebRTC chat room. The first backend used Go and Pion with a self-hosted SFU. I later rewrote it with Elixir and Membrane, then moved the realtime stack to Cloudflare. Today it also has an Agent Runtime, permissions, voice, temporary tasks, Live View, and Room Apps. I wrote the longer infrastructure history in Four Evolutions of a WebRTC Chat Room.

The project is not large in the enterprise-SaaS sense. It does not have a giant account hierarchy, billing system, or dozens of business entities. But it is small and technically deep.

A good example is a feature that sounds almost trivial: let an agent join a room and speak.

That sounds like “add TTS.” In practice the path became: human audio travels through an SFU into a local Agent Runtime, Pion receives the media, speech-to-text turns it into text, an ACP harness lets the agent reason over it, the result goes through TTS and audio processing, Pion publishes a new media track, Cloudflare forwards it through the SFU, and another browser finally plays the voice.

The early Agent Runtime was itself split across two environments. Node/TypeScript owned the CLI, daemon lifecycle, MCP/ACP orchestration, room state, and speech integration. A separate Go/Pion process owned the WebRTC media plane, with the two talking over JSONL. As media lifecycle and agent lifecycle became more entangled, the cleaner answer was eventually to rewrite the runtime as a self-contained Go binary and run Pion in-process.

That did not make the hard problems disappear. For a while, the project turned into a small WebRTC 101 laboratory.

We hit bugs that only existed across the full path. Local TTS audio and a Pion encode/decode loopback could both be clean, while the same audio developed a periodic robotic distortion after traveling through Cloudflare’s SFU into a browser. Restarting Agent Voice could publish a fresh track while some browsers stayed silent until another participant triggered renegotiation. A Worker deployment once restored text connectivity while leaving the media publication dead, so the agent looked healthy but never spoke again.

Permissions added another boundary: letting an agent publish its own voice must not implicitly let it discover and listen to human audio. These failures rarely showed up in unit tests. They required a real browser, a real SFU, a real runtime, and multiple devices.

So Free4Chat is not complicated because it has a lot of code. It is complicated because a feature that looks small often crosses browsers, WebRTC, Cloudflare, a native runtime, an agent harness, authorization, and several independent lifecycles.

Over the last few months it has also grown Tasks, Live View, Room Apps, agent permissions, and a set of temporary collaboration primitives shared by humans and agents. The feature and code growth is now faster than I can follow PR by PR in my spare time.

MyInvestPilot is complex in a different direction.

It started as a portfolio and strategy tool and gradually grew into a broader system: market data, strategy execution, backtesting, schedulers, portfolio metrics, AI analysis, research workflows, email notifications, and the infrastructure needed to keep all of that running. The current system boundaries and AI architecture are documented in the public myinvestpilot/ai-architecture repository.

If Free4Chat goes deep down a realtime path, MyInvestPilot spreads wider across product, data, strategy execution, scheduling, and production infrastructure. It also has domain-correctness problems that are specific to investing.

Consider dollar-cost averaging. If external cash keeps entering a portfolio and you calculate returns directly from broker portfolio value, some of the new money can look like investment profit. The system already had a cash-flow-adjusted NAV series and used it to recompute CAGR, drawdown, and volatility. At one point Sharpe ratio simply missed that adjusted path. The raw Sharpe was close to 2. The cash-flow-corrected version was around 0.6.

There is nothing glamorous about that bug. But if the number is wrong, the product is wrong.

Infrastructure bugs can be just as subtle. Old market data could be rehydrated from object storage into Redis. If that copy refreshed Redis TTL, the TTL no longer meant “how fresh is this data?” It only meant “when did I most recently copy it back into Redis?”

The first fix exposed more edges: provider outages could amplify retries, a cooldown claimed too late could not stop concurrent callers, and an older object-store value could race with newer Redis data. A “stale cache” bug turned into a problem about source freshness, retry amplification, cooldown ownership, recovery, and precedence across cache layers.

One project goes deep. The other goes broad.

Neither represents the entire software industry. But they are complex enough that dismissing them as vibe-coded demos would miss the point.

And that is why what happened next started to bother me.

Leaving the Implementation Layer

For most of my career, I could tell a lot about an engineer by reading their code.

Code exposes decisions that are hard to fake: where boundaries are drawn, how failures are handled, where state lives, how races are prevented, when to abstract and when not to, whether a bug is patched locally or traced back to the wrong ownership model.

Code is where an engineer leaves a trail of thought.

Now, if you opened a random module from the last month of Free4Chat and asked me exactly how it worked, there is a good chance I could not answer.

MyInvestPilot is similar.

I still know what these systems are for, why the major boundaries exist, and what I do not want them to become. I know why Free4Chat needs to stay temporary and anonymous-first rather than slowly turning into another permanent workspace. I know why MyInvestPilot favors transparent, rule-based strategies and portfolio-level decisions instead of becoming a mysterious AI stock picker.

But I no longer know many implementation details.

That is not because I left the projects. I am still deciding what they should do next.

What changed is that I am leaving the implementation layer.

A typical workflow now looks roughly like this: I discuss a problem with ChatGPT, settle the product direction and engineering constraints, then hand the task to Codex, Claude, DeepSeek, GLM, or another coding agent. The agent reads the repository and issues, implements the change, runs tests, and fixes CI. When it is done, I ask a different model to review the work.

If the review finds a problem, that feedback goes back to the implementation agent. It fixes the issue and the code is reviewed again. The loop continues until the reviewer sees no blocker.

The important part is that I often do not perform a line-by-line review anywhere in that loop.

In 2023 I thought human review was the final boundary AI coding could not cross. Today that boundary is itself increasingly handled by another agent.

In a loose sense, I have moved from:

design → write code → test → review

to:

decide what problem is worth solving → decide whether the result is acceptable

Even the mechanical job of copying review feedback from one system into another is disappearing. A computer-use agent can operate a browser, access systems where I am already logged in, open ChatGPT, collect a review, and feed the result into the next development loop.

I used to joke that software engineers might eventually become “copy-paste engineers.”

It now looks possible to remove the copy-paste engineer too.

Agent Review Does Not Make Software Correct

There is an obvious misunderstanding here: if I no longer review most code, does that mean agents have become almost perfectly reliable?

No.

Free4Chat once had a PR with green CI, merged cleanly, and then the homepage simply did not work.

Real multi-device testing has uncovered plenty that automation missed: browsers becoming sluggish as a room stayed open, attachments failing on some devices, some agents missing @mentions, screen sharing looking different on phones and desktops, a large video attachment arriving on one machine but not another.

WebRTC has a particular talent for humiliating anyone who believes “all tests passed” means the system works.

So my lack of manual review does not mean the risk disappeared.

The risk structure changed.

My old mental model was:

AI writes; a human makes sure it is correct.

The current model is closer to:

an agent writes; another agent reviews; tests and production expose failures; agents repair them; the human accepts that this loop is not mathematically reliable and steps in where the consequences justify it.

Agent review does not create correctness.

It simply automates review too.

This is also why I do not think engineering experience has stopped mattering. It still matters. It just shows up less often as code I personally wrote, and more often as knowing where I should not trust an apparently clean result.

Even Finding the Problem Is Moving to Agents

There is another comforting interpretation: perhaps I am still the real senior engineer. I identify the problem, define the root cause, and hand a well-specified task to cheap execution agents.

If that were true, the change would be much smaller. It would just mean a senior engineer had acquired a large pool of inexpensive implementers.

But that is increasingly not what happens either.

The Sharpe-ratio issue above was not something I discovered by manually auditing the formula. Many cache, concurrency, and security problems were not problems I first defined and then asked an agent to fix. They surfaced through agent reviews, automated tests, log analysis, or systematic investigation after a production symptom.

MyInvestPilot has had production jobs fail and alert in Slack. In the past, that meant I would log into servers, inspect logs, find the job, check Redis and queues, compare recent deployments, and slowly reconstruct the failure chain.

Now I often hand the symptom to an agent.

It checks logs and deployment state, looks at recent changes, inspects the relevant caches and queues, and eventually gives me something close to an incident report: what happened, which layer failed, whether the failure was transient, whether data was damaged, whether a fix is needed, and what that fix should look like.

Sometimes my entire understanding of an incident comes from reading that report at the end.

That affects me more than “AI can write code.”

Writing code was always one of the most mechanically automatable parts of software development. But when investigation, code review, testing, and production diagnosis also become agent work, the question changes.

It is no longer just:

Will programmers still need to write code?

It becomes:

How many stages of software production still require continuous participation from a software engineer?

What I Still Look At Personally

None of this means I stopped caring.

If anything, I now pay closer attention to a smaller set of decisions.

The first is product direction.

Free4Chat could easily keep expanding: permanent workspaces, an agent platform, accounts, centralized memory, project management, a plugin marketplace, remote desktop access. An agent can write a very convincing PRD for every one of those directions.

I do not want the product to become those things.

To me, Free4Chat is valuable precisely because it is a low-cost, temporary collaboration boundary. Humans and agents can enter with their own capabilities, work together, and leave without first creating a permanent workspace.

Some of the most important product decisions are not about what to add. They are about what not to build.

Agents are very good at answering, “How do we make this requirement more complete?” The question of why a project exists, and which reasonable features would destroy its character, still depends heavily on human judgment and taste.

The second thing I watch closely is cost.

Free4Chat is free to use. That turns many architecture decisions into a brutally practical question:

If usage grows by 100x, am I willing to pay that bill?

I look at Cloudflare Durable Object duration, SFU egress, whether long-lived connections prevent hibernation, and whether a new feature quietly turns high-frequency state into storage operations.

MyInvestPilot has the same constraint in a different form. Fly.io, Cloudflare, model APIs, and market-data services eventually become numbers on a real credit card statement.

At that point I become a very conservative architect again.

An agent does not lose sleep because Cloudflare will charge its credit card next month.

I do.

The third thing is the actual human experience.

Does a voice agent sound natural? Does this button feel wrong here? Is the room annoying on a phone? Is a three-second wait fine but eight seconds unacceptable?

We can automate parts of this. Browser agents can test it. Vision models can score screenshots. But I still open the product myself.

The software is ultimately for people, and I happen to be one.

The last thing is more fundamental: responsibility.

The accounts are mine. The servers are mine. The credit card is mine. When users run into a problem, they eventually reach me.

Agents can recommend a decision and execute it. The question of which risk is worth accepting is still mine.

So I do not think humans have disappeared from software production.

A better description is that humans are disappearing from large parts of the process and becoming concentrated at a smaller number of high-consequence decision points.

Attention Is Becoming the Bottleneck

This creates a constraint I did not have before.

The scarce resource in a side project used to be time.

You might have four free hours on a weekend. You had to design, implement, debug, and test the feature yourself. Anything moderately complex could take several weekends. Scope control happened naturally—not because we were brilliant product strategists, but because we simply could not build everything.

That constraint is weakening.

Agents can implement, test, and fix things while I am at work, eating, or out doing something else. On some weekends I only need to spend half a day reviewing outcomes and making decisions.

The machines keep going.

So a different bottleneck appears:

I cannot keep up with what they produce.

The problem is no longer that the code cannot be written. It is that the code, PRs, issue analyses, experiments, and reviews arrive faster than I can deeply understand them.

That makes “I do not manually review every PR” less of a provocative choice and more of an inevitable consequence of throughput.

If a machine can generate dozens of mergeable changes in a day, but a human insists on reading every line, then the agent’s production capacity is still capped by human review bandwidth.

I now spend more time deciding what deserves my attention.

Routine implementation detail: usually not me.

A failed test: let the agent fix it.

Ordinary review feedback: let the agents resolve it.

A production error: first let an agent investigate.

But if cost jumps, the product boundary changes, an authorization surface widens, the architecture starts drifting, or the real user experience feels wrong, I go back in.

Software engineering used to be constrained by engineering time.

In these two projects, it is increasingly constrained by human attention.

Experience Still Matters, but It Is Used Differently

There is an obvious objection to all of this:

I can work this way because I have already been a software engineer for more than a decade.

I think that objection is correct.

I may not review every line, but I am not ignorant of the systems.

I understand Free4Chat’s major architecture boundaries: why the Runtime and Harness are separate concerns, why a capability is not the same thing as authorization, why high-frequency Room App state should not casually become Durable Object storage.

I understand why MyInvestPilot must separate external cash flow from investment return, why a pretty portfolio metric is not enough, and why transparent rules matter more to the product than a mysterious “AI prediction.”

That experience affects the questions I ask, the outputs I distrust, and my ability to recognize a design that technically works but should not exist.

Anthropic published a useful study in June, Agentic coding and persistent returns to expertise, based on roughly 400,000 Claude Code sessions.

Its division of labor looks surprisingly familiar. In a typical session, people made about 70% of planning decisions while Claude made about 80% of execution decisions. More expert users also triggered longer chains of agent actions per instruction.

Experience still correlated strongly with success. Sessions rated novice reached Anthropic’s strict “verified success” measure about 15% of the time, while intermediate-and-above sessions reached roughly 28–33%. When a session had already run into trouble, verified success rose from about 4% for novice sessions to 15% for expert ones.

That matches my experience.

But the role of expertise is changing.

It used to be:

If you do not know how to do it, you cannot build it.

It is becoming:

Even if you do not know how to do it, an agent may still build it. What your expertise changes is whether you know when not to trust the result.

Engineering experience is moving away from being purely a production skill and toward being a way to allocate risk, complexity, and attention.

Anthropic’s occupation data makes the same shift harder to dismiss. Among sessions that actually produced code, users in software-related occupations reached verified success about 34% of the time; users in other occupations reached 29%. Under the looser “at least partial success” measure, the numbers were 89% and 88%.

That gap is already small.

If it keeps shrinking, software production may become a normal part of work in many professions rather than something that necessarily belongs to a distinct software occupation.

That is more important, in my view, than whether an agent can beat an excellent programmer at programming.

The deeper question is whether the ability to produce software still needs to be a profession of its own.

Software Engineering Was Designed Around Humans

I once wrote a series of notes on Software Engineering at Google (Chinese). One idea from that book has stayed with me: programming and software engineering are not the same thing.

Writing the program is only the beginning.

Software survives for years. It changes. More people touch it. The hard part is allowing a team to keep changing the same system safely across time and scale.

That is why we have code review, CI, design docs, tech leads, ownership, on-call rotations, knowledge sharing, coding conventions, and all the process around them.

Those practices absolutely have technical value.

But look at them again and a large fraction are solving a particular problem:

How do you let a group of people—with different skills, different information, limited communication bandwidth, imperfect memory, vacations, turnover, and plenty of mistakes—maintain one complex system together?

Code review catches bugs, but it also spreads knowledge.

Design docs capture architecture, but they also create shared understanding before people commit to a direction.

Coding conventions are not only about aesthetics; they reduce the cognitive cost of reading one another’s work.

Ownership and on-call are technical mechanisms, but they also answer the organizational question: when this breaks, who is responsible?

We have long assumed a simple relationship:

As software grows more complex, it eventually needs more people. As the team grows, coordination cost increases. Software engineering as a discipline developed a large body of practice for controlling that human coordination cost.

Now imagine that one participant can read the entire repository, issue history, PRs, logs, and documentation on demand. It does not miss context because it took a day off. It can review a very large diff in minutes. A second agent can review the first agent independently. They do not schedule meetings, wait until tomorrow, or create an “only Alice knows this module” bus factor in quite the same way.

Not every process disappears.

But the thing those processes were designed to optimize is changing.

Full Stack in 2016, Agents in 2026

This reminds me of an old 2016 post, A Product’s Journey from Zero to One (Chinese).

What excited me then was that open source, cloud services, and full-stack development had made it possible for one person to build a complete product that would once have needed a small team.

But what did “one-person development” mean in 2016?

It meant that one person did more things.

Design the product. Write the iOS app. Write the backend. Design the database. Deploy the servers. Test the whole thing.

The essence of the full-stack engineer was:

one person can work across the whole stack.

Ten years later, “one person building a product” is starting to mean something else.

In the agent era, one-person development does not necessarily mean one person learned the whole stack.

It may mean almost the opposite:

one person increasingly does not need to personally execute across the whole stack.

Both situations look like “one-person software” from the outside, but the production model is completely different.

One expands the human skill boundary.

The other starts to separate software production from direct human execution.

From that angle, the full-stack engineer may turn out to have been an interesting transitional form.

The Limits of These Two Samples

The boundaries of this argument matter.

Free4Chat and MyInvestPilot are my projects. There is no large organization, no chain of dozens of teams, and no complicated approval hierarchy. I can make decisions directly, and if those decisions are wrong, I personally absorb much of the downside.

That lets me delegate to agents far more aggressively than many companies can.

Enterprise software is different.

Finance, healthcare, critical infrastructure, and regulated systems require auditability, separation of duties, compliance sign-off, and controlled changes. An agent being technically capable of an action does not mean an organization can or should let it take that action.

Many forms of organizational “friction” are not waste. Knowledge distribution, clear accountability, and multiple sign-offs are also forms of risk control and governance.

So two side projects obviously do not prove that every company will end up with one engineer and a swarm of agents.

That would be nonsense.

What these projects offer is something closer to a low-friction sample.

One person holds much of the product authority, architecture authority, and final responsibility, and is free to use agents aggressively. Under those conditions we can observe something that used to be hard to observe:

What is the minimum amount of human engineering labor a non-trivial software system needs in order to keep evolving?

The answer I am seeing is much lower than I would have expected three years ago.

Large organizations will not immediately reach that floor.

Some may never reach it.

But if the floor moves, that alone matters.

Markets have a habit of trying to move toward lower-cost production.

When Engineering Gets Cheap

There is an irony in all of this.

As building software becomes easier, I do not feel that building a successful product has become easier.

Free4Chat can gain new capabilities quickly. MyInvestPilot can add experiments, analytics, and automation quickly.

But the hard product questions are exactly the same:

Who needs this?

Why would they use it?

Why would they keep using it?

Why would they pay?

Is the need real, or do I just find the feature interesting?

Does the product have product-market fit?

Coding agents do not answer those questions for free.

In some ways the problem gets worse.

Many products used to never exist because they were too expensive to build. If an idea required five engineers for six months, most people would never even test it.

Now the same idea may have a usable first version in days.

Software supply gets cheaper.

Demand does not grow at the same speed. People do not get more hours in the day. Their wallets do not become larger because software is easier to generate.

The agent era may not produce a world where everyone owns a successful software product.

It may produce a world with dramatically more software, while the overwhelming majority of it still has no users.

Engineering scarcity is falling.

Problem scarcity is not.

That is one reason I now kill ideas more often than I used to. When implementation becomes cheap, code is no longer the thing that most needs to be conserved.

Attention and opportunity cost are.

Where Will the Next Senior Engineers Come From?

There is one question I do not have a good answer to.

My current workflow probably works as well as it does partly because I have already spent more than ten years doing software engineering.

The painful parts of those years matter: debugging concurrency problems myself, investigating production incidents, reviewing other people’s code, having my own code reviewed, making bad architecture decisions, living with the consequences, and later refactoring them.

Together those experiences formed a kind of risk intuition.

A lot of it is hard to turn into a checklist. I can look at a design and simply feel that something is wrong.

But where did that intuition come from?

Traditionally, a junior engineer writes code and makes mistakes. A senior reviews it. The junior debugs failures, sees incidents, revisits bad assumptions, and repeats the cycle. Years of dense feedback eventually create the person who can recognize which “reasonable” local decisions will lead a system in the wrong global direction.

What happens if the most effective future workflow is:

the agent writes the code, the agent reviews it, the agent debugs it, and the agent investigates the incident—while the junior mostly sees a final message saying “tests passed”?

Ten years later, where does the senior engineer come from?

This may be one of the strangest long-term effects of agents on the software industry.

The best agent workflows today still seem to benefit strongly from experienced people.

But those same workflows may reduce the opportunities that used to create experienced people.

The industry may not only shrink the amount of junior work. It may change the mechanism by which it produces senior engineers.

Perhaps engineering judgment will be learned through different feedback loops: design, evaluation, simulation, supervising agents, and studying agent failures rather than personally writing hundreds of thousands of lines of code.

Perhaps future models will simply supply more of the judgment that experienced humans provide today.

Anthropic explicitly notes that it is watching whether the “returns to expertise” decline as models improve. If that happens, even the part of the human role that still looks durable today may not be durable.

I do not know the answer.

But I think this question matters more than whether a new engineer should still learn Python.

Living Through It

In 2016, I saw open source and cloud computing make it possible for one person to build software that once required a team.

In 2023, I saw ChatGPT act as a capable coding driver, but I still believed human review was non-negotiable.

By 2024, I was already writing about AI-driven development (Chinese) and spending less of my time writing implementation code myself.

Now review, testing, debugging, and production incident investigation—the activities I used to think of as core software-engineering responsibilities—are also moving away from the human.

The software has not become simpler.

Free4Chat is much more complex than it used to be.

MyInvestPilot is too.

There is more code, more infrastructure, more system boundaries, more real users, and more real production failure.

That is exactly what makes the change hard to ignore:

the software keeps getting more complex, but the amount of human engineering labor required to keep it evolving is no longer growing in proportion.

For most of the history of software, we have assumed a fairly natural relationship:

more complex software → more software engineers.

That relationship may no longer be a technical necessity.

We may still need experienced people to set product direction, constrain architecture, watch costs, choose which risks to accept, and make final calls at high-consequence points.

Large organizations may continue to employ many engineers because responsibility, compliance, and organizational structure do not disappear just because agents get better.

Today’s agent workflows almost certainly contain risks we do not yet understand.

But in these two systems that I have maintained myself, I can no longer pretend this is merely “a programmer with a better tool.”

What I see is closer to this:

Software still needs to be designed, operated, maintained, and repaired.

Who—or what—does that work is changing.

I am still a software engineer.

But inside my own software, I increasingly do not look like one in the traditional sense.

If software can keep growing, changing, and handling difficult engineering problems while human engineers participate less and less in the implementation, then the important question may no longer be:

Will AI replace software engineers?

A more fundamental question is:

Does complex software still need as many software engineers as it used to?

And the question I now find even more important is:

If the future really is a small number of experienced people working with a large number of agents, where will those experienced people come from?