How I Build
2026-01-10
I maintain a reference system for building modern web applications — part documentation, part AI agent instruction set. Ruler aggregates rules from .ruler/*.md files and generates CLAUDE.md files that coding agents follow.
The canonical code principle: no backward compatibility, no deprecation warnings. When updating patterns, update ALL usage sites immediately. This sounds aggressive but it keeps the codebase honest — no cruft accumulates because "someone might need the old way."
Strict conventions: TypeScript with no any, Biome for formatting, battle-tested packages only (usehooks-ts, es-toolkit, dayjs, react-hook-form + zod). The rules are opinionated because opinions reduce decisions.
The Ruler workflow
Every project has a .ruler/ directory with markdown files — one per concern. code-style.md covers formatting and naming. react.md covers component patterns and hooks. nextjs.md covers routing and data fetching. testing.md covers what to test and how.
When I start a project or join a codebase, I drop in the ruler files that apply. They're modular — you don't need all of them. A pure API project skips the React and Tailwind rules. A static site skips the testing rules.
The key insight is that these files serve two audiences. Humans read them for onboarding and reference. AI agents read them as system instructions. The same document that tells a new developer "use for...of over forEach" tells Claude Code the exact same thing. One source of truth for how the codebase works.
What ends up in CLAUDE.md
The generated file is a concatenation of all the ruler files, structured so Claude can parse it efficiently. Each section starts with a clear scope, uses RFC 2119 terms (MUST, SHOULD, NEVER) for precision, and focuses on decisions rather than explanations.
The balance is comprehensive but scannable. Too short and the agent misses important patterns. Too long and it loses focus. I've found that 2,000–4,000 words covers most projects well — enough to encode the conventions without drowning in edge cases.
The rules evolve. Every time I find myself correcting Claude on the same pattern twice, I add a rule. Every time a rule causes friction without preventing real problems, I remove it. The system is alive — it reflects how I actually build, not how I theoretically want to build.