Why 'Don't' Rules Are Highly Effective
Positive rules tell the AI what to do: 'Use async/await for asynchronous operations.' Negative rules tell the AI what not to do: 'Never use .then() chains — use async/await instead.' Both communicate the same convention. But negative rules: are often more effective for preventing specific anti-patterns because the AI has been trained on vast amounts of code that uses the anti-pattern. Without an explicit prohibition: the AI may generate the anti-pattern because it has seen it thousands of times in training data.
The most impactful negative rules: prevent patterns that are common in public code but wrong for your project. Common public code patterns that may not fit your project: console.log for logging (your project uses a structured logger), throw for error handling (your project uses a Result pattern), any type in TypeScript (your project requires explicit types), default exports (your project uses named exports), and class components in React (your project uses functional components). Each of these: the AI will generate by default unless explicitly prohibited.
The format that works: every negative rule pairs the prohibition with the positive alternative. Not just 'Don't use X.' Instead: 'Don't use X. Instead: use Y because Z.' The AI needs: something to do instead. A bare prohibition without an alternative: the AI knows what not to do but not what to do. It may choose a third option that is also wrong. AI rule: 'Every negative rule needs a positive alternative. The prohibition prevents the wrong pattern. The alternative provides the right one.'
Step 1: The Anti-Pattern + Alternative Format
The format: 'NEVER: [the anti-pattern with a brief example]. INSTEAD: [the correct pattern with a brief example]. WHY: [the reason — helps the AI make correct decisions in ambiguous cases].' Example: 'NEVER: use any type in TypeScript (const data: any = response.json()). INSTEAD: use explicit types or unknown with type narrowing (const data: unknown = response.json(); if (isUserResponse(data)) { ... }). WHY: any disables type checking, hiding bugs that TypeScript should catch at compile time.'
Concise format for simple prohibitions: 'No console.log — use logger from @/lib/logger. No default exports — use named exports. No var — use const (or let when reassignment is needed). No class components — use functional components with hooks.' Each prohibition: one line, the anti-pattern and the alternative. The concise format: works for rules where the prohibition and alternative are self-explanatory. The detailed format (NEVER/INSTEAD/WHY): works for rules that need explanation.
Library-specific prohibitions: 'Do not import from lodash (the entire library). Instead: import specific functions (import { debounce } from lodash/debounce) or use native alternatives. Do not use moment.js — use date-fns or Intl.DateTimeFormat. Do not use request or axios for internal services — use the organization's HTTP client that includes retry, timeout, and tracing.' These rules: prevent the AI from introducing large, deprecated, or non-standard dependencies. AI rule: 'Library prohibitions prevent the AI from importing packages you have decided not to use. Without them: the AI defaults to the most popular package, which may not be your team's choice.'
'Don't use any.' OK — but what should the AI use instead? Without an alternative: the AI knows what not to do but guesses what to do. It might choose: unknown (good), Object (bad), or a generic type parameter (depends). With an alternative: 'Don't use any. Use unknown for truly unknown types, then narrow with type guards.' The AI: knows both what to avoid AND what to use. No guessing. No wrong alternatives.
Step 2: The 10 Most Impactful Prohibition Rules
Prohibition 1: 'No any type. Use unknown for truly unknown types, then narrow with type guards.' Prohibition 2: 'No console.log in production code. Use the structured logger.' Prohibition 3: 'No string concatenation for SQL queries. Use parameterized queries.' Prohibition 4: 'No secrets (API keys, passwords) in source code. Use environment variables.' Prohibition 5: 'No floating-point for currency. Use integer cents or a decimal library.'
Prohibition 6: 'No default exports. Use named exports (easier to rename, better for tree shaking).' Prohibition 7: 'No synchronous file system operations. Use async/promises.' Prohibition 8: 'No bare catch blocks that swallow errors. Always log or re-throw.' Prohibition 9: 'No mutable global state. Use module-scoped constants or dependency injection.' Prohibition 10: 'No TODO comments in merged code. Resolve before merging or create a tracked issue.'
These 10 prohibitions: prevent the most common issues in AI-generated code across all tech stacks. Each one: addresses a pattern that the AI frequently generates because it is common in public code but inappropriate for production projects. AI rule: 'Start with these 10. Add project-specific prohibitions as you discover anti-patterns the AI generates for your codebase.'
The AI's training data: millions of code files. console.log appears in almost all of them. When you ask: 'Add debugging output to this function' — the AI's strongest instinct: console.log. Without a prohibition: the AI follows its training. With a prohibition: 'NEVER console.log. Use logger from @/lib/logger.' The prohibition: overrides the training data's influence. This is why negative rules are so important — they counteract the AI's default patterns that come from public code, not your project's conventions.
Step 3: Testing That the AI Avoids the Pattern
Prohibition testing: use prompts that would naturally trigger the prohibited pattern. To test 'No console.log': prompt: 'Add debugging output to this function.' Expected: the AI uses the structured logger, not console.log. To test 'No any type': prompt: 'Parse the API response and extract the user data.' Expected: the AI uses explicit types or unknown with type guards, not any. If the AI generates the prohibited pattern: the rule is not strong enough. Strengthen with more explicit language and examples.
The strength spectrum: weak prohibition: 'Prefer not to use console.log.' (The AI treats this as a preference, not a requirement.) Medium: 'Do not use console.log in production code.' (Clear, but 'production code' is ambiguous.) Strong: 'NEVER use console.log. Use logger from @/lib/logger for all output. console.log is stripped in production builds and provides no structured data for monitoring.' (Explicit, with an alternative and a reason.) AI rule: 'Use strong prohibitions for critical patterns (security, data integrity). Medium for conventions. Avoid weak prohibitions — they are suggestions, not rules.'
Override tracking for prohibitions: if developers consistently override a prohibition: investigate. The override: may indicate the prohibition is too broad (it covers cases where the prohibited pattern is actually correct) or the alternative is not practical (the suggested replacement does not work for certain use cases). Revise the prohibition: add an exception clause if needed. AI rule: 'A prohibition with a high override rate: needs an exception clause, not stricter enforcement. Example: No console.log — EXCEPT in build scripts and CLI tools where structured logging is unnecessary.'
Prohibition: 'No console.log.' Override rate: 25%. Investigation: developers override in build scripts and CLI tools where the structured logger is overkill. The prohibition: too broad. Fix: 'No console.log in application code (src/). Exception: build scripts (scripts/) and CLI tools (cli/) may use console.log directly.' The override rate: drops to 3% (only genuine edge cases). The exception clause: acknowledges that some contexts are different. The prohibition: remains strong where it matters.
Negative Rules Summary
Summary of writing effective prohibition AI rules.
- Format: NEVER [anti-pattern]. INSTEAD [alternative]. WHY [reason]. Always pair prohibition with alternative
- Concise: 'No X — use Y.' One line for self-explanatory prohibitions
- Library prohibitions: prevent importing large, deprecated, or non-standard packages
- Top 10: no any, no console.log, no SQL concatenation, no hardcoded secrets, no float for currency, no default exports, no sync file ops, no bare catch, no mutable globals, no TODOs in merged code
- Testing: prompt that would trigger the anti-pattern. Verify the AI uses the alternative instead
- Strength: NEVER > do not > prefer not to. Strong for security. Medium for conventions
- Override tracking: high override rate = prohibition needs an exception clause
- Exceptions: add when the prohibition is too broad for certain valid use cases