Engineering Rules

History

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.

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 to put them

  • 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