Instructions for AI agents working in this codebase. Read this before touching anything.
Also read: CLAUDE.md for commands and architecture, docs/WRITING.md for documentation voice, docs/PHILOSOPHY.md for the beliefs behind every decision.
This codebase follows a simple principle: simple is not easy, but it's the only thing that scales.
We take inspiration from 37signals, DHH, Jason Fried, and the Primeagen. Bias toward the boring solution. Complexity is a cost you pay with every future change. Don't add indirection that doesn't earn its keep. Don't build for hypothetical requirements. Don't make ten changes when one would do.
The four principles — principles over tools, progressive abstraction, craftsmanship, human experience first — are documented in full in docs/PHILOSOPHY.md. Read it once. It's the question to ask before you add anything.
A coherent unit is one logical change with a clear boundary. It has a name. It could be described in a single sentence. It has tests. It gets a commit. If it's non-trivial, it gets a branch and a PR.
A coherent unit is:
- A new feature (
feat) - A bug fix (
fix) - A refactor that doesn't change behavior (
refactor) - A dependency update + any required code changes (
chore) - A documentation improvement (
docs)
A coherent unit is NOT:
- A feature + an unrelated test fix + a renamed variable
- "All the things I noticed while looking at the file"
- Every change across the entire repo triggered by one small ask
When you find something adjacent that needs fixing, note it. Finish the current unit. Then address it separately.
Never commit directly to main or dev for anything beyond a typo fix. New work = new branch.
git checkout dev
git pull origin dev
git checkout -b <type>/<short-description>Branch naming:
| Type | Pattern | Example |
|---|---|---|
| New feature | feature/<name> |
feature/post-pagination |
| Bug fix | fix/<name> |
fix/session-cookie-expiry |
| Refactor | refactor/<name> |
refactor/error-handler-cleanup |
| Chore | chore/<name> |
chore/update-drizzle |
| Documentation | docs/<name> |
docs/better-auth-schema |
One branch = one coherent unit. Don't accumulate unrelated changes on a branch because they happen to be open at the same time.
Conventional commits. Always.
<type>(<scope>): <what changed>
<why it changed, if not obvious>
Types: feat, fix, refactor, chore, docs, test, perf
Scopes: backend, frontend, shared, db, auth, jobs, scripts, deps
Good:
feat(backend): add pagination to the posts list endpoint
Adds paginationQuery schema and paginate() helper from packages/shared.
Consistent with users list — same offset/limit convention.
Bad:
update stuff
fixed some things and also added the new feature and updated docs
wip
Commit size: one concern per commit. If the message needs "and", it's probably two commits. If the diff spans five unrelated files, stop and ask what the actual unit is.
Keep commits logical even on a feature branch — they tell the story of how you got there.
Tests are part of the work unit, not an afterthought.
- Write or update tests before committing — don't leave a commit that breaks the suite
- For new backend modules: at minimum, a scaffolded
handlers.test.tswith the first test passing - For logic that branches: test every branch
- For bug fixes: write the test that would have caught it first, then fix it
pnpm test # All tests
pnpm typecheck # TypeScript
pnpm lint # BiomeAll three must pass before you commit. CI will catch it anyway — save the round trip.
PR per coherent unit. Not per day. Not "everything I did this session". One unit, one PR.
Before opening a PR:
- All tests pass (
pnpm test) - No type errors (
pnpm typecheck) - No lint warnings (
pnpm lint) - Branch is rebased on latest
dev - Commit history tells a legible story
PR description must include:
## What
Brief description of the change. One to three sentences.
## Why
The user problem or technical need this solves. Link to issue if one exists.
## How
Key architectural or implementation decisions. What alternatives were considered.
## Testing
How to verify this works. Specific steps, not "it works".
## Notes
Anything a reviewer should know. Technical debt introduced. Follow-up work needed.PR size: reviewable in under 30 minutes. If the diff is 1000+ lines, it's probably two PRs. Split by layer or by phase of the work.
These are the failure modes to actively avoid:
Don't batch unrelated changes. If you're asked to add pagination to the posts endpoint, don't also fix the users handler, rename a variable you noticed, and update three docs files. Do the thing asked. Commit it. Note the rest.
Don't refactor while adding a feature. If the existing code is messy, open a refactor PR first, then build the feature on top of clean ground. Mixing the two makes both harder to review and harder to revert.
Don't create summary documents. Don't create a CHANGES.md, SUMMARY.md, or TODO.md unless explicitly asked. If you need to track state across a long task, use the task list in your head or ask the user.
Don't touch files you weren't asked to touch unless they're directly load-bearing for the change. Noticing something is not the same as being asked to fix it.
Don't leave the codebase in a half-done state. A partially implemented feature is worse than no feature — it creates confusion and merge conflicts. Either complete the unit or don't start it. If something is larger than expected, surface that before diving in.
The patterns are documented; use them. Don't invent new ones without documenting them.
Error handling: every function that can fail returns Result<T, E>. No throw in repository or use-case code. Infrastructure catches go through tryInfra. Domain errors are typed discriminated unions in *.errors.ts. See apps/backend/docs/DECISIONS.md.
Module structure: use the scaffolder. pnpm new:module <name> generates the correct file layout. The pattern is: route → handler → repository → error types → use-cases. See apps/backend/docs/ARCHITECTURE.md.
Frontend components: Base UI primitives wrapped with CVA variants. Design tokens from CSS custom properties (--color-primary, etc.). No hardcoded gray-* Tailwind classes. See apps/frontend/docs/DECISIONS.md.
Adding a Better Auth plugin: run schema generation before migrating. See the Better Auth section in apps/backend/docs/DECISIONS.md.
You're done with a unit when:
- The feature, fix, or refactor works as intended
- Tests cover the new behavior
- Lint and typecheck pass
- The commit message describes what changed and why
- The branch is clean and rebased
- The PR description would let a stranger understand the change in five minutes
Quality is not completeness. Shipping ten half-finished things is worse than shipping three finished ones. Finish what you start. Leave the codebase better than you found it.
- Engineering Philosophy — principles that anchor all decisions
- Engineering Playbook — practices and rituals
- Git Workflow — branching and commit conventions
- PR Guidelines — what a good PR looks like
- Orcta Workflow — end-to-end development workflow