12 Rules for Claude.md (Example) A developer shared a detailed CLAUDE.md configuration file for an iOS app called Trailmark, which is backed by a Node.js REST API. The file establishes strict coding rules, including type safety, naming conventions, and a test-driven development loop, to guide AI assistants in maintaining the project. | CLAUDE.md | | | Trailmark — an iOS app for logging hikes, backed by a Node.js REST API. | | | - ios/ — SwiftUI app, iOS 17+, native components only | | | - api/ — Node 22, TypeScript, Express, Postgres 16 Kysely | | | - packages/contracts/ — zod schemas + generated OpenAPI. Single source of truth for both sides. Change the contract first, then the server, then the client. | | | Nested rules live in api/CLAUDE.md and ios/CLAUDE.md . Longer workflows release, migration, security review are skills in .claude/skills/ — do not paste them here. | | | Ask before you assume | | | Never guess at intent. If a task leaves anything open — which screen, which endpoint, what happens on failure, whether it needs a migration, whether this is user-facing — stop and ask. One question up front is cheaper than half a day of work in the wrong direction. | | | - Ask when the request could reasonably mean two different things. | | | - Ask before changing a public API shape, a DB schema, or anything in packages/contracts/ . | | | - Do not invent product decisions, copy, or acceptance criteria. | | | - Do not widen scope past what was asked. Note the adjacent thing you spotted; don't fix it unprompted. | | | - If you had to assume something you couldn't resolve, list it explicitly at the top of your summary. | | | The loop | | | Every change runs through this. A task is not done until it is green. | | | bash | | | npm run check tsc --noEmit, eslint, prettier --check | | | npm test vitest, unit + integration | | | npm run test:api supertest against a throwaway Postgres | | | npm run db:migrate never edit an applied migration, always add a new one | | | | | | bash | | | cd ios && xcodegen && xcodebuild test -scheme Trailmark -destination 'platform=iOS Simulator,name=iPhone 17' | | | cd ios && swiftlint --strict | | | | | | Rules for the loop: | | | - Write the failing test first. Watch it fail for the right reason, then make it pass. | | | - Run npm run check && npm test after every meaningful edit, not once at the end. | | | - Never report success on a red loop. Never disable, skip, or .only a test to get to green. | | | - If a test is wrong, say so and explain why before changing it. | | | - Do not start long-running processes npm run dev , expo start to "verify" — they never exit. Use the commands above. | | | Type checking | | | Both languages are strict. Type errors are not warnings. | | | tsconfig.json — these stay on: | | | json | | | { | | | "compilerOptions": { | | | "strict": true, | | | "noImplicitAny": true, | | | "noUncheckedIndexedAccess": true, | | | "exactOptionalPropertyTypes": true, | | | "noImplicitOverride": true, | | | "verbatimModuleSyntax": true | | | } | | | } | | | | | | - No any . If a type is genuinely unknown, use unknown and narrow it. | | | - No as to escape an error, and no non-null . Fix the type or narrow properly. | | | - No @ts-expect-error without a comment naming the upstream issue. | | | - All external input request bodies, query params, API responses is parsed with a zod schema from packages/contracts/ . Types are inferred from the schema, never hand-written alongside it. | | | - Swift: no force unwraps, no try , no as . Use guard let , typed throws , and Result at boundaries. | | | Respect these from the start. Do not write loose code and correct it after the type check fails. | | | Naming | | | Consistency is for the model as much as for us. Pick the existing word, don't coin a new one. | | | Domain vocabulary — one word per concept: | | | | Concept | Use | Never | | | | | --------------- | ---------------------- | ----------------------------- | | | | | A recorded walk | hike | trip, walk, activity, session | | | | | The GPS line | track | route, path, trace | | | | | A saved place | waypoint | pin, marker, poi | | | | | Account access | Sign in / Sign out | Login, Log In, Log out | | | | Code: | | | - Functions: createHike , getHike , listHikes , updateHike , deleteHike . Not fetch , remove , save , handle . | | | - Booleans read as assertions: isSyncing , hasTrack , canEdit . | | | - Routes: kebab-case, plural nouns — GET /v1/hikes/:hikeId/waypoints . | | | - Postgres: snake case tables and columns, plural tables — hikes , waypoints , started at . | | | - Swift types are UpperCamelCase and the file is named after the type: HikeDetailView.swift . | | | - SwiftUI views end in View , observable state ends in Store : HikeListView , HikeStore . | | | - Test files sit beside the source: hikes.service.ts → hikes.service.test.ts . | | | User-facing copy: sentence case for buttons and labels "Save hike", not "Save Hike" . Copy strings live in ios/Trailmark/Resources/Localizable.strings — no string literals in views. | | | Project structure | | | | | | api/ | | | src/ | | | modules/