AGENTIC ENGINEERING · TOOLS AND WORKFLOW
Agentic engineering
I lead our agency in adopting agentic engineering workflows to increase our velocity - enough that our release cadence has gone from fortnightly to twice weekly. We review the newest tools and update our workflows monthly. It's a lot of fun to tweak our AI tooling and get features shipped as fast as we can think of them.
We put a lot of effort into the scaffolding and guardrails for our AI. Below I'll go into detail about how I currently deliver new features and new project prototypes rapidly.
THE PIPELINE · SAME SHAPE ON EVERY REPO
How a feature ships
Every new feature travels the same route. Small features or tweaks can skip the spec step and jump straight into planning with the agent.
- 01SpecScope, examples, and acceptance checks in a markdown file the agent reads first.
- 02PlanUsing Lavish, the agent creates a detailed plan for how it will add the feature, and presents it as an annotatable page file in browser
- 03BuildOnce the plan is accepted, one orchestration agent runs with subagents, in its own worktree. It implements the full feature, tests included.
- 04GatesTypecheck, linting, integration tests or e2e tests at multiple viewports. Each step provides feedback to the agent so it can self-correct
- 05no-mistakesIn depth automated review step. The agent flags any long term maintainability issues, re-runs tests and proposes fixes or improvements.
- 06Human reviewWe look through the files that were changed, smoke test locally if needed, and modify the spec file if anything needs changing, then raise a PR with another tool call
BACKEND · FASTAPI · POSTGRES
Backend: guardrails to stay on track
Backend agents get the strictest scaffolding, because backend mistakes are the quiet ones. The repo's CLAUDE.md is a real onboarding document: architecture map, module conventions, the exact CI commands, and a list of project-specific pitfalls with worked wrong-and-right code examples, so the agent doesn't rediscover them the hard way. We use a makefile with all the common commands the agent needs, which greatly increases the reproducability of agents.
Linting an agent can't argue with
One command, make lint-all, runs four layers: a formatter, a linter, a type checker, and a custom linter we keep for rules no off-the-shelf tool checks. The custom rules are small and evaluate in milliseconds, for example every route handler must be async, no print statements in endpoints, and every model column's type annotation must agree with its nullability. A post-edit hook also auto-formats every file the agent touches the moment it writes it, so unformatted code never even reaches a commit. This keeps code looking consistent across the codebase.
Modularity the agent can navigate
Every entity in the project is a module with the same fixed file layout: model.py, schemas.py,endpoints.py, factory.py,utils.py, and its tests. Each module also carries afeature_definition.md - a living spec of the business rules the agent must read before touching the module and update after. Predictable structure is what makes agents fast: they always know where things go.
Integration tests, given-when-then
Tests run against a real Postgres database, not mocks - factories build the object graph, external services are stubbed centrally, and every test reads as given, when, then. The style is mandated in the repo's style guide, so the agent writes tests we can skim in seconds:
async def test_get_time_entries_for_task_success(
async_client: CustomTestClient,
):
# Given we have an admin user with a business
user_business = await admin_user_with_business()
# Given we have a task with 3 time entries
task, _ = await _create_task_with_time_entries(
user_business.business, 3
)
# When we request time entries for this task
response = await async_client.api(
f"/time-entry/task/{task.id}",
expected=200,
token=user_business.token,
)
# Then we should get back all 3 time entries
assert len(response) == 3FRONTEND · REACT · TAILWIND V4 · PLAYWRIGHT
Frontend: the agent looks at what it built
Frontend needs different guardrails, because the failure mode is different: a page can pass every test and still look wrong. The stack is chosen for agents as much as for users: Tailwind utility classes keep every style decision in the markup where the agent can see it, design tokens are the only allowed colours, and Zod validates every API boundary against a generated contract. Of course we also use typescript and enforce linting, on the frontend, but that doesn't catch as many errors as proper e2e testing.
E2E tests the agent runs on itself
Every screen has Playwright coverage at two viewports - desktop and iPhone - and both must pass before any task is done. The suite runs against deterministic mock fixtures rather than a live backend, so a red test always means the frontend broke. Each parallel agent worktree grabs its own free port and boots its own dev server, so several agents can run e2e at once without stepping on each other.
Screenshots are a gate
This rule makes agents excellent at design too: tests passing is not sufficient - the screenshots must look right. After any UI change the agent captures full-page screenshots of every touched screen at both viewports, opens them, and fixes overflow, cramped tap targets, and broken alignment before it may call the work done. This one practice catches more real bugs than any lint rule I've written.
One contract, two repos
The two repos share a single source of truth for the API. Whenever the backend changes, it exports its full API docs to a file the frontend repo keeps a copy of - so the frontend agent always builds against the exact endpoints and shapes the backend actually serves, with Zod checking them at runtime:
- 01BACKEND REPO · FASTAPIThe endpoint landsWhenever the API changes, the agent runs one make target that regenerates the docs straight from the live FastAPI code.
- 02GENERATED CONTRACTbackend-docs.toonThe full OpenAPI spec in TOON, a token-efficient format - compact enough for an agent to read the whole API in one sitting. Never hand-edited.
- 03FRONTEND REPO · ZODSchemas validate every boundaryThe frontend agent writes Zod schemas, query hooks, and mock fixtures against the fresh contract. Any drift fails loudly at runtime, not quietly in the UI.
The generated contract file is the load-bearing idea: backend and frontend never guess at each other. The frontend is always built against what the backend actually serves, regenerated the moment the endpoint lands.
THE TOOLKIT · 14 ENTRIES · CHANGES MONTHLY
The toolkit
Everything above runs on this set of tools.
SKILLS · WRITTEN AND MAINTAINED MYSELF
TOOLS
PRACTICES