AI has changed how I write software, although the change has been different from what I expected. When coding agents became useful, I went in early. I built my own agents, subscribed to the tools, and pushed them into my workflow as far as I could.
For a while, I assumed agentic engineering meant letting agents implement more of the product while I supervised. The code appeared quickly, and each prompt created a satisfying burst of visible progress. After working this way for long enough, I realised that the growing codebase was becoming harder for me to understand.
My working definition of agentic engineering is a software development workflow where agents accelerate exploration and implementation while an engineer remains responsible for the system. The useful output may be production code, a prototype, a rejected approach, or a clearer technical decision. The measure that matters is whether the team ends up with software it understands and can operate.
What changed when I let coding agents drive too much
The first warning signs were ordinary engineering problems. Agents duplicated logic, added abstractions that the system did not need, and solved the local prompt without understanding the wider product. Each change looked plausible when viewed alone, so the accumulating cost was easy to miss.
As code output increased, my understanding failed to keep pace. I stopped building the internal map that normally forms while working through a problem and implementing it. Debugging then required reading code I had technically approved without ever understanding how the pieces fit together.
That gap affects more than code quality. A weak understanding of the domain makes architecture decisions less reliable, production incidents harder to diagnose, and future changes more expensive to estimate. The team eventually has to recover the missing context, usually when the system is already under pressure.
I responded by swinging back towards writing most of the code myself. I kept using LLMs for discussion, brainstorming, and technical exploration, which restored the part of the work where I form my own model of the system. That period changed how I decide which work to delegate.
A working definition of agentic engineering
Simon Willison describes agentic engineering as developing software with coding agents, meaning agents that can write and execute code in pursuit of a goal. That ability to execute code matters because the agent can inspect a repository, make a change, run a test, and respond to the outcome. It turns an LLM response into an iterative engineering tool.
Vibe coding describes a looser workflow where generated code can be accepted without being properly reviewed or understood. Addy Osmani’s distinction places professional engineering discipline around the agent through planning, review, testing, and human responsibility. This distinction is useful because the same coding model can support a disposable prototype or a production system, depending on the process around it.
Typing every line by hand is unnecessary for genuine ownership. An engineer can own generated code by understanding its role, verifying its behaviour, and remaining able to change it safely. The responsibility stays with the engineer and the team regardless of who produced the first implementation.
My agentic coding workflow
Different parts of a product require different levels of involvement. I now move through four stages as uncertainty falls and the implementation pattern becomes clearer. The agent receives more implementation responsibility after the important decisions have been made.
1Understand the business problem
2 |
3Explore approaches with an agent
4 |
5Establish the production pattern
6 |
7Delegate repeatable implementation
8 |
9Review, test, deploy, and operate
Explore before committing
Agents are particularly useful when I am still trying to understand the shape of a problem. I can ask for several possible implementations, compare APIs, build small prototypes, or investigate how an approach behaves under realistic inputs. This creates more evidence before I commit the product to an architecture.
I am willing to delete most of the code produced during this stage. The code has already served its purpose if it exposed a constraint, challenged an assumption, or made a tradeoff concrete. Exploration becomes cheaper because several directions can be tested with working software instead of being discussed only in abstract terms.
Deploy Crate, an infrastructure management tool I have been working on, is a good example. Its interesting problems concern abstractions, failure modes, operational behaviour, and the amount of complexity the system can afford. I want an agent involved in exploring those decisions because it can generate useful prototypes, while I retain responsibility for choosing the model that the product will carry forward.
Establish the first production pattern
Once an approach looks promising, I work through the core model in enough detail to understand it. That means deciding how data moves, where failures surface, which concepts deserve an abstraction, and which behaviour should remain explicit. These decisions create the boundaries that later implementation has to respect.
Writing the first production implementation myself is often useful because the friction reveals flaws in my mental model. An awkward interface may indicate that two responsibilities have been combined, and a difficult test may reveal that the system boundary is wrong. Those discoveries are harder to make when I only review the finished diff.
This preference is contextual rather than a rule requiring handwritten core code. A sufficiently clear design, good tests, and careful review can support an agent-generated implementation. I still need enough direct contact with the system to explain how it works and recognise when the generated structure is wrong.
Delegate repeatable implementation
Agents become more effective after the production pattern is stable. A known integration shape, established endpoint convention, or clear component boundary gives the agent concrete examples to follow. Review also becomes easier because deviations from the pattern are visible.
This is where I use agents for repeat integrations, internal tools, administrative interfaces, data migration helpers, API clients, and other bounded work. The classification depends on the product rather than the type of file being changed. An administrative screen can contain a critical permission boundary, while a large integration may be routine once its data contract is understood.
Verify that the team can operate the result
Generated code goes through the same production questions as handwritten code. The team needs to test it, deploy it, observe it, diagnose failures, and change it when the product evolves. Approval of a diff provides limited confidence when nobody can explain the behaviour that matters.
This also exposes the dependency risk around coding tools. Providers control model availability, product behaviour, access, and pricing, so a workflow may change for reasons outside the team. A product remains operable when its essential knowledge lives with the people responsible for it rather than inside a particular agent session.
What the CMS integrations taught me
One client project required integrations with around 15 to 20 content management systems. Each CMS had a different API, although the product needed the same broad flow: ingest content, process it, generate embeddings, and make the result available for LLM-powered search. Starting every integration independently would have produced a collection of locally sensible implementations with no stable system underneath them.
I began with one of the larger CMS platforms and used an agent to investigate its API, content model, polling requirements, and place in the embedding pipeline. The purpose of that work was to expose the domain and discover which details belonged in the common model. Several implementation choices could then be compared before they became expensive to reverse.
Once the shape was clear, I checked it against the other CMS APIs. A reusable pattern emerged, and I could write the core implementation, deploy it, and verify the behaviour with a real system. The remaining integrations became suitable for agent-assisted implementation because the central decisions had already been tested.
The broader engagement is covered in my ChatSheet AI principal engineering case study, including the vector-based context search and CMS integration work. The experience showed why the timing of delegation matters. Agents provided the most leverage after exploration had produced a domain model that both the system and I could rely on.
The strongest argument for generating more production code
A disciplined team can let agents write a large proportion of production code. Clear specifications give the agent a target, automated tests provide feedback, and code review checks whether the implementation fits the surrounding system. As models improve, the amount of implementation that can be delegated safely will probably grow.
I agree with much of that argument. The number of lines personally typed is a poor measure of engineering ownership, and handwriting routine code can consume time without creating useful understanding. An engineer owns agent-generated code when they can evaluate its design, verify its behaviour, and accept responsibility for operating it.
My boundary is therefore based on uncertainty and consequence. I stay closer to decisions that shape the domain, architecture, reliability model, and expensive failure modes. I delegate more freely when the pattern is established, the behaviour is testable, and a mistake can be detected without reconstructing the entire system.
An ownership check before generated code ships
I use a small set of questions to test whether generated code is ready to become part of the product. These questions apply to handwritten code as well, although agents make them more important because implementation can arrive faster than understanding. A weak answer indicates that the team needs more investigation before shipping.
| Question | What it protects |
|---|---|
| Can we explain the domain model and data flow? | Future changes can be designed from an accurate understanding of the system. |
| Do we know where failures surface and which ones are expensive? | Operational risk can be monitored and handled deliberately. |
| Do the tests verify behaviour that users or other systems depend on? | Passing tests provide evidence beyond confirming the current implementation. |
| Can an engineer debug and change the critical path? | Production ownership remains inside the team. |
| Could we operate the product if the current coding tool disappeared? | Tool dependency stays separate from system understanding. |
A failed check does not automatically require rewriting the code by hand. The smallest useful response may be a walkthrough, an additional test, a simpler design, or a focused rewrite of one critical boundary. The goal is to close the understanding gap before production forces the team to close it under worse conditions.
The workflow I trust now
I use coding agents heavily, and I expect their role in my work to keep expanding. I use them to explore alternatives, test assumptions, create disposable prototypes, and accelerate implementation around an established pattern. I remain closely involved in the decisions that determine how the product behaves, fails, and changes.
This workflow produces plenty of code when the product needs it. It also makes deleting generated code an ordinary outcome because a prototype may have delivered enough clarity to justify its cost. Agentic engineering is most useful to me when it improves the decisions surrounding implementation and leaves the team with a system it can still own.
If your team wants AI-assisted delivery while keeping architecture and production responsibility clear, this is part of the technical leadership and AI workflow work I provide. You can tell me about the project when a product or codebase needs this balance. We can start with the real engineering constraint and decide where agents provide useful leverage.
