When One Size Does Not Fit All
Some rules apply universally: 'never hardcode secrets' applies to every file in every directory. Other rules depend on context: frontend components use functional patterns, but backend services may use classes. API routes need input validation, but internal utility functions do not. Test files use mocking patterns that production code should never use. Conditional rules: apply different patterns based on the context, making the AI's behavior intelligent rather than uniform.
The conditional rule format: 'When [context], do [pattern]. Otherwise, do [default pattern].' Example: 'When working in src/routes/: validate all request inputs with Zod schemas. When working in src/utils/: no input validation needed (these are internal functions called with validated data).' The AI: reads the context (the file path, the import patterns, the function signature) and applies the appropriate rule.
Common conditions: file path (src/routes/ vs src/utils/), file extension (.test.ts vs .ts), framework layer (controller vs service vs repository), data sensitivity (endpoints handling PII vs public data), and environment (server-side code vs client-side code). Each condition: triggers a different set of patterns. AI rule: 'Conditional rules make the AI context-aware. Without them: the AI applies the same patterns everywhere, which is correct for some rules (security) but wrong for others (testing patterns in production code).'
Step 1: Common Conditional Patterns
Directory-based conditions: 'Files in src/routes/: include Zod input validation, structured error responses, and authentication middleware. Files in src/services/: business logic only, no HTTP-specific code, use Result pattern for errors. Files in src/repositories/: database access only, use the ORM query builder, return typed data.' This pattern: mirrors your project's architecture. The AI: generates code appropriate for each architectural layer.
File-type conditions: 'In .test.ts files: use Vitest, describe/it naming, and mock external dependencies. In .ts files (non-test): never import from vitest or testing libraries. In .tsx files: use React functional components, Tailwind classes, and import from the design system.' The file extension: is a reliable signal for the code's purpose. The AI: adjusts its patterns based on whether it is generating tests, utilities, or components.
Framework-layer conditions: 'React Server Components (default in app/ directory): async functions, direct database access, no useState/useEffect. React Client Components (files with "use client"): hooks, event handlers, browser APIs. Server Actions (functions marked "use server"): form handlers, data mutations, server-side validation.' This pattern: encodes the Next.js App Router's component model as conditional rules. AI rule: 'Framework-layer conditions are the most impactful conditional rules. They prevent the AI from applying one layer's patterns to another layer.'
Your project architecture: routes (HTTP handling) โ services (business logic) โ repositories (data access). Your conditional rules: match this architecture exactly. Routes: validation, auth, structured responses. Services: Result pattern, business rules, no HTTP dependencies. Repositories: ORM queries, typed returns, no business logic. The AI generates code appropriate for each layer because the conditions mirror the architectural boundaries.
Step 2: Writing Effective Conditional Rules
The if-when-then format: 'When [condition is met]: [do this]. When [opposite condition]: [do this instead]. Default: [what to do when neither condition is clear].' Example: 'When the function handles user input (API routes, form handlers): validate with Zod before processing. When the function is internal (called only by other functions with validated data): no validation needed. Default: if unsure whether input is validated, add validation (safe by default).' The default: tells the AI what to do when the context is ambiguous.
Specificity in conditions: vague condition: 'For important functions, add logging.' (What is important? The AI guesses.) Specific condition: 'For functions in src/routes/ (API handlers): add structured logging with requestId, userId, and response status. For functions in src/services/ (business logic): add logging only for error paths. For functions in src/utils/: no logging (utility functions are called frequently, logging would be noisy).' The specific conditions: leave no room for guessing.
Avoiding condition proliferation: too many conditions make the rule file complex and hard to maintain. Rule of thumb: if a condition applies to more than 3 rules, it should be a section heading (not an inline condition). Example: instead of 5 rules each starting with 'When working in src/routes/:', create a section '### API Route Rules' with 5 rules underneath. The section heading: is the condition. The rules: are the patterns. AI rule: 'When a condition repeats across 3+ rules: create a section for it. The section heading is the condition. This reduces repetition and improves readability.'
Conditional rules without a default: 'When in src/routes/: add validation. When in src/services/: use Result pattern.' The developer asks: 'Create a middleware function in src/middleware/.' Neither condition applies. The AI: guesses. With a default: 'Default: if the function's location does not match a specific condition, treat as an API boundary and add validation (safe by default).' The AI: follows the safe default. The developer: overrides if needed. Always safer than guessing.
Step 3: Testing Conditional Rules
Test each condition separately: for directory-based conditions, use test prompts that specify the file location. Prompt 1: 'Create a new API route handler in src/routes/users.ts.' Expected: Zod validation, structured response, auth middleware. Prompt 2: 'Create a new utility function in src/utils/format-date.ts.' Expected: no validation, no HTTP-specific code. If both prompts generate appropriate code: the conditional rules work.
Test the default behavior: prompt without specifying a directory. 'Create a function that processes a user's order.' The AI: should follow the default behavior (safe by default โ add validation since the context is ambiguous). If the AI generates code without validation: the default rule is not clear enough. Strengthen the default: 'Default: if the function's context is unclear, treat it as an API boundary and add input validation.'
Edge cases: test prompts that fall between conditions. 'Create a middleware function in src/middleware/auth.ts.' Middleware: is not a route handler, not a service, not a utility. Which condition applies? If none: the default should guide the AI. If you find that common code locations are not covered by any condition: add a condition or update the default. AI rule: 'Test edge cases that fall between conditions. These reveal: gaps in your conditional rules that the AI fills with generic patterns instead of your preferred patterns.'
Rule 1: 'When in src/routes/: validate with Zod.' Rule 2: 'When in src/routes/: add auth middleware.' Rule 3: 'When in src/routes/: return structured JSON responses.' Rule 4: 'When in src/routes/: log request and response.' Better: '### API Route Rules (src/routes/)\n- Validate all inputs with Zod schemas\n- Require authentication middleware\n- Return structured JSON responses\n- Log request/response with requestId.' The section heading IS the condition. The rules: are clean bullets without repetition.
Conditional Rules Summary
Summary of writing conditional AI rules.
- Purpose: different patterns for different contexts. One size does not fit all code
- Common conditions: directory (routes vs services), file type (.test.ts vs .ts), framework layer (Server vs Client Component)
- Format: When [condition]: [pattern]. When [opposite]: [pattern]. Default: [safe fallback]
- Specificity: specific conditions ('files in src/routes/') not vague ('important functions')
- Avoid proliferation: 3+ rules with the same condition โ create a section with that condition as heading
- Defaults: always specify. Tell the AI what to do when context is ambiguous. Safe by default
- Testing: one prompt per condition. Test defaults. Test edge cases between conditions
- Impact: conditional rules are the most sophisticated rule type โ they make the AI context-aware