AI Dev
Lesson 3Rules: encode your standards once
Stop restating your conventions in every prompt — write them down where the agent picks them up automatically
By Lesson 2 you can write a precise prompt. The problem is that half of every precise prompt is the same each time:
...use TypeScript strict mode, no
any. Named exports only. Data fetching goes through theuseApihook, never raw fetch. Never interpolate user input into SQL...
That is not context about this task. It is a description of how your codebase works, and repeating it is both tedious and unreliable — the day you forget it is the day you get raw fetch and a string-interpolated query.
Rules fix this by moving standing instructions out of the prompt and into files the agent picks up on its own.
What a rule is
A Cursor rule is a .mdc file — Markdown with a YAML frontmatter block — living in .cursor/rules/.
---
globs: src/**/*.tsx
alwaysApply: false
---
# React conventions
- Functional components with hooks; no class components.
- Data fetching goes through the `useApi` hook, never raw fetch.
- Co-locate component, test, and styles.Two halves, and the frontmatter is the interesting one.
The body
Plain Markdown instructions. Short imperative bullets work best — this is guidance the model reads, not prose it admires.
The frontmatter
globs, alwaysApply, description — this is when the rule applies. It is the entire point.
Without the frontmatter you would be back to dumping every standard into every chat. With it, Cursor injects the right rule at the right time.
The four trigger types
| Type | Frontmatter | Fires when | Token cost |
|---|---|---|---|
| Always | alwaysApply: true | Every single request | Paid on every request — keep it short |
| Auto attached | globs: [...] | A file matching the glob is in context | Only when the pattern matches |
| Agent requested | description: ... | The agent reads the description and decides | Only when the agent pulls it in |
| Manual | none of the above | You type @rule-name | Only when you ask for it |
Agent requested is the one worth understanding properly. Setting a description with no globs means the agent sees a one-line summary of the rule and chooses whether to load the full text. That makes the description the most important line in the file — it is the only part the agent reads before deciding.
How Cursor decides
The right-hand branch is the payoff. A rule that does not fire is not merely inactive — it costs nothing. That is what makes it safe to have twenty rule files, and what makes one giant rules file a mistake: an always-on file pays its full token cost on every request, including the ones it has nothing to say about.
Worked examples
Three rules covering the three useful triggers.
Always-on — the project baseline. Keep this one tiny; you pay for it constantly.
---
alwaysApply: true
---
# Project base (keep this tiny)
- TypeScript strict mode; never use `any`.
- Named exports only, no default exports.
- All async code handles errors explicitly.Auto attached — conventions for a file type. Fires only when a matching file is in context.
---
globs: src/**/*.tsx
alwaysApply: false
---
# React conventions
- Functional components with hooks; no class components.
- Data fetching goes through the `useApi` hook, never raw fetch. See @src/hooks/useApi.ts
- Co-locate component, test, and styles.Agent requested — specialised guidance. No globs; the agent pulls it in when the description matches what it is doing.
---
description: Security standards for API routes, auth, and DB access
alwaysApply: false
---
# Security guardrails
- Never interpolate user input into SQL; parameterized queries only.
- Validate all request input with the shared Zod schemas.
- Never log secrets, tokens, or PII. Read secrets from env, never hardcode.
- Every mutating endpoint requires an authorization check.Note the @src/hooks/useApi.ts reference in the second example. A rule can point at a canonical implementation, which is far more reliable than describing the pattern in words.
Writing rules that work
Small, focused files — not one big one
typescript-standards.mdc, react-conventions.mdc, security.mdc. Each can then have its own trigger, which is the entire mechanism. A single file can only have one.
Keep always-on rules short
Every token in an alwaysApply: true file is spent on every request, including trivial ones. Reserve it for genuinely non-negotiable, project-wide constraints.
Write imperatives, not explanations
“Parameterized queries only” beats a paragraph on why SQL injection is bad. The model does not need convincing; it needs the constraint stated.
Point at real code
A rule that says “follow the pattern in @src/hooks/useApi.ts” stays accurate as the code evolves. A rule that describes the pattern in prose goes stale silently.
Encode security standards as rules, not just review checks
A rule steers the model while it generates. A review catches the problem afterwards. Both are worth having, but the first is cheaper.
AGENTS.md — the broad brief
.mdc rules answer “when editing this kind of file, do X.” Some things are not file-specific at all: how the repository is laid out, how to build and test it, what never to touch. That is what AGENTS.md is for — a single “README for agents” that is generally relevant regardless of what is being edited.
Put in it:
- How the repo is laid out — the map, and which directory owns what
- How to build, test and run — the exact commands
- Non-negotiables — “don't touch X”, “prefer Y”
- Where the docs live
| AGENTS.md | .cursor/rules/*.mdc | |
|---|---|---|
| Shape | One broad document, or a few | Many small rules |
| Scoping | Whole-project | By glob, or always, or on request |
| Best for | “How this repo works” | “When editing TS, do X” |
| Portability | High — a common convention across tools | Cursor-native |
They are complements, not alternatives. AGENTS.md carries the project brief; .mdc files carry the conditional detail.
Planning documents are not agent policy
A third artefact often gets confused with the first two: your own plan — a roadmap, a status page, a list of decisions and priorities. Perhaps a plan.html you open in a browser.
That is for you, not for the agent. It tracks what is being done. Rules describe how to behave while coding. Mixing them produces a rules file full of stale task lists and a plan nobody reads.
| What it is | Where it goes | Who reads it |
|---|---|---|
| Task plan, roadmap, status, decisions | plan.html | You — or the agent, when you @ it deliberately |
| Stable, project-wide agent instructions | AGENTS.md | The agent, generally |
| File-type and domain conventions | .cursor/rules/*.mdc | The agent, conditionally |
You do not need AGENTS.md to replace your planning document. Keep planning in whatever format you find readable; use rules only when you want the agent to inherit conventions without you pasting them each time.
User Rules — personal and global
Cursor also has User Rules, set in settings rather than in the repository. They apply to every project you open and travel with your account, not your team.
Belongs in User Rules
- How you like explanations pitched
- Preferred response length and tone
- “Show me the diff before applying”
- Personal editor and workflow habits
Belongs in the repo instead
- Architectural constraints
- Security standards
- Framework and library conventions
- Anything a teammate also needs
The test is simple: would a new teammate need this to work correctly? If yes, it belongs in the repository where they will actually get it. Personal preferences in a team repo are noise; team standards in your personal settings are invisible to everyone else.
The whole landscape
Cursor is not the only tool reading instruction files, and the same repository often carries several. Each has a distinct scope and a distinct token-cost profile.
| File pattern | Tool | Scope & trigger | Primary function | Token cost |
|---|---|---|---|---|
| ~/.claude/settings.json | Claude Code | Global user | Personal formatting preferences and base hooks | Minimal static footprint |
| CLAUDE.md (root) | Claude Code | Repository root | Stack setup, build commands, core constraints | Keep well under ~60 lines |
| CLAUDE.md (subdirectory) | Claude Code | Directory path | Path-specific domain logic and API schemas | Loaded on entering that path |
| .mdc — alwaysApply: true | Cursor | Global project | Non-negotiable architectural mandates | Permanent — paid every request |
| .mdc — globs: [...] | Cursor | Pattern match | Language or framework-specific rules | Only when a matching file is open |
| .mdc — alwaysApply: false | Cursor | Semantic match | Specialised edge-case workflows or refactors | Retrieved when the agent judges it relevant |
The column that matters is the last one. Instruction files are not free, and the discipline is the same across every tool: make the expensive ones small and the specific ones conditional.
Common mistakes
| Mistake | What goes wrong |
|---|---|
| One large always-on rules file | Every request pays for guidance it does not need, and nothing can be scoped |
| Vague descriptions on agent-requested rules | The agent has nothing to match against, so the rule loads inconsistently |
| Describing a pattern in prose instead of linking the file | The description drifts out of date and nobody notices |
| Task lists inside rule files | Stale work items get injected as if they were standards |
| Team standards in personal User Rules | Your code follows them, your teammates’ does not, and reviews get inconsistent |
| Rules nobody revisits | The codebase moves on and the agent keeps enforcing last year’s conventions |
Recap
Lesson 4 turns to the other side of the contract: what to hunt for in a diff before it reaches main.
Check your understanding
1 / 10What does the YAML frontmatter in an .mdc file control?