Rules Without 'Why' Become Arbitrary Mandates
A rule without rationale: 'Use the Result pattern for error handling.' The developer thinks: 'Why? What is wrong with try-catch? This feels arbitrary.' They resist or override. A rule with rationale: 'Use the Result pattern for error handling. Why: try-catch in service functions can swallow errors when multiple services are composed — a caught error in service A prevents service B from knowing the operation failed. The Result pattern makes error states explicit and composable.' The developer: understands the reasoning, appreciates the problem it solves, and follows the rule willingly.
Rationale benefits the AI too: when the AI encounters an ambiguous situation (should this function use the Result pattern or try-catch?), the rationale helps it decide. The rule says Result pattern. The rationale says why: composable error states across service boundaries. The AI: applies the Result pattern in service-to-service code (where the rationale applies) and may reasonably use try-catch in a standalone script (where composition is not a concern). The rationale: helps the AI apply the rule intelligently, not mechanically.
Rationale benefits the team long-term: after 12 months, nobody remembers why a rule was added. A new team member: sees the rule and has no context. Without rationale: they accept it blindly (fragile) or question it endlessly (time-consuming). With rationale: they understand the reasoning and either agree (fast adoption) or challenge it with a specific counter-argument (productive discussion). AI rule: 'Rationale transforms rules from commands into explanations. Commands are followed grudgingly. Explanations are followed willingly.'
Step 1: The What-Why-When Format
Every rule: three parts. What (the convention — what to do): 'Use async/await for all asynchronous operations.' Why (the reasoning — why this convention exists): 'Consistent async patterns reduce cognitive load when reading code across the codebase. Mixing .then() chains with async/await creates inconsistency that requires mental context-switching.' When (the scope — where and when this applies): 'All new code. Refactor existing .then() chains to async/await when modifying those files.' The what: tells the AI what code to generate. The why: helps the AI decide in edge cases. The when: tells the AI where the rule applies.
The why categories: preventing bugs (the rule prevents a specific class of bugs — 'no floating-point for currency prevents rounding errors'), improving consistency (the rule makes the codebase consistent — 'consistent naming reduces cognitive load when reading code from different authors'), compliance requirement (the rule satisfies a specific regulation — 'audit logging satisfies HIPAA 164.312(b)'), and team preference (the rule reflects a team decision — 'named exports over default exports — decided by team vote in Q1 2026'). The category: helps the AI understand the rule's weight. A bug-prevention rule: stronger than a team preference.
Where to write the rationale: inline with the rule (recommended for rules files). Format: the rule text, followed by the rationale in a slightly different format. Example: '- Error handling: use Result<T, E> pattern for business logic functions.\n Why: try-catch swallows errors at service boundaries. Result makes error states explicit.\n Scope: src/services/ and src/repositories/. Express middleware: use try-catch with next(error).' The indented why and scope: clearly associated with the rule but visually distinct from the rule text itself. AI rule: 'Inline rationale: keeps the why next to the what. The AI reads both together. Separate rationale documents: get outdated and disconnected from the rules they explain.'
Rule: 'Use Result pattern for error handling.' Rationale: 'for composable error propagation across service boundaries.' The AI encounters: a standalone script that does not compose with other services. The rationale: tells the AI the rule's purpose is service composition. The script: does not compose. The AI: may reasonably use try-catch for the standalone script (the rationale does not apply). Without rationale: the AI applies the rule mechanically everywhere. With rationale: the AI applies it intelligently where it matters.
Step 2: Writing Good Rationale
Good rationale: specific and concrete. 'Why: try-catch in service functions can swallow errors when service A calls service B — if A catches the error, B never knows the operation failed.' The reader: can evaluate the reasoning because it is specific. Bad rationale: vague and generic. 'Why: better code quality.' The reader: cannot evaluate what 'better' means or why this rule specifically improves quality. The test: could someone disagree with the rationale? If yes: it is specific (disagreement requires a specific counter-argument). If no: it is vague (nobody disagrees with 'better code quality').
Rationale length: 1-2 sentences for most rules. The rationale: should be shorter than the rule itself. If the rationale is longer than the rule: the rule might be trying to do too much (split it), or the rationale includes unnecessary detail (trim to the core reasoning). Exception: rationale for rules with compliance implications — these may reference specific regulatory sections and need more detail.
When to skip rationale: self-evident rules do not need rationale. 'Use camelCase for variable names': every JavaScript developer knows why (it is the language convention). 'No secrets in source code': the reason is obvious (security). For these: the rule text is sufficient. Add rationale when: the rule is surprising (different from what the AI would do by default), non-obvious (the reasoning requires domain knowledge), or potentially controversial (reasonable people might disagree). AI rule: 'Rationale for non-obvious rules. No rationale needed for universal conventions. The test: would a new team member ask why? If yes: add rationale.'
Vague: 'Use named exports instead of default exports. Why: better code quality.' — every rule claims to improve quality. The developer: cannot evaluate whether this is true. Specific: 'Use named exports instead of default exports. Why: named exports enable precise renaming during refactoring (find-and-replace works on the exact name) and improve tree-shaking (bundlers can eliminate unused named exports more reliably).' The developer: can evaluate these specific claims and decide if they agree.
Step 3: Keeping Rationale Current
Rationale decay: the rule is updated but the rationale is not. The rule now says 'use Drizzle for database access.' The rationale still says 'Prisma provides the best TypeScript integration.' The rationale: contradicts the rule. A reader: confused. The AI: confused. When updating a rule: always update the rationale. If the reason for the rule changed (migrated from Prisma to Drizzle because of serverless compatibility): the rationale should explain the new reasoning.
Rationale in the quarterly review: during the quarterly review, for each rule that is evaluated: is the rationale still accurate? Does the original reason still apply? If the codebase has changed (no longer using service-to-service composition): the rationale for the Result pattern may no longer apply. The rule: might be revised or kept for consistency. But the rationale: should reflect the current reasoning, not the historical reason.
Version-tracking rationale: in the changelog, include the rationale for each change. 'v2.5.0: Changed error handling from try-catch to Result pattern. Rationale: service composition made error swallowing a recurring production issue. The Result pattern makes error propagation explicit.' The changelog: preserves the historical reasoning. The rules file: reflects the current reasoning. Both: important for understanding the rules' evolution. AI rule: 'The rules file has the current rationale. The changelog has the historical rationale. Together: the complete story of why each rule exists and how it evolved.'
A new developer reads the rules. Rule without rationale: 'No default exports.' The developer: 'Why? My previous team used default exports and it was fine.' They push back or silently ignore. Rule with rationale: 'No default exports. Named exports enable precise refactoring and better tree-shaking.' The developer: 'That makes sense — my previous team had issues with renaming default exports during refactoring.' They follow the rule because the reasoning convinced them, not because the rule file told them to.
Rule Rationale Summary
Summary of documenting why each AI rule exists.
- Value: rationale turns commands into explanations. Developers follow willingly, not grudgingly
- AI benefit: rationale helps the AI apply rules intelligently in ambiguous situations
- Format: what (the convention) + why (1-2 sentences of reasoning) + when (scope of applicability)
- Why categories: bug prevention (strongest), consistency, compliance, team preference
- Inline: rationale next to the rule in the rules file. Indented for visual distinction
- Quality test: could someone disagree with the rationale? Specific = yes. Vague = no = rewrite
- Skip for: self-evident rules (camelCase, no secrets). Add for: surprising, non-obvious, controversial rules
- Maintenance: update rationale when the rule changes. Quarterly review: verify rationale still applies