<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NongdyZ</title>
    <description>The latest articles on DEV Community by NongdyZ (@nongdyz_d9c3069b1acb2a08c).</description>
    <link>https://dev.to/nongdyz_d9c3069b1acb2a08c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4005348%2F445dafed-1b33-4340-8386-43a33db1c0fe.jpg</url>
      <title>DEV Community: NongdyZ</title>
      <link>https://dev.to/nongdyz_d9c3069b1acb2a08c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nongdyz_d9c3069b1acb2a08c"/>
    <language>en</language>
    <item>
      <title>Cursor Rules That Actually Improve AI Output (.mdc Project Rules)</title>
      <dc:creator>NongdyZ</dc:creator>
      <pubDate>Sat, 27 Jun 2026 19:06:17 +0000</pubDate>
      <link>https://dev.to/nongdyz_d9c3069b1acb2a08c/cursor-rules-that-actually-improve-ai-output-mdc-project-rules-50cg</link>
      <guid>https://dev.to/nongdyz_d9c3069b1acb2a08c/cursor-rules-that-actually-improve-ai-output-mdc-project-rules-50cg</guid>
      <description>&lt;p&gt;Most Cursor rules I see online are wish lists. "Write clean code." "Follow best practices." "Be concise." Cursor reads them, nods politely, and ships exactly the same code it would have without them.&lt;/p&gt;

&lt;p&gt;The reason is simple: vague rules give the model nothing to act on. A rule only changes output if it's &lt;strong&gt;specific, enforceable, and scoped to when it matters.&lt;/strong&gt; After rewriting my rules around those three properties, Cursor went from "code that works but I'd reject in review" to "code that passes review the first time" on most requests.&lt;/p&gt;

&lt;p&gt;Here's what actually moved the needle, with real &lt;code&gt;.mdc&lt;/code&gt; rules you can paste in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The format: &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;, not &lt;code&gt;.cursorrules&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Cursor has moved to &lt;strong&gt;Project Rules&lt;/strong&gt; stored as &lt;code&gt;.mdc&lt;/code&gt; files in &lt;code&gt;.cursor/rules/&lt;/code&gt;. The big advantage over the old single &lt;code&gt;.cursorrules&lt;/code&gt; file is &lt;strong&gt;scoping&lt;/strong&gt;: each rule has frontmatter that controls &lt;em&gt;when&lt;/em&gt; it loads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rules&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code"&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three frontmatter fields decide everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;alwaysApply: true&lt;/code&gt; — the rule is in context on every request. Use for short, universal rules (security, error handling).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;globs: "**/*.tsx"&lt;/code&gt; — the rule auto-attaches only when a matching file is in play. Use for stack-specific rules so your React rules don't pollute your Python context.&lt;/li&gt;
&lt;li&gt;Neither set — the agent pulls the rule in &lt;em&gt;when it judges it relevant&lt;/em&gt;, based on the &lt;code&gt;description&lt;/code&gt;. Use for situational best-practice rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This scoping is the whole game. Loading every rule on every request dilutes the context and the model starts ignoring all of them. Scoped rules stay sharp because only the relevant ones are present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 1: A global rule the model can actually enforce
&lt;/h2&gt;

&lt;p&gt;Keep your always-on rule short and written as concrete checks, not aspirations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Global engineering rules — always apply&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; Validate and type all external input (API bodies, query params, env vars) at the boundary.
&lt;span class="p"&gt;-&lt;/span&gt; Every function that can fail returns a typed error or throws — never returns null silently.
&lt;span class="p"&gt;-&lt;/span&gt; No secrets in code. Read from env. Never log tokens, passwords, or full PII.
&lt;span class="p"&gt;-&lt;/span&gt; When you change a function's signature, update every caller in the same change.
&lt;span class="p"&gt;-&lt;/span&gt; Prefer deleting code over adding flags. The best fix removes complexity.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there's no "write clean code." Every line is a check the model can apply to a specific diff. "Validate input at the boundary" produces a &lt;code&gt;zod&lt;/code&gt; schema; "write clean code" produces nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 2: A stack rule scoped by glob
&lt;/h2&gt;

&lt;p&gt;This one only loads when a TypeScript/React file is involved, so it never interferes with your backend or scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;React + TypeScript conventions&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.tsx,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;**/*.ts"&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; Components are typed function components. No &lt;span class="sb"&gt;`React.FC`&lt;/span&gt;. Props via an explicit interface.
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`any`&lt;/span&gt;. Use &lt;span class="sb"&gt;`unknown`&lt;/span&gt; + a narrow, or define the type. If you truly can't, add a &lt;span class="sb"&gt;`// TODO: type`&lt;/span&gt; and explain.
&lt;span class="p"&gt;-&lt;/span&gt; Follow the Rules of Hooks: no hooks in conditions or loops. Effects list every dependency they read.
&lt;span class="p"&gt;-&lt;/span&gt; Data fetching goes through the existing query layer, not raw &lt;span class="sb"&gt;`fetch`&lt;/span&gt; inside components.
&lt;span class="p"&gt;-&lt;/span&gt; Every interactive element is keyboard-accessible and has an accessible name (label, aria-label, or text).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;no any&lt;/code&gt; rule is the one people underrate. Left alone, Cursor reaches for &lt;code&gt;any&lt;/code&gt; constantly because it's the path of least resistance. One scoped line removes most of it and forces it to think about the actual type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 3: A best-practice rule the agent loads on demand
&lt;/h2&gt;

&lt;p&gt;Some rules only matter sometimes — like database access. Leave &lt;code&gt;alwaysApply&lt;/code&gt; off and write a clear &lt;code&gt;description&lt;/code&gt; so the agent pulls it in when it touches SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Database and SQL safety — apply when writing queries or migrations&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;-&lt;/span&gt; Always use parameterized queries / prepared statements. Never interpolate user input into SQL.
&lt;span class="p"&gt;-&lt;/span&gt; Every query that can return many rows is paginated or explicitly bounded with LIMIT.
&lt;span class="p"&gt;-&lt;/span&gt; Add an index before shipping a query that filters or joins on a non-indexed column — call it out.
&lt;span class="p"&gt;-&lt;/span&gt; Migrations are reversible and never run automatically. Generate the file; let the human apply it.
&lt;span class="p"&gt;-&lt;/span&gt; Watch for N+1: fetching a list then querying per item is a bug, not a style choice.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this rule isn't always loaded, it doesn't eat context on your CSS work. But the moment you ask Cursor to write a query, its description makes it relevant and it gets pulled in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The before/after that convinced me
&lt;/h2&gt;

&lt;p&gt;Same request — "add an endpoint to fetch a user's orders" — with and without the rules above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without rules:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM orders WHERE user_id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL injection, no input validation, no pagination, &lt;code&gt;SELECT *&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the rules:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, total, status, created_at FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameterized, validated, paginated, explicit columns. Same model, same prompt. The rules did that.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start without overdoing it
&lt;/h2&gt;

&lt;p&gt;Don't write twenty rules on day one — you'll dilute the context and burn out maintaining them. Start with exactly three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One short &lt;code&gt;alwaysApply&lt;/code&gt; global rule (security + error handling).&lt;/li&gt;
&lt;li&gt;One glob-scoped rule for your main stack.&lt;/li&gt;
&lt;li&gt;One on-demand rule for whatever bites you most (SQL, accessibility, tests).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Live with those for a week, then add a rule only when you catch Cursor making the same mistake twice. Rules earn their place by preventing a real, repeated error.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want a full, tuned AI-coding setup
&lt;/h2&gt;

&lt;p&gt;Rules are one half of the picture. The other half is giving your AI assistant a real workflow — focused review/test/refactor steps instead of one generalist doing everything. I packaged my complete setup as &lt;strong&gt;&lt;a href="https://planfulnest.gumroad.com/l/womhu?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=cursor-rules-that-actually-work" rel="noopener noreferrer"&gt;Claude Code Agent OS&lt;/a&gt;&lt;/strong&gt;: 26 specialized subagents, 12 slash commands, ready-to-edit &lt;code&gt;CLAUDE.md&lt;/code&gt; templates, MCP configs, and safety hooks — the same scoped, anti-fluff philosophy as the rules above, applied to the whole coding loop. It works alongside Cursor and any editor.&lt;/p&gt;

&lt;p&gt;That said, the three-rule starter in this post costs nothing and will improve your very next Cursor session. Build those first, then decide if you want the full setup.&lt;/p&gt;

&lt;p&gt;What's the one mistake Cursor keeps making in your codebase? Tell me in the comments and I'll suggest a rule for it.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Set Up Claude Code with 26 Production Subagents (CLAUDE.md, MCP, Hooks)</title>
      <dc:creator>NongdyZ</dc:creator>
      <pubDate>Sat, 27 Jun 2026 13:54:39 +0000</pubDate>
      <link>https://dev.to/nongdyz_d9c3069b1acb2a08c/how-i-set-up-claude-code-with-26-production-subagents-claudemd-mcp-hooks-3jij</link>
      <guid>https://dev.to/nongdyz_d9c3069b1acb2a08c/how-i-set-up-claude-code-with-26-production-subagents-claudemd-mcp-hooks-3jij</guid>
      <description>&lt;p&gt;When I first opened Claude Code on a real project, it felt like hiring a brilliant junior who had read every book but never shipped anything. It could write code fast, but it would forget my conventions halfway through a session, run a migration without asking, and "fix" a bug by deleting the failing test.&lt;/p&gt;

&lt;p&gt;After a few weeks of tuning, it now behaves like a small senior team: it plans before it codes, reviews its own work, writes tests, and refuses to run anything destructive. The difference wasn't a better model. It was a better &lt;strong&gt;setup&lt;/strong&gt; — a &lt;code&gt;CLAUDE.md&lt;/code&gt;, a set of focused subagents, a couple of MCP servers, and two small hooks.&lt;/p&gt;

&lt;p&gt;Here is exactly how that setup works, so you can build your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. CLAUDE.md is the contract, not a wishlist
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the file Claude Code reads automatically at the start of every session. Most people treat it like a README. It works far better as a &lt;strong&gt;contract&lt;/strong&gt;: short, specific, and written in always/never terms the agent can't misread.&lt;/p&gt;

&lt;p&gt;The two rules that gave me the biggest jump in quality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Build &amp;amp; test (run these, do not guess)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Install:  pnpm install
&lt;span class="p"&gt;-&lt;/span&gt; Dev:      pnpm dev
&lt;span class="p"&gt;-&lt;/span&gt; Test:     pnpm test -- --run
&lt;span class="p"&gt;-&lt;/span&gt; Lint:     pnpm lint
&lt;span class="p"&gt;-&lt;/span&gt; Typecheck: pnpm typecheck

&lt;span class="gu"&gt;## Always&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`pnpm typecheck &amp;amp;&amp;amp; pnpm test -- --run`&lt;/span&gt; before saying a task is done.
&lt;span class="p"&gt;-&lt;/span&gt; Use parameterized queries. Never build SQL with string concatenation.
&lt;span class="p"&gt;-&lt;/span&gt; When you change a public function, update its tests in the same edit.

&lt;span class="gu"&gt;## Never&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never run &lt;span class="sb"&gt;`git push`&lt;/span&gt;, &lt;span class="sb"&gt;`git reset --hard`&lt;/span&gt;, or any DB migration without me confirming.
&lt;span class="p"&gt;-&lt;/span&gt; Never add a dependency to fix something a few lines of code can solve.
&lt;span class="p"&gt;-&lt;/span&gt; Never disable a failing test to make the suite pass.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason the "Build &amp;amp; test" block matters: without the exact commands, the agent &lt;strong&gt;guesses&lt;/strong&gt; them (&lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;yarn test&lt;/code&gt;, &lt;code&gt;make test&lt;/code&gt;) and wastes turns failing. Give it the real commands once and it stops guessing.&lt;/p&gt;

&lt;p&gt;Keep this file under ~50 lines. A long &lt;code&gt;CLAUDE.md&lt;/code&gt; gets diluted in the context and the agent starts ignoring the middle of it. Be ruthless.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Subagents: split one generalist into many specialists
&lt;/h2&gt;

&lt;p&gt;A single agent juggling "review this, also write tests, also refactor" produces mediocre output at every task. Subagents fix this by giving each job its own focused instructions and its own context window. In Claude Code these live in &lt;code&gt;.claude/agents/*.md&lt;/code&gt; and you invoke them by name.&lt;/p&gt;

&lt;p&gt;A subagent is just a Markdown file with frontmatter and a system prompt. Here's a real one — my &lt;code&gt;code-reviewer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-reviewer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reviews&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;diff&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bugs,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;issues,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;convention&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;violations.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;after&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;writing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code."&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Grep, Bash&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

You are a senior code reviewer. Review ONLY the changed lines plus their immediate context.

Output exactly three sections:
&lt;span class="p"&gt;1.&lt;/span&gt; &lt;span class="gs"&gt;**Blocking**&lt;/span&gt; — bugs, security holes, data loss risks. Each with file:line and a fix.
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Should fix**&lt;/span&gt; — correctness or clarity issues that aren't blocking.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Nits**&lt;/span&gt; — style/naming. Keep this short.

Rules:
&lt;span class="p"&gt;-&lt;/span&gt; If there are no blocking issues, say so on line one. Do not invent problems to look thorough.
&lt;span class="p"&gt;-&lt;/span&gt; Check: injection, missing error handling, N+1 queries, unhandled null, race conditions.
&lt;span class="p"&gt;-&lt;/span&gt; Quote the exact line you're flagging. No vague "consider improving error handling".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two lines that make it useful are &lt;em&gt;"Do not invent problems to look thorough"&lt;/em&gt; and &lt;em&gt;"Quote the exact line."&lt;/em&gt; Without them, reviewers either rubber-stamp everything or generate a wall of generic advice. With them, you get actionable output.&lt;/p&gt;

&lt;p&gt;I run a roster of focused specialists this way — a &lt;code&gt;debugger&lt;/code&gt; that reproduces before it theorizes, a &lt;code&gt;test-writer&lt;/code&gt; that targets edge cases instead of happy paths, a &lt;code&gt;security-auditor&lt;/code&gt; that thinks in OWASP categories, an &lt;code&gt;api-designer&lt;/code&gt;, a &lt;code&gt;refactor-architect&lt;/code&gt;, and so on. Each one is a small file with a tight output format. The pattern is always the same: &lt;strong&gt;one job, one output format, one anti-fluff constraint.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. MCP: give the agent eyes and memory
&lt;/h2&gt;

&lt;p&gt;MCP (Model Context Protocol) servers are how Claude Code reaches outside the chat — the filesystem, a browser, a persistent memory store. You configure them in &lt;code&gt;.mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-filesystem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sequential-thinking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-sequential-thinking"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The one I'd add first is &lt;strong&gt;sequential-thinking&lt;/strong&gt;. It gives the agent a scratchpad to break a problem into steps before acting, which noticeably reduces the "confidently wrong" answers on multi-step tasks. A browser MCP (Playwright) is the second I reach for, because it lets the agent actually load the page and read the console instead of guessing why the UI broke.&lt;/p&gt;

&lt;p&gt;One caution: MCP servers move fast and some get abandoned. Pin to maintained ones and check they still install before you rely on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Hooks: the safety net that runs without asking
&lt;/h2&gt;

&lt;p&gt;Hooks are shell commands Claude Code runs on events you choose. Two are worth setting up on day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-format on save&lt;/strong&gt; — so you never review a diff full of whitespace noise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm prettier --write &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_FILE_PATHS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Block dangerous commands&lt;/strong&gt; — a pre-execution guard that refuses destructive shell calls even if the agent tries them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/guard.sh — exit non-zero to block the command&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_TOOL_INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf /|git reset --hard|DROP TABLE'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: destructive command refused by guard hook."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the difference between trusting an autonomous agent and babysitting it. The model can be wrong; the hook is deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together: a feature, start to finish
&lt;/h2&gt;

&lt;p&gt;With the setup above, my actual workflow on a new feature is short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the agent to &lt;strong&gt;plan&lt;/strong&gt; — it writes the steps before touching code.&lt;/li&gt;
&lt;li&gt;It scaffolds and implements, formatting on save via the hook.&lt;/li&gt;
&lt;li&gt;I invoke &lt;code&gt;code-reviewer&lt;/code&gt; on the diff, then &lt;code&gt;test-writer&lt;/code&gt; for the gaps it found.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pnpm typecheck &amp;amp;&amp;amp; pnpm test&lt;/code&gt; runs because &lt;code&gt;CLAUDE.md&lt;/code&gt; says it must.&lt;/li&gt;
&lt;li&gt;It drafts the commit and PR description.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent does the typing. I do the deciding. That's the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the whole thing as a drop-in?
&lt;/h2&gt;

&lt;p&gt;Building all of this from scratch is a real time sink — the 26 subagents alone took me weeks of tuning to get the output formats right. If you'd rather skip that and drop a finished setup into any repo, I packaged my exact configuration as &lt;strong&gt;&lt;a href="https://planfulnest.gumroad.com/l/womhu" rel="noopener noreferrer"&gt;Claude Code Agent OS&lt;/a&gt;&lt;/strong&gt;: 26 specialized subagents, 12 slash commands (&lt;code&gt;/plan&lt;/code&gt;, &lt;code&gt;/review&lt;/code&gt;, &lt;code&gt;/test&lt;/code&gt;, &lt;code&gt;/ship&lt;/code&gt;...), 5 ready-to-edit &lt;code&gt;CLAUDE.md&lt;/code&gt; templates, the MCP configs, and the safety + format hooks (bash and PowerShell) — with a START HERE quickstart that gets you running in about 5 minutes.&lt;/p&gt;

&lt;p&gt;But you don't need it to get value from this post. Start with a tight &lt;code&gt;CLAUDE.md&lt;/code&gt; and one &lt;code&gt;code-reviewer&lt;/code&gt; subagent today; that pair alone will change how your next commit feels.&lt;/p&gt;

&lt;p&gt;If you build your own subagent roster, I'd genuinely like to see which specialists you find most useful — drop them in the comments.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
