TL;DR: Cursorrules best practices start with an inconvenient fact: Cursor's own help docs call the .cursorrules file legacy and say it will be deprecated. The current mechanism is .cursor/rules/*.mdc; a plain .md there is ignored. Below: what actually replaces it, a four-question test to run before you save any rule, and migration-ready .mdc templates for what people put in .cursorrules.
If you searched for cursorrules best practices, the first thing worth knowing is not a formatting tip. It is that the file is going away. Cursor's own customization help page states plainly that the .cursorrules file in your project root "is legacy and will be deprecated". It also gives a migration path off it. That is not a forum rumor: it is the vendor's current documentation, read directly on September 2, 2026, at cursor.com/help/customization/rules.
So the honest version of best practices for .cursorrules is: stop treating it as the target, and write your rules for what replaced it. Cursor's separate reference page, cursor.com/docs/rules, names the current mechanism as project rules: .mdc files inside .cursor/rules. It is explicit that the extension is not cosmetic — a plain .md file dropped in that folder "is ignored by the rules system", because it carries no frontmatter to declare when the rule should apply. Everything below is written for that mechanism. The old keyword stays in this title because that is still what people type into a search box, not because the old file still deserves the effort.
Is .cursorrules still read, or already dead?
Cursor does not say, and the honest answer is that nobody outside Cursor can tell you either. The help page's wording is future tense (it says only "will be deprecated"), which is consistent with the file still working today and equally consistent with support already quietly thinning out. The migration steps imply the file currently does something, but that is an inference from the fact that migrating it makes sense, not a documented statement about today's behavior.
The practical move is to stop trying to answer that question and make it irrelevant instead. Cursor's own four-step migration is short: create a new rule through the command palette, copy your .cursorrules content into it, set the type to Always Apply, which Cursor states "matches the old behavior", then delete the old file. Once it is deleted, whether Cursor was still reading it stops being your problem.
What actually replaced .cursorrules?
Project rules did: separate .mdc files inside .cursor/rules, each scoped by its own frontmatter instead of one blob that is always fully active. Side by side:
.cursorrules | .cursor/rules/*.mdc | |
|---|---|---|
| Extension | Any (plain text) | .mdc required; a .md file is ignored |
| Shape | One file, repo root | One file per concern, inside .cursor/rules/ |
| Scoping | None (the whole file loads every time) | alwaysApply, globs, or description decide |
| Frontmatter | None | Exactly three fields |
| Team distribution | Whatever is checked into git | Same, plus optional enforced Team Rules on paid plans |
| Status per Cursor | Legacy, will be deprecated | Current mechanism |
The row that actually explains most complaints about bloated rule files is Scoping. A single .cursorrules file has no way to say only when editing a test or only when the agent thinks it's relevant. Every line in it is active on every request, which is exactly the shape Cursor's own best practices warn against.
What makes a Cursor rule actually help?
This is the fact that actually decides whether a rule helps or just sits there burning context. Cursor's reference docs describe it directly: rule contents are "included at the start of the model context." There is no interpreter, no validator, no compliance check. It is text, prepended to the conversation, and it inherits every failure mode a typed prompt has. You just never see it fail, because you never watch it get sent.
So before a rule goes into .cursor/rules, run it through four questions. This is a pre-write test, not a cleanup pass. The goal is to stop a bad rule from ever reaching the repo, rather than noticing it later.
Would the model already do this unasked? Write clean code and think carefully cost lines and change nothing, because the model already believes it is doing that. Cursor's own best-practice list says the same thing more bluntly: "Avoid vague guidance. Write rules like clear internal docs". A rule earns its place only when it names something specific the model would otherwise get wrong: a directory, a banned import, a naming pattern, an order of operations.
Does it disagree with a rule you already have? Cursor merges rules rather than picking a winner between them, so two contradictory instructions do not error — they both load, and behavior varies session to session depending on which one the model happened to weight higher. Before adding a rule, grep .cursor/rules/ for the noun you are about to regulate. If something already touches it, you are about to create a coin flip, not a fix.
Does this need a rule type, or the default one? Cursor's own dropdown gives four choices for a reason: Always Apply, Apply to Specific Files, Apply Intelligently, Apply Manually. Defaulting everything to Always Apply is how a .cursorrules-shaped problem reappears inside .cursor/rules: one blob, just split across more filenames. Post 342 has the full interaction table for description, globs, and alwaysApply; the short version is that almost nothing you write should default to always-on.
What does this cost on requests it has nothing to do with? Cursor's guidance caps rules at "Keep rules under 500 lines" and separately tells you to "Split large rules into multiple, composable rules". That is a documented best practice, not an enforced ceiling. There is no token budget published anywhere, and nothing stops a 900-line file from saving. But a long always-applied rule is charged on every single request, including the ones it does nothing for. The 500-line figure is a signal about cost, and the real target for an always-on file is closer to twenty lines than five hundred.
Why do my Cursor rules seem to do nothing at all?
Before debugging frontmatter, confirm you are testing in the surface rules actually reach. Cursor's help page is direct: "Rules only apply to Agent (Chat)." Its reference documentation asks the same question in its own FAQ: "Do rules impact Cursor Tab or other AI features?" It answers: "No. Rules do not impact Cursor Tab or other AI features."
That single fact accounts for a large share of my rules are broken reports that have nothing wrong with the rule at all. Write a naming-convention rule, test it with an inline edit or a Tab completion, watch it get ignored, and the natural conclusion is that the rule failed. The rule was never in play: that keyboard shortcut does not read .cursor/rules at all. If you have confirmed you are in Agent chat and a rule still seems to do nothing, that is a real configuration problem, and post 300's diagnostic ladder works through it rung by rung, cheapest check first, rather than repeating that here.
Why do Cursor's two rules pages seem to disagree?
They don't disagree — one of them just documents less than the other. Cursor publishes two documents about rules, and they do not cover the same ground. The reference page, cursor.com/docs/rules, opens with "Cursor supports four types of rules" and lists Project Rules, User Rules, Team Rules, and AGENTS.md. Counted directly in that page's own markdown source, the string cursorrules appears zero times: the file is not mentioned at all, not even to warn you off it.
The help page, cursor.com/help/customization/rules, covers the same four mechanisms and adds three the reference page never names: the legacy .cursorrules file itself, on-disk user rule files at ~/.cursor/rules that the help page says "stay on the machine and do not sync", plus CLAUDE.md, which the help page states is "always applied to every conversation, regardless of any alwaysApply frontmatter setting." Counted the same way, cursorrules appears four times in that page's markdown.
Turn one sprawling .cursorrules into scoped .mdc files
This is the shape a real .cursorrules file tends to take once a team has been adding to it for a year: everything in one place, no way to tell what applies when, and half of it addressed to a part of the repo that has nothing to do with whatever you are currently editing.
# .cursorrules — repo root, no frontmatter, always fully active
Use pnpm for this repo. Never edit the lockfile by hand.
Imports use the @/ alias for anything under src/. No deep relative
imports like ../../../lib.
Every user-facing string must go through the i18n helper, not a bare
string literal in JSX.
Database migrations live in prisma/migrations/, numbered. Never edit
a migration that has already run against a real environment — add a
new one instead.
Never rename or drop a column in the same migration that adds
something new. Split it into two migrations.
Test framework is Vitest. One behavior per test, no snapshot tests
for anything a human has to read to review.
Commit messages follow Conventional Commits and reference the ticket
ID in the body, e.g. "Refs PROJ-482".
Always explain your reasoning before making a change, and ask before
adding a new dependency.
Every line in that file is true and worth keeping. The problem is that it all loads on every single request: the lockfile rule fires while you are writing a commit message, and the migration rule fires while you are styling a button. Split by what each line actually needs to be near, and the same content gets cheaper and more precise at the same time.
House style, the only file left alwaysApply: true. Keep this one small forever; everything in it is billed on every request.
---
alwaysApply: true
---
- Package manager is pnpm. Never hand-edit the lockfile — run the
install command and let it regenerate.
- Imports use the `@/` alias for anything under `src/`. Never emit a
deep relative import like `../../../lib`.
- Every user-facing string goes through the i18n helper. A bare
string literal in JSX is a bug, not a style preference.
- No new dependency without asking first — check `package.json`
before proposing one.
Database migrations, auto-attached by glob, so it costs nothing while you are nowhere near the schema.
---
globs: prisma/migrations/**, prisma/schema.prisma
alwaysApply: false
---
- Migrations are additive and numbered. Never edit a migration that
has already run against a real environment — write a new one.
- Never rename or drop a column, table, or constraint in the same
migration that adds something new. Split the change into two:
add first, remove only after every reader is updated.
- Existing rows must keep loading after the migration runs. If a new
column is required rather than optional, it needs a backfill step
in the same migration.
Test conventions, auto-attached by glob, scoped to test files only.
---
globs: "**/*.test.ts, **/*.test.tsx"
alwaysApply: false
---
- Test framework is Vitest. Do not introduce another one.
- One behavior per test. If a test name needs "and" to describe it,
split it into two tests.
- No snapshot test for anything a human has to actually read to
review — assert on the specific fields that matter instead.
Commit and PR conventions, agent-requested through a description, no glob, because it applies to a moment in the workflow rather than a file type.
---
description: How this repo writes commit messages and PR descriptions — format and required references
alwaysApply: false
---
- Commit messages follow Conventional Commits: a type, an optional
scope, then a short imperative summary.
- Reference the ticket ID in the body when one exists, e.g.
"Refs PROJ-482" — not in the summary line.
- One logical change per commit. If a diff mixes an unrelated
formatting pass with a real change, ask whether to split it before
committing.
Four files, each doing one job, none of them competing for attention on a request they have nothing to do with. That is the actual difference between real best practices and a longer version of the same blob: not better prose, narrower loading.
What does Cursor not document about its own rules system?
Three things, specifically — and guessing into any of them is how a rule set quietly relies on behavior nobody can promise will still be there next release. Cursor is unusually direct about what it publishes; it is equally direct, by omission, about these.
Whether an existing .cursorrules file is read today. Covered above: the fix is to stop needing the answer, not to find it.
The alwaysApply: false plus both description and globs combination. The reference page's own interaction table has no row for this. It documents globs alone, description alone, and neither together, but never both set at once, even though the UI lets you save exactly that. If you want to know what your own install actually does with it, you can test it empirically, though the result is a fact about your version today, not documentation:
---
description: CANARY — used only to test whether this description still pulls the rule in when globs is also set
globs: src/**/*.ts
alwaysApply: false
---
If this rule is active, start your reply with: rule-canary-0902
Ask Agent something about a file the glob does not match. If the canary still fires, the description is doing work alongside the glob. If it only fires on a matching file, the glob is the only thing loading it and the description is inert. Either way, do not build a rule set that depends on the answer. Split the concern into two rules with a single field each, which is documented, instead of one rule leaning on a combination Cursor has never described.
Nested .cursor/rules directories inside project subfolders. Both pages describe organizing files inside one .cursor/rules directory (the reference page's example shows a frontend/ folder holding a .mdc file), but neither describes a second .cursor/rules directory living inside, say, packages/api/. The help page's own advice, in fact, is to skip nesting: "Keep all rules in a flat .cursor/rules/ directory." What is documented for per-directory instructions is AGENTS.md. Cursor's reference states "Cursor supports AGENTS.md in the project root and subdirectories." Nested files are then combined with the parent directory's, with the more specific instruction taking precedence. If a monorepo genuinely needs different guidance per package today, that is the documented route, not a nested rules folder.
Where does an MCP server fit alongside your rules?
A rule and an MCP tool solve different problems, and it is worth being clear about which one you actually need before writing either. A rule is standing text: it changes behavior but cannot fetch anything, and it goes stale the moment the codebase moves past it. An MCP server is a live connection the agent can call.
Cursor is a listed client on our own MCP integration page, connected under Settings, MCP, with an OAuth sign-in on first use. We do not generate .cursor/rules files and there is no Prompt Architects extension for Cursor. What we expose instead are improve, refine, shorten, and enhance as callable tools. The division that holds up in practice: keep the conventions of this repo in a rule, checked into git alongside the code they describe. Keep anything you want available in every project, such as a prompt library or a rewrite step you run before a long task, behind a tool instead, because a tool follows you across repos and a rule does not.
Stop rewriting prompts. Start shipping.
Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.
Create An AccountShip the migration, then leave it alone
The actual best practice for .cursorrules is to stop maintaining it. Migrate what is in it into .cursor/rules/*.mdc, split by concern rather than by however the file happened to grow, and keep exactly one file set to always-apply: small enough that you would not mind rereading it today.
After that, the discipline is restraint, not addition. Run the four-question test above before any new rule gets saved, delete a rule the moment a linter or a type checker can enforce the same thing mechanically, and verify a rule actually loaded in Agent chat before concluding its wording needs work. If a rule seems to load and still gets ignored, that is a different problem with its own ladder — worked through in why Cursor ignores your rules file — and if you want the complete mechanism reference plus a from-scratch template set for a repo that has nothing yet, Cursor rules and prompt templates is the deeper page. This one was for the file you already have, and what to actually do with it.
Every mechanic above was verified directly against Cursor's own documentation at cursor.com/docs/rules and cursor.com/help/customization/rules on September 2, 2026. This system has been reorganized before, so if the app disagrees with what is written here, Cursor's own docs are the tiebreaker, not this page.