Agentic Dev Patterns
Who this is for — developers already working with agents daily who want the habits that make the next session cheap.
Read this if — your agent keeps re-learning your project.
Skip this if — you have not yet built a harness; start with Canary-First Development.Takeaways
- Treat the repository as the agent’s memory: state, warnings, and next steps go into
issues/before the context ends.- Configuration is data, not code — specs in
spec/, embedded at compile time.- One component, one language; a duplicate implementation is a divergence with a schedule.
An AI coding agent is a brilliant colleague with a strange disability: at unpredictable moments, it forgets everything and starts over. To build software efficiently alongside such colleagues, our workflows must account for both model strengths and constraints (such as context window limits and formatting requirements) — and turn the repository itself into the memory the agent lacks.
The following practices are compiled from development cycles on emojig and cati.
graph LR
S[Session works] --> C{Context ending?}
C -->|compaction / handover| W[Write state to issues/]
W --> P[Run make preflight]
P --> N[Next session reads issues/ cold]
N --> S
C -->|no| S
1. Context Compaction & Self-Documentation
Frontier models lose fine-grained details when context windows are compacted or when starting new conversation sessions.
- The Practice of Self-Documentation: Before a context compaction event or at the end of a session, write down the current state, compiler warnings, design choices, and immediate next steps in a temporary or persistent issue tracker file (e.g., in
issues/). - Preflight Cleanliness: Always execute a standard check target (e.g.,
make preflight, formatter checks, and license linters) before ending a turn. This catches code formatting errors or compilation issues before they pollute the next agent’s context.
🤖 Agent takes the wheel — writing its own handover. Current state, files touched, open warnings, next step: the agent has this information and the human does not. Asking for it costs one instruction at the end of a session.
🛡️ The check that makes it safe —
make preflightimmediately before the handover file is written. A handover that describes a red repository as done is worse than no handover, and preflight is what makes the claim checkable.
2. Spec-Driven Architecture
Avoid embedding layout constants, UI labels, keyboard shortcuts, or color definitions directly inside application code.
- Declarative Specs: Define UI structure and bindings in declarative files (YAML or JSON) inside a
spec/folder. - Compile-Time Embedding: Load these specifications at compile time (using
//go:embedin Go or@embedFilein Zig) to prevent runtime file reads. - Single Source of Truth: Auto-generate derivative formats (such as JSON schemas or web simulator configurations) from the master specifications. This prevents double-maintenance hazards where changes in the code must be manually copied to documentation or schemas.
This pattern was not designed on a whiteboard; it was extracted from a week of
pain. Week 2 of the Emojig Chronology is where the
picker’s layouts, captions, key bindings, and colors moved out of the Zig loops
and into spec/ — after which ten UI languages were added without a single
line of Zig changing. This book’s own fonts and colors work the same way, and
the story of the bug that shipped with them is The Golden Path.
🤖 Agent takes the wheel — the extraction itself: hoisting constants into a spec file, generating the schema, and rewriting call sites. Wide, repetitive, and verifiable by construction.
🛡️ The check that makes it safe — a schema-validated spec plus a unit test asserting the loaded struct matches it (here,
TestLoadSpecConfig_Books_DisableHeadersFootersinscripts/build_slides_test.go), so a value that silently stops arriving fails the build instead of the page.
3. Complexity Reduction & Single-Language Focus
Supporting duplicate implementations of the same logic in different languages is a massive agentic maintenance burden.
- Divergence Trap: Maintaining a picker in both Go and Zig leads to silent differences in search scoring, layout boundaries, and terminal handling.
- The Fix: Consolidate on a single language for each component (e.g. rewriting and focusing 100% on Zig for the
emojigTUI). Lowering repository lines of code simplifies the build pipeline and increases execution speed. The bill came due in Week 4: 2,450 lines of Go clone deleted in one commit. - Zero-Daemon Philosophy: Prefer statically linked, self-contained binaries that execute instantly and clean up after themselves. Avoid background resident daemons that complicate error tracking and state synchronization.
Why These Patterns Scale to a Fleet
Each pattern above removes one reason a project needs you specifically to stay healthy:
- Self-documentation means any session — yours, an agent’s, a future contributor’s — can resume the work cold.
- Spec-driven architecture means style and structure changes are config edits, not code archaeology.
- Single-language focus means each repo asks for one mental model, not three.
Apply all three across ten small apps and the maintenance load stops multiplying: every repo greets its next visitor with the same files (AGENTS.md, spec/, issues/, a make preflight), the same checks, and no surprises. That uniformity — not heroics — is what lets one developer keep a fleet alive.