Back to blogStill an Engineer, Just Moved

Last Tuesday, 4:40pm. I'm sitting in front of a 340-line diff with my hands nowhere near the keyboard. An agent had just finished rewriting our tenant-provisioning service around a new retry policy — clean, lint-passed, backed by unit tests it wrote for itself. I sat there for about four minutes doing nothing that looked like engineering. No editor open. No terminal scrolling by. Just me, the diff, and every failure mode I could think to throw at it, trying to decide whether I trusted this enough to let it near the path that onboards every new customer we have. That's the job now. Not "can I write this better," but "do I believe this enough to let it run."

My actual Tuesday, hour by hour

This is pulled straight from my calendar and shell history, not dressed up for the post.

  • 8:30–9:15 — Overnight logs first, from three subagents I'd left running on a flag-cleanup migration. I always read logs before diffs — logs tell you if something went off-script before you've even looked at the output.
  • 9:15–10:00 — Rewrote a single paragraph in the platform repo's CLAUDE.md, because Tuesday's incident (more on that below) traced straight back to a vague instruction about "canary tenants." Getting that paragraph right took longer than hand-writing the actual fix would have.
  • 10:00–11:30 — Pair review with two senior engineers over what three agents had produced overnight. Maybe 20 minutes of that was reading code line by line. The rest was "what did we actually tell it to do" held up against "what did it do."
  • 11:30–12:00 — Wrote a verification script that now has to pass in staging before any tenant-config-change agent is allowed to open a PR: row-count delta, tenant-ID allowlist, a hard cap on blast radius.
  • 1:00–2:30 — Actual hand-written code: sixty lines fixing a race condition in our webhook dedup logic. This used to be the whole day. Now it's a block on the calendar.
  • 2:30–4:00 — Built a new subagent for database migration review with a narrower tool allowlist — read and propose, no direct execute. Directly downstream of the incident.
  • 4:00–5:00 — Postmortem doc. Almost none of it about the code. Almost all of it about the instruction layer and the permissions an agent had walking into the task.

Eleven minutes

One of my engineers asked an agent to clean up a specific set of feature flags: pull the rollout-state row for canary tenants once their rollout was done, so the flag table would stop bloating. The agent had access to a migration-execution subagent with write access to the tenant_flags table — not just a PR, but a run-against-staging gate that auto-approved anything matching a pattern we'd already cleared: single-table, single-column update.

The agent read "rollout complete" against the schema and, technically correctly, matched it to every tenant where rollout_pct had hit 100 — which was, it turned out, all of them, not the five actual canaries. We'd overloaded that column two years back and never got around to separating "rollout percentage" from "migration complete." It updated roughly 600 rows in about 40 seconds, then moved on to the next step of its plan: dropping now-empty flag rows for tenants it considered fully migrated. That deleted live routing flags for tenants who hadn't finished migrating at all.

Six minutes in, four customers were seeing their old onboarding flow reappear, and an anomaly alert fired on flag-lookup latency — not on the deletion itself, since we'd never put an alert on that table's write volume. An engineer checked the migration bot's log channel, saw the queries it had run, and killed the queued follow-up steps. Eleven minutes, start to kill. Rollback took another 40, because we had to rebuild state from an event log instead of a clean snapshot — the table had kept drifting further while we were still figuring out what happened. No data loss, no money lost, about 90 minutes of two engineers' time, and roughly 20 minutes of degraded onboarding for four customers. Small, as incidents go. But it moved at a speed no person doing the same wrong thing ever could have.

It executed the wrong plan perfectly

A junior engineer forgetting a case usually gives you a local bug — one function, one call site, something a test would probably catch. This wasn't that. The agent didn't misread the instruction; it satisfied it exactly, to the letter, against a mental model of the schema nobody had actually checked matched reality. The failure was in the plan, not the execution of it — and it carried out that wrong plan flawlessly, fast, everywhere it applied at once, instead of one call site at a time the way a person with the same bad assumption would have. A person hits tenant #3, notices something's off, and stops to ask.

So the postmortem didn't spend time on why we missed it in review — every individual query, read on its own, was correct. It asked why a subagent had unattended write-and-delete access to a shared table with no hard cap on tenant count, and why the auto-approve gate keyed on "matches an allowed pattern" instead of "matches an allowed pattern and touches fewer than N rows." Those are the questions I build postmortems around now. They're also the questions I want answered before a task starts, not after a diff lands — a different point in the loop than code review even sits at.

What actually fills my calendar now

The unit of work I ship isn't a diff anymore. It's a harness plus a diff — and most days the harness eats more of my time than the diff does.

  • Verification scripts that decide whether an agent's change is even eligible for a PR: row-count deltas, allowlist checks, environment guards.
  • Blast-radius limits per subagent — which tables it can touch, which environments it can reach, hard caps on records changed per run.
  • The instruction layer — CLAUDE.md files, subagent definitions, prompts — reviewed with the same rigor as production code, because a vague sentence in there is now its own species of bug.
  • Postmortems that end in a change to the harness or the instructions, not just a code fix.

That's still engineering. It's encoding constraints, reasoning through failure modes, designing a system that fails safely under load — just one level up from where the function used to sit.

Still an engineer, just moved

I didn't stop being an engineer the day I stopped hand-writing most of my code. The job moved underneath me the same way it moved when we stopped hand-rolling memory management, then infra, then deploys. Each move pushed the work up a layer of abstraction and left more of the "who's accountable when it's wrong" question for a human to hold. Multi-agent systems move that line again, and faster, because now the system can act on its own conclusions instead of just proposing them.

In my PhD work at RMIT I spend a lot of time watching what happens when you hand a team of agents overlapping authority and no shared model of state. The tenant_flags incident is a small, real version of the exact coordination failure I study in simulation: agents that are each individually correct relative to their own model of the world, and wrong relative to the actual shared one. That's not a training problem. It's a systems-design problem, and systems design is engineering.

So here's what I'd tell a senior dev who feels like they're losing their craft: you haven't lost it, you're just being asked to point it somewhere you've never had to point it before — at the boundary conditions of a system that can now act on its own conclusions, fast, at scale. If your gut still says "wait, what happens if this is wrong everywhere at once" a beat before you approve something, that instinct is the craft. It used to fire mid-keystroke. Now it has to fire before you type the first word of the instruction.

Discussion

Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.

Ask me anything about “Still an Engineer, Just Moved” — trade-offs, decisions, or the story behind it.