Our AI-Driven Development Workflow: Plan Files, Guardrails, and Reviewed Diffs

Kicked TeamApril 9, 20266 min read

We have now shipped three open-source infrastructure tools — nftably, birdy, and meerkat — with AI coding agents doing most of the typing. Not as an experiment: as the actual production workflow. This post is the workflow itself, in enough detail to steal. For the conceptual background, start with what agentic development actually is; for the results, see the case study.

Everything starts with a PLAN.md a human writes

Before an agent touches the repo, a human writes the design doc. Ours contain, in order:

  • What the thing is, in one paragraph — and just as important, what it is not. "One router, done well. No agents, no controller, no fleet."
  • The architecture — components, boundaries, what talks to what
  • The data model — actual table definitions, not vibes
  • Milestones — each one shippable on its own
  • Explicit non-goals — the features we are deliberately not building

The milestone rule from birdy's plan is the one we now apply everywhere:

Each milestone is independently usable and cannot break anything the previous one didn't already touch.

For birdy that meant: it is a read-only viewer you can trust before it ever writes a config. That ordering wasn't incidental — it let us put a beta tool on a real router at milestone one, because milestone one couldn't break anything.

Why a document and not a conversation? Because a conversation is ephemeral and a doc is durable. Agents lose context between sessions; a PLAN.md in the repo means every session starts from the same ground truth. A design doc is simply a better prompt: it is versioned, reviewable, and it forces us to decide the hard things — the object model, the safety semantics — before generation starts, instead of letting the agent decide them by accident mid-diff.

The guardrails

The agent works inside a fence. The fence is boring, and that is the point:

  • CI on every change. Build, vet, lint, full test suite. The agent must leave CI green; a red pipeline is an automatic rejection, no discussion.
  • Linters and formatters as law. Style debates with an agent are wasted breath. Encode the rules; the tools enforce them for free.
  • The agent writes tests alongside code. Then we review the tests harder than the code, because an agent can write test-shaped code that asserts nothing. (More on that in our field guide to reviewing AI code.)
  • Small diffs, always. One milestone slice, one reviewable change. When an agent produces a 2,000-line diff, we don't review it heroically — we reject it and cut the task smaller.
  • No secrets in the loop. Agents get dev environments and fixtures, never production credentials. When agents need to touch real infrastructure at all, that access is designed deliberately — a topic big enough that we wrote about it separately in giving AI agents access to real infrastructure.

The division of labor

The clean way to say it: the agent types, the human decides.

The concrete example we keep coming back to is the armed auto-revert. Both nftably and birdy apply configuration to a live box — a firewall, a BGP router — that you are usually connected through. A bad rule locks you out; a bad BGP filter takes you off the internet. So both tools apply changes as an atomic transaction with a timer armed: if you don't confirm within the window, the box reverts to the last good config on its own.

No agent proposed that. It came from a human who has hand-edited nftables over SSH and felt the specific fear of pressing enter. The agent then implemented it — state machine, timer, rollback path, tests — quickly and well. That is the division of labor in one feature: safety semantics are a design decision made by someone with scar tissue; implementation is labor the agent is genuinely good at.

The same split held everywhere: humans owned the data model, the config-ownership rules ("this tool owns the whole file it renders"), the decision to refuse to render configs that look like route leaks. Agents owned CRUD, renderers, UI plumbing, packaging, and docs sync.

End every session by updating the plan

Agent context dies when the session ends. The repo is the only memory that survives. So the last act of every working session is updating the plan and docs: what shipped, what diverged from the design, what is next. The next session — agent or human — starts by reading it.

This also keeps you honest about drift. Birdy's plan now carries a note listing everything that shipped differently from the design. That note is a feature, not an embarrassment: the plan is a record of intent, and the delta between intent and reality is where the engineering judgment lives.

When the agent goes sideways

It will. The failure smell is always the same: mounting complexity without mounting progress — a growing diff, weirder edits, special cases stacked on special cases. What works:

  1. Revert early. The moment a session smells wrong, throw the diff away. Agent code is cheap to regenerate; that changes the economics of reverting. Sunk-cost reasoning about generated code is a bug in you.
  2. Re-spec, don't argue. A struggling agent in a long thread has usually been poisoned by its own context. Arguing with it adds more bad context. Reset, improve the task description with what you learned, start clean.
  3. Cut the task smaller. Nine times out of ten, "the agent can't do this" means "the task was two tasks."
  4. Take it back. Some problems — subtle concurrency, a genuinely novel algorithm — are faster to write by hand than to specify. Knowing which is which is the skill.

The workflow, as a checklist

  1. Human writes PLAN.md — architecture, data model, milestones, non-goals
  2. Milestones ordered by trust — each independently usable, none can break the last
  3. CI, linters, and tests as hard gates — the agent keeps them green or the diff dies
  4. Agent types, human decides — safety semantics and object models are never delegated
  5. Small diffs, real review — reject and re-cut instead of heroically reviewing big ones
  6. Update the plan at session end — the repo is the only memory that survives
  7. Revert early, re-spec, don't argue — generated code is cheap; your time isn't

We got here by shipping — and by making the early mistakes on our own tools instead of a client's. If you'd rather skip that part, we help teams set up agentic development with these guardrails in place. Talk to us.