Skip to content
Back to Reference

GARDEN principles

About this document

GARDEN is a set of six language-agnostic software design principles for the agent-first era, in which code is written, reviewed, and analyzed primarily by LLM agents while humans set intent and constraints but rarely read the code line by line. This document is the canonical, self-contained statement of the six principles. It does not depend on any other file in this documentation set and may be copied into any project as a standalone agent-facing reference.

The keywords MUST, MUST NOT, and SHOULD are used as defined in RFC 2119:

  • MUST — an absolute requirement.
  • MUST NOT — an absolute prohibition.
  • SHOULD — a recommendation that may be deviated from for a documented reason.

Each principle below is presented as: a short statement, a rationale, a set of rules, named anti-patterns, a minimal good/bad example pair, and the classic design principles it inherits from. A summary of how the six principles reinforce one another closes the document.

G — Grep-first Discoverability

Statement. Structure and naming minimize the number of search steps an agent needs to find the right code. One canonical name per concept; call sites traceable statically; no magic or dynamic dispatch that hides relationships.

Rationale. Coding agents navigate a codebase either by lexical search (grep/glob) or by vector indexes, and both approaches miss on large codebases; a common failure mode is an agent not finding an existing utility and reimplementing it. When a concept has a single canonical name — used consistently across code, tests, docs, and error messages — and call sites are resolvable by text search, the search step that precedes almost every agent action becomes reliable instead of probabilistic.

Rules.

  • MUST: use one canonical name per domain concept, and use it in code, tests, docs, and error messages.
  • MUST: keep call sites resolvable by static text search; do not use string-built identifiers, reflection-based dispatch, or convention-magic routing for domain logic.
  • MUST: name files and directories after the concept they contain.
  • SHOULD: prefer flat, predictable directory layouts over deep nesting.
  • SHOULD: word errors and log messages with grep-stable, unique phrases.

Anti-patterns.

  • Synonym sprawl — three or more names for one concept (user, account, member) used interchangeably.
  • Stringly-typed dispatch — routing behavior through string keys assembled at runtime instead of a statically resolvable reference.
  • Clever metaprogramming — behavior generated by reflection, decorators, or macros that a text search cannot follow to its definition.
  • Umbrella modules — catch-all utils/, helpers/, or common/ directories with no single concept to search for.
  • Identifier concatenation — names built by string interpolation, such as get_${entity}_handler, that defeat search.

Example.

// bad: identifier built dynamically, unsearchable by name
function dispatch(entity: string) {
  return require(`./handlers/${entity}Handler`);
}

// good: canonical, statically resolvable names
import { orderHandler } from "./handlers/order-handler";
import { invoiceHandler } from "./handlers/invoice-handler";

Inherits from. Principle of least astonishment. Rejects or weakens: convention-over-configuration magic, reflection-heavy dependency injection.

A — Atomic Vertical Slices

Statement. Small, self-contained vertical modules; one task fits in one context window. Tests colocated with code. Managed duplication instead of premature abstraction.

Rationale. GitClear's AI Code Quality research reports that across roughly 200 million changed lines, copy-pasted code rose from about 8.3% to 12.3% of changed lines while refactored or moved code fell from about 25% to under 10%, and blocks of duplicated code grew roughly eightfold since AI assistants became mainstream. Agents editing horizontally layered code must load and reconcile several layers per task; organizing by capability rather than by technical layer keeps the requisite context for one capability in one place and makes the natural tendency toward duplication a manageable, detectable cost instead of an architectural failure.

Rules.

  • MUST: organize code by capability (vertical slice), not by technical layer.
  • MUST: size a slice to fit a one-context task — entry point, logic, data access, and tests together; guideline: functions under approximately 50 lines, slices readable in one sitting.
  • MUST: colocate tests with the slice they verify.
  • MUST NOT: extract an abstraction before at least three concrete usages exist (rule of three); manage duplication through clone-detection signals in CI, not through preemptive DRY.
  • SHOULD: let slices communicate only through explicit interfaces; no reaching into another slice's internals.

Anti-patterns.

  • Horizontal layer folders — controllers/, services/, models/ spread such that one capability's code is scattered across the tree.
  • Premature abstraction — a shared function or base class created before three concrete usages justify it.
  • God modules — a single slice that has grown to cover multiple unrelated capabilities.
  • Shared mutable "common" state — state reached into and mutated by several slices outside any explicit interface.

Example.

// bad: one capability's parts scattered across technical layers
controllers/order-controller
services/order-service
models/order-model
tests/order-tests

// good: one capability, one place
slices/order/
  entry.ts
  logic.ts
  data.ts
  order.test.ts

Inherits from. Single responsibility principle, high cohesion / low coupling, KISS. Rejects or weakens: strict DRY, weakened to "managed duplication."

R — Regenerable Components

Statement. Any component can be rewritten from scratch against its contract without ripple effects. The spec/contract is the durable artifact; code is expendable. Extension happens through explicit ports, not rewrites.

Rationale. Spec-driven development, in which the spec is treated as the durable artifact and the code as regenerable, is gaining adoption, but specs drift from their code just as code drifts from intent, and need the same deterministic checks. Treating code as precious pushes agents toward cautious, incremental patches that accumulate implicit coupling; treating the contract — interface, behavioral spec, and examples — as precious and the code as disposable lets an agent regenerate a component confidently because correctness is checked against the contract, not against the previous implementation's shape. Extension therefore happens by adding adapters at explicit ports rather than by reaching into existing components.

Rules.

  • MUST: give every component a contract (interface, behavioral spec, and examples) precise enough that the component can be rewritten from scratch against it.
  • MUST: version contracts and specs next to the code they govern.
  • MUST: point dependencies at contracts (ports), not concrete implementations, at slice boundaries.
  • SHOULD: extend behavior by adding adapters or implementations at explicit ports, not by editing unrelated components.
  • SHOULD: when a component's code and contract disagree, correct the contract first, then regenerate the code — never silently patch the code into divergence from its contract.

Anti-patterns.

  • Spec-less components — components with no contract, so "correct" is undefined outside the current implementation.
  • Leaked internals as de-facto API — callers depending on implementation details never declared in the contract.
  • Spec drift — the contract and the code it describes have diverged and nothing detects it.
  • Distributed monolith coupling — components nominally separate but coupled through shared internal state or undocumented ordering.

Example.

// bad: caller depends on an implementation detail, not a contract
const total = order._internalLineItems.reduce(sum);

// good: caller depends on a declared contract
interface OrderTotals {
  total(order: Order): Money;
}
const total = orderTotals.total(order);

Inherits from. Open/closed principle, Liskov substitution principle, dependency inversion principle, Parnas information hiding. Rejects or weakens: the code-as-precious-artifact mindset.

D — Deterministic Verification

Statement. An agent never self-certifies. Every invariant is encoded in executable form: types, lint rules, tests, CI gates. LLM review is a complement with diverse lenses, never a substitute for deterministic gates.

Rationale. Studies report false-negative rates around 24% and false-positive rates up to 50% for LLM code review; in one reported case, a consensus of 80+ agent runs "confirmed" a nonexistent vulnerability, showing that consensus among samples from the same model is not independent evidence. Multi-agent review with genuinely different lenses does help — the CodeX-Verify study reported a 39.7 percentage point improvement in defect detection over single-pass review. Deterministic gates give the one signal that is reproducible regardless of which model or how many samples produced the code.

Rules.

  • MUST: express every stated invariant in executable form — a type, a lint rule, a test, or a CI gate. An invariant that cannot be checked deterministically is a documented risk, not a rule.
  • MUST NOT: let an agent merge or ship code on its own assessment; deterministic gates decide.
  • MUST: treat lint configuration as an executable architecture specification — import boundaries, naming rules, and layering enforced by lint, not by convention.
  • SHOULD: run LLM review as multiple passes with genuinely different lenses (correctness, security, contracts, performance), and triage its findings as hypotheses to verify, not verdicts.
  • SHOULD: fail fast — surface violations at the earliest gate (type check before test, test before review).

Anti-patterns.

  • Self-certification — an agent declaring its own change correct with no deterministic gate behind the claim.
  • Review-by-consensus-of-one-model — repeated sampling from the same model treated as independent confirmation.
  • Invariants living only in prose — a rule stated in documentation with no check that enforces it.
  • Flaky tests kept green by retries — a test whose failures are suppressed rather than fixed, defeating its purpose as a gate.

Example.

// bad: correctness asserted in a comment, never checked
// invariant: balance is never negative
function withdraw(balance, amount) { return balance - amount; }

// good: invariant encoded as an executable check
function withdraw(balance: NonNegative, amount: NonNegative): NonNegative {
  assertNonNegative(balance - amount);
  return balance - amount;
}

Inherits from. Design by contract, test-driven development, fail fast. Rejects or weakens: review-as-human-judgment-only.

E — Explicit Everything

Statement. No implicit invariants: explicit dependencies, typed interfaces, self-describing errors, no temporal coupling, no magic values. Boring explicit code beats clever compact code.

Rationale. Temporal coupling, magic strings, and undocumented formats are invisible to an agent that reads code in fragments rather than holding the whole system in mind; such implicit conventions are a leading source of agent-introduced defects. Pushing invariants into types, lint rules, and tests turns an implicit convention an agent might miss into a fact the agent cannot avoid seeing at the point of use.

Rules.

  • MUST: pass dependencies explicitly (parameters, constructor arguments), never through hidden globals or ambient state.
  • MUST: fully type interfaces; data crossing a boundary has a named, versioned shape.
  • MUST NOT: leave required ordering between operations as temporal coupling — enforce it through types or builders, or eliminate it.
  • MUST NOT: use magic values; give every literal with meaning a named, documented constant.
  • MUST: make errors self-describing — what failed, why, and what the caller can do, in grep-stable wording.
  • SHOULD: prefer boring, explicit, slightly longer code over clever compact code.

Anti-patterns.

  • Ambient context or globals — behavior depending on state not passed explicitly to the function that uses it.
  • Init-order landmines — code that only works if unrelated setup ran first, with nothing enforcing that order.
  • Boolean parameters with non-obvious meaning — a call site like save(true) whose meaning is not visible without reading the definition.
  • Error codes without messages — failures signaled by a bare code with no description of cause or remedy.
  • "Smart" defaults that vary by environment — behavior that silently changes depending on where the code runs.

Example.

// bad: magic value and boolean flag with hidden meaning
retry(fn, 3, true);

// good: explicit, named, self-describing
retry(fn, { maxAttempts: 3, retryOnTimeout: true });

Inherits from. "Explicit is better than implicit" (Zen of Python), interface segregation principle. Rejects or weakens: clever implicit conventions, temporal coupling.

N — Navigable Knowledge

Statement. Layered documentation with progressive disclosure: short hand-written context files, READMEs in significant directories, human-authored intent ("why"), specs versioned next to code. Knowledge needed for an edit lives at most one hop from the edit site.

Rationale. Model performance degrades over long sessions even within the advertised context window — a failure researchers, including Chroma's "context rot" study (2025), report as primarily a state-tracking problem rather than a reasoning one — so invariants must live in re-readable files, not in conversation memory. Context files research from ETH Zurich (2025) found that hand-written context files improved task success by about 4 percentage points, while autogenerated ones reduced it by about 3%; the same research reports a practical instruction budget of roughly 150-200 instructions, and compliance with a rule stated only in an instructions file of around 25-40%, versus approximately 95% when the same rule is enforced as a runtime hook or deterministic gate.

Rules.

  • MUST: keep a short hand-written context file at the repository root (target under approximately 200 lines / 150-200 instructions); autogenerated bulk dumps are forbidden.
  • MUST: give every significant directory a README stating its purpose, contracts, and links, so knowledge for an edit is at most one hop from the edit site.
  • MUST: author the "why" — intent, trade-offs, decision records — by a human; agents may draft it, but a human must approve it.
  • SHOULD: structure documentation with progressive disclosure — a summary layer that links down only where needed.
  • SHOULD: delete docs that only restate what the code already shows; documentation is subject to YAGNI.

Anti-patterns.

  • Encyclopedic autogenerated docs — bulk-generated documentation that degrades task success rather than improving it.
  • Stale wikis far from code — documentation that drifts because it lives outside the repository and outside the edit path.
  • Tribal knowledge in chat logs — decisions that exist only in conversation history, unreachable by an agent starting a new session.
  • Context files that grow monotonically — a root context file that accumulates instructions without pruning, exceeding the practical instruction budget.

Example.

// bad: intent exists only in a chat thread, not in the repo
// (no file, no README, no comment — decision is unrecoverable)

// good: intent captured next to the code it explains
// payments/README.md:
// "Retries are capped at 3 because the upstream gateway rate-limits
//  after the 4th attempt within 60s (see payments/CONTRACT.md)."

Inherits from. YAGNI applied to documentation, architecture decision record (ADR) practice. Rejects or weakens: exhaustive autogenerated documentation.

Mutual reinforcement

The six principles form a system rather than an independent checklist: G makes A findable — a slice organized by capability is only useful if an agent can locate it by name. A makes R affordable — small, bounded slices keep contracts small enough to state precisely. R makes D meaningful — a contract is what a deterministic gate verifies against. D makes E enforceable — explicitness becomes a lint rule or a type check rather than a convention to remember. E makes N cheaper — the less that is implicit, the less documentation has to explain. N makes G work at repository scale — naming conventions documented once let grep-first discovery hold as the codebase grows.