Diff v1 → v2
v1: bot legacy · 2026-08-04T06:50:41Z
v2: bot legacy · 2026-08-04T06:57:12Z
# Engineering Rules > **When to use it:** always. These are the default rules for writing code on any > project — not a checklist to run at review time. Reach for the page when you need > the reasoning behind one of them, when a rule seems to conflict with a project > convention, or when onboarding a new repo or agent. Where a project's own > `CLAUDE.md` says something more specific, the project wins. Seven rules. They govern how code gets written, not just how it gets reviewed. - **Do not preserve backward compatibility.** Remove obsolete paths instead of adding compatibility layers, fallbacks, or migrations. - **Choose the simplest implementation that fully meets the current requirements.** Avoid speculative abstractions, configuration, and indirection. - **Grow the system in layers.** Start from the smallest version that works end to end, and add each new capability on top of a product that already works. Never trade a working product for unfinished complexity. - **Keep components modular and concerns clearly separated.** - **Prefer established, well-maintained libraries** when they reduce overall complexity or improve reliability. Do not reimplement common functionality without a clear reason. - **Lean on the dependencies already in the project** before writing your own implementation or adding packages. Do not assume a library lacks a capability without checking its documentation and types. - **Make architectural decisions for the long term.** Do not accept a stopgap that only works for now and is meant to be replaced later. ## Reading them correctly Each of these has a misreading that does real damage. The rules are short on purpose; the corrections are the part that stops them being harmful. **"Migrations" in rule 1 means compatibility shims** — dual-write paths, version branches, adapters kept alive for old callers. It does **not** mean database schema migrations. In any project with a migration tool, a schema change without one is a bug. This distinction is worth writing into the project's own rules file, because the literal reading is catastrophic and an agent has no way to guess which was meant. **Rules 2 and 7 are not in conflict.** Simplest-that-works is about *how much* you build now. Long-term architecture is about *which direction* you build in. You can satisfy both by building a small thing pointed the right way. An implementation shortcut with a named ceiling and an upgrade trigger is fine; a stopgap architecture chosen because the right one felt like work is not. **Rule 1 does not license breaking someone else's running system.** Deleting a path only this codebase calls is cleanup. Deleting a published API, wire format, or on-disk format that external callers depend on is a breaking change — that needs a decision from the owner, not a unilateral delete. The rule is about refusing to *accumulate* compatibility cruft, not about ignoring consumers who exist. **Rule 2 never applies to safety.** Input validation at trust boundaries, authorization, error handling that prevents data loss, and audit logging are not speculative — they are the requirement. Cut the abstraction, never the control. See [Security Baseline: NSM Grunnprinsipper & EU Regulation](/wiki/security-baseline-nsm-and-eu). ## The failure mode they prevent Eagerness, not laziness. Asked for a config loader, an agent returns a plugin system. Asked for one endpoint, it returns a service layer, a repository interface, and a `utils/` package. Everything runs, the tests pass, every line is individually defensible — and the feature is three times the size it needed to be. That cost compounds. Every unnecessary abstraction is something a human must read, something the next agent must hold in context, and something that can break. Before writing any new construct, stop at the first rung that answers the need: 1. Does this need to exist at all? 2. Does the codebase already have it? 3. Does the standard library? 4. Does the platform? 5. Does a dependency already installed? 6. Can it be one line? 7. Only then: write the minimum that works. Most over-building happens because rung 7 gets reached first. Full version in [[The Restraint Principle: YAGNI for Agents]]. - ## Where they live + ## Where to put them - - **Globally** — `~/.claude-joe/engineering-rules.md`, pulled into every session via an - `@engineering-rules.md` include in the global `CLAUDE.md`. Always loaded, because rules - that only apply when something triggers them are not rules. - - **Per project** — copied into the repo's own `CLAUDE.md` above the project specifics, - with any project-specific exceptions named explicitly next to them. An exception that + - **Globally** — in the agent's always-loaded instruction file, not in a skill or any + other doc that loads on a trigger. Rules that only apply when something activates them + are not rules. Most agent harnesses support including a separate file from the global + instructions, which keeps the rules editable in one place. + - **Per project** — in the repo's own `CLAUDE.md` (or equivalent), above the project + specifics so they read as primary rather than as one more section. Name any + project-specific exception explicitly next to the rule it breaks. An exception that isn't written down gets rediscovered as a bug. ## See also - [[The Restraint Principle: YAGNI for Agents]] — the decision ladder in full - [[Clean Architecture]] — the modularity and separation-of-concerns rule, worked out - [Security Baseline: NSM Grunnprinsipper & EU Regulation](/wiki/security-baseline-nsm-and-eu) — the controls simplicity never cuts - [[Trust but Verify]] — necessity as a review dimension, not just correctness