DEV Community

Cover image for 10 Cursor Rules Every Developer Should Steal (.mdc)
NongdyZ
NongdyZ

Posted on

10 Cursor Rules Every Developer Should Steal (.mdc)

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/.

Quick refresher on .mdc frontmatter

Cursor'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.
  • Neither set — Cursor pulls it in when the 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.

1. The non-negotiable global rule

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.
Enter fullscreen mode Exit fullscreen mode

2. Kill 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.
Enter fullscreen mode Exit fullscreen mode

3. SQL safety, loaded on demand

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.
Enter fullscreen mode Exit fullscreen mode

4. Tests that target edges, not happy paths

---
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.
Enter fullscreen mode Exit fullscreen mode

The "edge cases first" line is what changes the output. Without it, Cursor writes the one test that proves the function runs and stops.

5. Commit messages a human can scan

---
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.
Enter fullscreen mode Exit fullscreen mode

6. Accessibility, scoped to the frontend

---
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.
Enter fullscreen mode Exit fullscreen mode

7. Error handling that surfaces, not swallows

---
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.
Enter fullscreen mode Exit fullscreen mode

8. Comments that explain "why"

---
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.
Enter fullscreen mode Exit fullscreen mode

9. Dependency restraint

---
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.
Enter fullscreen mode Exit fullscreen mode

10. The "stay in scope" rule

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.
Enter fullscreen mode Exit fullscreen mode

How to actually adopt these

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.

If you'd rather start from a tuned set

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.

Top comments (1)

Collapse
 
xm_dev_2026 profile image
Xiao Man

The insight about scoping rules is what makes this post so valuable. I made the exact mistake you described - loading too many rules and watching the model ignore all of them. Once I started scoping rules with globs instead of alwaysApply, the quality of generated code jumped noticeably. The SQL safety rule is especially underrated. Most devs do not think about teaching their AI assistant about parameterized queries until they see a generated query with string interpolation. Great list - bookmarked for reference!