Engineering, loosely defined, in the LLM era
A few months ago I followed a bug from the frontend through an API and into a database query, fixed all three layers, and shipped it that afternoon. Two years earlier I may have handed part of it to somebody else and waited.
LLMs have made that sort of breadth normal for me. They've also made it much easier to produce a lot of software nobody needs. These are the habits I use to tell the difference.
Judgment is the job now
An agent will happily build whatever I describe. My job is deciding whether the request solves a real problem, where the result belongs in the architecture, and what it'll cost us six months from now.
Much of the useful work now happens before the first prompt. I might remove a feature from the plan, find a smaller way to test the assumption, or notice that the proposed solution creates more work than the original problem. Faster implementation gives bad ideas less time to reveal themselves, so the judgment has to come earlier.
Own the whole problem
At my best I'm in the room when the problem surfaces, in Linear when the plan is written, in the codebase during implementation, and beside the people using it after release. I can answer the awkward questions because I was there for the decisions that created them.
Good product managers make this easier by bringing engineers into the problem while the options are still open. The trouble starts when one person hears the request, another turns it into a spec, and a third builds it weeks later. Every transfer removes some of the reason the work existed in the first place.
Owning the problem means staying close enough to challenge what people asked for, explain the technical choices, and see whether the finished thing helped. Questions are part of that job. Unexamined assumptions making it into production are the failure.
Validate before you commit
I've watched teams spend months fighting an existing implementation, bolting features onto architecture that wasn't designed for them, instead of stepping back and asking “what do we actually need?”
Before I commit to an approach, I check that the stack can support it, trace the integration points, and look for complexity hiding inside simple requirements. A conversation might settle it. A small technical spike or prototype in code might expose something nobody had considered. Code can be research, and a quickly generated prototype does not need to become the architecture we live with.
Ship, then iterate
I'd sooner put a small, reversible version in front of people than spend another week discussing the perfect one. Once somebody uses it, the conversation has evidence in it.
The bar follows the risk. I can ship a reversible interface change with rough edges and watch what happens. A medication workflow needs evidence, controls, and a much higher level of assurance before it reaches anyone. In both cases I'm explicit about what we've left out and how we'll learn what to do next.
Turn requests into decisions
A request rarely arrives in the form the codebase needs. “Add a dashboard” still leaves you to work out who will read it, what they should do afterwards, how current the data needs to be, and whether a dashboard is the right answer at all.
I use a spec to expose those decisions before they get buried in code. It starts with the problem, records why we chose the approach, and makes the technical trade-offs visible. By the time implementation starts, the people involved can disagree with the same written plan instead of carrying different versions of it in their heads.
Documentation, now more than ever
I write code with AI and use Linear as the log for the work. Each issue records the problem, the decisions we've made, the constraints, and what done looks like. If an awkward edge case only exists in somebody's head, the agent has no chance of accounting for it.
I connect Linear to my coding tools through MCP, so the agent reads the issue directly and implements from there. I don't rewrite the context in a prompt or rely on a meeting transcript to preserve the important bits.
Linear stays current while we build. When the code exposes a missing decision or changes the approach, I update the issue with what happened. That gives me, the team, and the agent the same working memory for the next pass.
Define what working means
Every feature starts with a user story, the behaviour we expect, and a benchmark. That might be fewer failed submissions, less time spent on a task, or a response that stays below 200 milliseconds. Writing the measure down forces us to say what improvement we expect before the feature exists.
I test the feature at the level where it can fail. Automated tests catch broken behaviour and regressions, while the product measure shows whether the behaviour helped anyone. A passing test suite can prove we built what we described. It cannot prove the description was worth building.
Delegate the work, not the responsibility
I use AI tools constantly. They write code, generate tests, draft documentation, scaffold services, challenge a plan, and surface options I had not considered. I also set work running overnight when the task and checks are clear enough for it.
Delegation expands with the context and risk. An agent can take most of a reversible interface change when the tests are good. I stay much closer to work involving medical data, an unfamiliar integration, or an architectural decision we'll be living with for years. I make the decision to ship, and “the agent wrote it” will not be a useful explanation if it goes wrong.
Dogfood everything
At LloydsDirect, badly packed pallets had been blamed on a supplier for years. The engineers went to the warehouse, tried to unpack one against the same target as the staff, and could not do it either. The supplier had followed the pallet configuration our software requested.
The reports showed missed targets. Unpacking a pallet showed why. I try to get that same feedback loop into every product by using the tool myself when I can, and sitting beside the people doing the work when I cannot. Build something, watch it being used, then fix what got in the way.