I used to think AI coding assistants plateaued because the models weren't good enough. Then I watched...
I used to think AI coding assistants plateaued because the models weren't good enough. Then I watched a teammate's Cursor produce cleaner code than mine on the same task, with the same model. The difference was his .cursor/rules/ folder. His assistant had a contract; mine was improvising.
A good Cursor rule isn't a motivational poster ("write clean code"). It's a specific, enforceable check scoped to when it matters. Below are ten rules I've stolen, refined, and now copy into every repo. Steal the ones that fit your stack. Each is a real .mdc file you can paste into .cursor/rules/.
.mdc frontmatterCursor's Project Rules live in .cursor/rules/*.mdc. Three frontmatter fields decide when a rule loads:
alwaysApply: true — in context on every request. Keep these short and universal.globs: "**/*.tsx" — auto-attaches only when a matching file is touched.description looks relevant.Scoping is the whole point. Ten rules loaded on every request dilute the context until the model ignores all of them. Ten scoped rules stay sharp because only the relevant two or three are ever present at once.
This is the only one I set to alwaysApply: true. Everything in it is a check the model can apply to a diff, not an aspiration.
---
description: Global engineering guardrails — always apply
globs:
alwaysApply: true
---
- Validate and type all external input (request bodies, query params, env) at the boundary.
- No secrets in code. Read from env. Never log tokens, passwords, or full PII.
- Functions that can fail return a typed error or throw — never return null silently.
- When you change a signature, update every caller in the same edit.
- Prefer deleting code over adding a flag. The best fix removes complexity.
any before it spreads (TypeScript)Left alone, Cursor reaches for any constantly — it's the path of least resistance. This glob-scoped rule forces it to think about the real type.
---
description: TypeScript type-safety rules
globs: "**/*.ts, **/*.tsx"
alwaysApply: false
---
- No `any`. Use `unknown` + a narrow, or define the type. If truly stuck, add `// TODO: type` with a reason.
- Components are typed function components with an explicit props interface. No `React.FC`.
- Exported functions have explicit return types. No relying on inference at module boundaries.
- Prefer discriminated unions over optional-everything objects.
No alwaysApply — it eats no context on your CSS work, but the description makes Cursor pull it in the moment you write a query.
---
description: Database and SQL safety — apply when writing queries or migrations
globs:
alwaysApply: false
---
- Always use parameterized queries / prepared statements. Never interpolate input into SQL.
- Any query that can return many rows is paginated or bounded with LIMIT.
- Watch for N+1: fetching a list then querying per item is a bug, not a style choice.
- Migrations are reversible and never run automatically — generate the file, let a human apply it.
- Select named columns, not `SELECT *`, in application queries.
---
description: Test-writing conventions — apply when adding or editing tests
globs: "**/*.test.*, **/*.spec.*"
alwaysApply: false
---
- Cover the edge cases first: empty input, null, boundary values, duplicate, and the error path.
- One behavior per test. The test name states the expected behavior, not the function name.
- No tests against private internals — test the public contract so refactors don't break them.
- Don't mock what you don't own without a thin wrapper around it.
The "edge cases first" line is what changes the output. Without it, Cursor writes the one test that proves the function runs and stops.
---
description: Git commit message format
globs:
alwaysApply: false
---
- Use Conventional Commits: type(scope): subject. Types: feat, fix, refactor, test, docs, chore.
- Subject is imperative and under 60 chars: "fix: handle empty cart on checkout".
- Body explains *why*, not *what* — the diff already shows what.
- One logical change per commit. Don't bundle a refactor with a feature.
---
description: Accessibility rules for UI
globs: "**/*.tsx, **/*.jsx, **/*.vue"
alwaysApply: false
---
- Every interactive element is keyboard-reachable and has an accessible name (label, aria-label, or text).
- Images have alt text; decorative images use alt="".
- Don't rely on color alone to convey state — pair it with text or an icon.
- Focus is visible and never trapped. Modals return focus to the trigger on close.
---
description: Error handling
globs:
alwaysApply: false
---
- Never catch an error just to log it and continue — either handle it meaningfully or rethrow.
- Error messages include enough context to debug without a repro (id, operation, input shape).
- User-facing errors are friendly; internal errors keep the stack. Never leak internals to the client.
- No empty catch blocks. Ever.
---
description: Comment discipline
globs:
alwaysApply: false
---
- Comment *why*, not *what*. If a comment restates the code, delete it and rename instead.
- Flag non-obvious decisions, workarounds, and "do not change this because…" with a reason.
- No commented-out code in commits. That's what git history is for.
---
description: Dependency policy
globs:
alwaysApply: false
---
- Don't add a dependency to solve what 15 lines of code can. State the tradeoff if you do add one.
- Prefer the platform/standard library before reaching for a package.
- No new dependency without checking it's maintained (recent commits) and has no known critical CVEs.
The one that saves the most cleanup time. Cursor loves to "helpfully" refactor three files you didn't ask about.
---
description: Scope discipline — always apply
globs:
alwaysApply: true
---
- Change only what the task requires. Don't reformat or refactor unrelated code in the same edit.
- If you spot an unrelated bug, mention it — don't fix it without asking.
- Keep diffs reviewable. A 400-line diff for a 2-line fix is a failure.
Don't paste all ten today — you'll dilute the context and burn out maintaining them. Start with three: rule #1 (global guardrails), one stack rule (#2 if you're on TypeScript), and rule #10 (scope discipline). Live with those for a week. Add another only when you catch Cursor making the same mistake twice. A rule earns its place by preventing a real, repeated error — not by sounding good in a blog post.
Curating and maintaining a rule library is its own small project. If you want a head start, I packaged my full tuned collection as the Cursor AI Power Pack — a set of ready-to-drop .mdc rules organized by stack and concern, plus prompt recipes and a START HERE guide so you know which rule to enable when. It's the same scoped, anti-fluff philosophy as the ten above, just done for you.
But you don't need it to get value here. Copy rules #1 and #10 into your repo right now — those two alone will make your next Cursor session noticeably tighter.
Which rule does your team enforce that I left off this list? Drop it in the comments — I'm always hunting for ones worth stealing.
cursorCursor Automations in 2026: wire event-triggered coding agents to Slack, CI, and...
aiThe specs exist. The AI just can't see them. I've always been the type who builds hobby...
amazonbedrockConnect Claude Code, Cursor and Codex to Amazon Bedrock's new console (2026) Summary. On 5...
aiThere is a weird uncanny valley with LLM-generated UI right now. The code functions perfectly, but if...
aiI went down a rabbit hole this morning reading the late-2025 Juejin AI roundups side by side, and the...
mcpInstall guide and config at curatedmcp.com Zendesk MCP: Let Claude Handle Your Support...
Workflows from the Neura Market marketplace related to this Cursor resource