Essay

Beyond Writing Code

January 2025

Code is Cheap

Software engineering has been a fundamentally human activity for decades.

We've continually improved the tools. Better languages, faster processors, more sophisticated frameworks, better IDEs and increasingly powerful abstractions. But the underlying process remained remarkably consistent: a person understands a problem, reasons about a solution and translates that solution into code.

AI agents are beginning to change that relationship.

We can now delegate implementation to machines. Not just autocomplete or snippets of code, but entire features, bug fixes, refactors and tests. We can run multiple agents simultaneously, each working on a different part of a codebase.

This is a significant change.

But I think a lot of people are drawing the wrong conclusion from it.

The opportunity isn't simply to replace one engineer with ten agents. It's to change how we organise engineering work, where we spend our attention and what we expect from the engineer.

The bottleneck is moving.

And understanding where it moves is far more important than counting how many agents we can run.

More Agents, More Problems

The obvious vision of agentic software development looks something like this:

  • Spin up ten agents.
  • Give each one a ticket.
  • Let them implement ten features.
  • Merge ten pull requests.
  • Achieve ten times the output.

It sounds reasonable. In practice, it breaks down very quickly.

Yes, agents can work in parallel. We can isolate them using Git worktrees, separate branches and independent environments. We can give them individual tickets and let them work autonomously.

But producing code is only one part of software engineering.

Someone still needs to understand what was built, whether it fits the architecture, whether it solves the actual problem and whether it introduces problems elsewhere.

Review becomes a bottleneck.

Ten agents can produce ten pull requests in a day. But if reviewing those pull requests requires the same engineer to understand ten separate changes, the work hasn't magically become ten times faster.

And the uncomfortable reality is that a completed task is not necessarily a completed piece of engineering.

AI agents can be remarkably confident without actually understanding the system they're changing.

They can:

  • Make significant architectural mistakes.
  • Misunderstand existing patterns.
  • Violate abstractions.
  • Duplicate existing functionality.
  • Silently break conventions.
  • Claim something is complete when it isn't.

Even relatively mundane things like Git branch management can become fragile when agents operate across multiple contexts.

The problem isn't generating code anymore.

The problem is maintaining understanding as implementation scales.

AI can scale local implementation much faster than it scales global understanding. That distinction is becoming one of the most important considerations in software engineering.

The Work Machines Do Well

I think we make a mistake when we discuss AI capability as though it's uniform across all engineering work.

It isn't.

The usefulness of an agent depends heavily on the shape of the problem, the amount of context required and how clearly the work can be bounded.

Some tasks are excellent candidates for delegation. Others require substantially more human involvement.

Understanding that distinction is how we get meaningful productivity improvements rather than simply generating more code to review.

Finding bugs

Bug fixing is primarily a problem of understanding.

A production bug might ultimately be fixed with a single line of code. The difficult part is figuring out why that line needs to change.

Where does the issue originate? Which assumptions have failed? Is the problem isolated, or does it reveal something more fundamental about the system?

AI agents are useful here, particularly as investigative partners.

They can explore code, trace execution paths, investigate hypotheses, identify edge cases and help narrow down the source of a problem.

But I still want an engineer involved in the reasoning.

The value isn't simply having an agent make a change. It's using the agent to understand the system more quickly.

Broad structural changes

This is where I remain considerably more cautious.

Large, interconnected systems require architectural awareness, historical context and an understanding of why things were built the way they were.

A model might successfully trace a property through a React component tree or refactor an isolated utility function. That doesn't mean it understands the architectural consequences of changing the underlying system.

It might introduce a new abstraction that conflicts with an existing one, duplicate functionality that already exists elsewhere or make a local improvement that creates problems across the application.

The larger the blast radius, the more important human judgement becomes.

I want agents helping engineers evolve systems, not independently deciding what those systems should become.

Isolated implementation

This is where current agents are genuinely impressive.

A well-defined task with clear boundaries and sufficient context is an excellent candidate for delegation.

Single-file changes, isolated functions, small components and relatively self-contained features can often be implemented with very little human intervention.

But even here, there are subtleties.

Agents frequently copy patterns from elsewhere in a codebase without understanding why those patterns exist.

Is the duplication intentional? Should the logic be shared? Does a utility already exist? Is the pattern deprecated?

Generated code can look perfectly reasonable while gradually degrading the architecture.

The solution isn't to stop delegating these tasks. It's to build better context, validation and review into the process.

Ticket creation

This is one of the highest-leverage applications of AI that I see.

Historically, engineering tickets have often been terrible.

Vague titles, incomplete requirements, missing acceptance criteria and little technical context. Engineers end up spending considerable time interpreting what someone actually wants before they can begin implementing it.

Given good templates and sufficient repository context, AI can produce structured tickets remarkably well.

It can turn conversations, requirements and existing system knowledge into actionable work items.

The reduction in effort can be enormous.

More importantly, better tickets create better conditions for parallel implementation. An agent cannot reliably execute work that nobody has properly defined.

Pull request creation

Agents are also very good at producing pull requests.

Given commit history, ticket information, templates and repository context, they can usually explain what changed, why it changed and how the implementation works.

They can summarise changes, identify affected areas and prepare useful documentation.

Where I still want considerable attention is validation.

Does the testing actually prove the feature works? Are the rollout considerations understood? Have edge cases been considered? What happens when something fails in production?

A convincing pull request description isn't evidence of a correct implementation.

We should be careful not to confuse good communication with good engineering.

Testing

AI-generated tests are another area where the productivity gains can be substantial.

Agents can generate unit tests, identify missing cases and help expand test coverage.

But there's a particularly dangerous failure mode here: tests that look comprehensive while providing almost no protection.

I've seen the kinds of mistakes that make this possible:

  • Mocking the very component that should be under test.
  • Testing implementation details rather than behaviour.
  • Writing assertions that still pass when the feature is broken.
  • Generating tests that simply mirror the implementation.

A passing test suite doesn't necessarily mean anything if the tests themselves are poorly designed.

We should use AI to improve our testing discipline, not create the appearance of quality.

The Limits of Planning

One of the stranger trends emerging alongside AI agents is the amount of planning they encourage.

Massive implementation plans. Elaborate design documents. Hundreds of decomposed tasks. Entire speculative architectures generated before anyone has written a line of code.

Sometimes this is useful.

Often, reviewing the plan takes longer than simply doing the work.

AI-generated plans can contain subtle logical flaws, incorrect assumptions, unnecessary abstractions and elaborate decompositions that create more coordination overhead than they remove.

The verbosity creates an illusion of rigour.

I prefer short feedback loops.

Define the problem. Establish the important constraints. Delegate a bounded piece of work. Review the result. Learn from what happened. Continue.

Software development is inherently iterative. We discover things through implementation that we couldn't have known through planning alone.

The objective isn't to predict every step before starting. It's to create a process that can respond intelligently as new information emerges.

The Engineer's New Role

This is where I think the most significant change is happening.

As implementation becomes cheaper, the value of engineering shifts towards the things that are harder to delegate.

Understanding systems. Making architectural decisions. Establishing direction. Prioritising work. Coordinating people. Reviewing quality. Understanding customers and production environments.

These capabilities become more valuable, not less.

An engineer who can effectively direct ten agents, understand their output and maintain the integrity of an entire system has a fundamentally different leverage profile from someone who simply writes code faster.

But this introduces a new responsibility.

You aren't just managing code anymore. You're managing a system of agents, people, workflows, environments and feedback loops.

I increasingly want a single place to understand that system:

  • Pull request pipeline and review status.
  • Ticket progress and priorities.
  • What teammates and agents are working on.
  • Build and deployment health.
  • Production monitoring.
  • Important communications.
  • Engineering quality and competency development.

The dashboard becomes less about tracking code and more about maintaining awareness of the entire engineering operation.

The engineer becomes an orchestrator.

Not because engineering has become less human, but because the scope of what an engineer can direct has expanded.

Remain in Control

There's an important distinction I think we need to preserve as these tools become more capable.

Delegation is giving something to another agent to accomplish while retaining responsibility for the outcome.

Abdication is handing over responsibility because the machine appears capable of handling it.

These are not the same thing.

I want agents doing the repetitive work. I want them exploring code, implementing features, generating tests, investigating bugs and preparing pull requests.

I want engineers spending more time thinking about architecture, product direction, system quality and the problems we're actually trying to solve.

I don't want engineers becoming passive consumers of generated code.

The ability to produce more software doesn't automatically mean we're becoming better at engineering.

We still need people who understand what they're building, why they're building it and whether it should exist in the first place.

The tools should expand that capability, not erode it.

Understanding Remains Human

I don't think AI agents make software engineers obsolete.

I think they expose something that has always been true about software engineering: implementation is only one part of the job.

For decades, we've optimised the implementation process. Better languages, better frameworks, better tooling and better abstractions.

Now we're beginning to make implementation abundant.

But understanding remains scarce.

The engineers who benefit most from this transition won't necessarily be the ones who run the most agents or generate the most code. They'll be the ones who can direct increasingly large amounts of implementation without losing sight of the system as a whole.

They'll know what to delegate, what to review, what to reject and what needs human attention.

That's the opportunity.

We should use these tools to become better engineers, not to stop being engineers.

The goal isn't to build machines that can do our jobs. It's to become engineers capable of building things we couldn't have built before.