DEV Community

Cover image for How I Stopped Cursor AI from Ruining My Code Style (and How You Can Too)
davidTang
davidTang

Posted on

How I Stopped Cursor AI from Ruining My Code Style (and How You Can Too)

I've been using Cursor as my primary editor for about a month now — working full-time on a Next.js 14 project with TypeScript, Tailwind CSS, and a fairly opinionated component architecture.

The AI capabilities are genuinely impressive. But after the honeymoon phase, I started noticing a pattern that was quietly eating my productivity.

The Problem Nobody Warns You About

The AI doesn't know your coding standards. And it doesn't care.

My project has clear conventions: functional components, named exports, Tailwind for all styling, strict TypeScript with zero tolerance for any, and early returns to keep nesting shallow.

But every time I asked Cursor to generate a component, help me refactor, or scaffold a new page, the output was a coin flip:

  • Sometimes export default function, sometimes export function — no consistency
  • Inline style={{ color: 'red' }} showing up in a Tailwind-only codebase
  • any sprinkled around like confetti
  • Pages Router patterns (getServerSideProps) sneaking into an App Router project
  • Class components appearing in 2026

None of these are "wrong" in isolation. But in the context of my project, every single one creates friction. I was spending 20-30% of my time just reformatting AI output to match conventions.

That's not AI-assisted development. That's AI-generated tech debt.

Discovering .cursorrules

I stumbled onto the solution while browsing GitHub repos one evening. Several open-source projects had a .cursorrules file sitting in their root directory — something I'd never noticed before.

The concept is simple: you create a plain text file called .cursorrules in your project root, write your coding standards in natural language, and Cursor's AI will respect them across all interactions — completions, chat, Composer, everything.

Think of it as onboarding documentation, but for your AI pair programmer instead of a new hire.

Here's a stripped-down example of what mine looks like:

You are a senior TypeScript developer working on a production Next.js application.

Tech Stack:
- TypeScript 5 with strict mode enabled
- React 18 with functional components only
- Next.js 14 using the App Router (NOT Pages Router)
- Tailwind CSS for all styling — no exceptions

Code Style Rules:
- Always use named exports. Never use default exports.
- Prefer early returns to reduce nesting. Max nesting depth: 2 levels.
- Never use the `any` type. Use proper interfaces, generics, or `unknown`.
- Use descriptive variable names. No single-letter variables except in short lambdas.
- Prefer `const` over `let`. Never use `var`.
- Always define proper TypeScript interfaces for component props.

File Structure:
- React components → /components
- Utility functions → /lib
- Type definitions → /types
- API routes → /app/api
Enter fullscreen mode Exit fullscreen mode

After dropping this into my project root, the difference was immediate and dramatic. The AI-generated code went from "needs significant cleanup" to "basically ready to commit."

Why Writing Good Rules Is Harder Than You Think

So I had the concept down. But getting the rules right? That took way more iteration than I expected.

Problem 1: Too vague = useless

My first attempt included gems like "write clean, maintainable code" and "follow best practices." The AI nodded politely and continued doing whatever it wanted. These instructions are the equivalent of telling a new developer to "just write good code" — technically correct, practically worthless.

Problem 2: Too verbose = also useless

My second attempt was a 150-line manifesto covering every edge case I could think of. The AI started ignoring chunks of it because there was too much to process. Rules files have an effective attention window, and flooding it with noise drowns out the signal.

Problem 3: Stack-specific nuances are tricky

The difference between "use React" and "use React 18 with functional components, hooks, and the App Router pattern" is enormous. The AI treats vague stack references as permission to use any version's patterns. I was getting componentDidMount mixed with useEffect in the same conversation.

Problem 4: Phrasing matters more than you'd think

"Don't use any" is weaker than "Never use the any type. For unknown types, use unknown and narrow with type guards." The more explicit and actionable the instruction, the better the compliance.

I spent probably 4-5 hours across two weeks refining my rules file. Effective? Yes. Fun? Not remotely.

A Faster Path

Around iteration six of my hand-crafted rules file, a colleague pointed me to an online generator that takes a completely different approach: instead of writing rules in prose, you select your tech stack and preferences from a visual interface, and it outputs an optimized rules file.

The tool is at ittoolshq.com/en/cursorrules-generator

What made it click for me:

Granular style controls. It's not just "React yes/no." You can specify individual code style preferences — functional components, named exports, early returns, strict TypeScript — at a very fine-grained level. These are exactly the kinds of rules that make the biggest practical difference.

Multi-editor support. I use Cursor primarily, but I've been experimenting with Windsurf on a side project. This tool generates the right format for each:

  • Cursor → .cursorrules
  • Windsurf → .windsurfrules
  • Cline → .clinerules or AGENTS.md

Sensible defaults. The generated output isn't just a list of "do this, don't do that." It structures the rules in a way that models respond well to — role framing, grouped directives, clear prohibitions.

The workflow is: open the page → check the boxes that match your stack → copy the output → paste into your project root → done. Took me about 2 minutes versus the hours I spent writing mine manually.

The Before and After

Let me show you the actual difference in AI output quality.

Before .cursorrules — asking Cursor to create a user profile card:

import React, { useState, useEffect } from 'react';

export default function UserProfile(props: any) {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`/api/users/${props.id}`)
      .then(res => res.json())
      .then(data => {
        setUserData(data);
        setLoading(false);
      });
  }, [props.id]);

  if (loading) {
    return <div style={{ textAlign: 'center', padding: '20px' }}>Loading...</div>;
  }

  return (
    <div style={{ border: '1px solid #ccc', borderRadius: '8px', padding: '16px' }}>
      <img src={userData.avatar} style={{ width: '64px', borderRadius: '50%' }} />
      <h2 style={{ margin: '8px 0' }}>{userData.name}</h2>
      <p style={{ color: '#666' }}>{userData.email}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Issues: default export, any props, inline styles everywhere, no TypeScript types, no error handling, no loading skeleton, untyped state.

After .cursorrules — the exact same prompt:

import { useState, useEffect } from 'react'

interface UserProfileProps {
  userId: string
}

interface UserData {
  avatar: string
  name: string
  email: string
}

export function UserProfile({ userId }: UserProfileProps) {
  const [user, setUser] = useState<UserData | null>(null)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const res = await fetch(`/api/users/${userId}`)
        if (!res.ok) throw new Error('Failed to fetch user')
        const data: UserData = await res.json()
        setUser(data)
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error')
      }
    }
    fetchUser()
  }, [userId])

  if (error) return <div className="text-center p-5 text-red-500">{error}</div>
  if (!user) return <UserProfileSkeleton />

  return (
    <div className="border border-gray-200 rounded-lg p-4">
      <img
        src={user.avatar}
        alt={`${user.name}'s avatar`}
        className="w-16 h-16 rounded-full"
      />
      <h2 className="mt-2 text-lg font-semibold">{user.name}</h2>
      <p className="text-gray-500">{user.email}</p>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Same AI. Same prompt. Same model. The only difference is a config file in the project root.

Named export. Proper interfaces. Tailwind classes. Early returns. Error handling. Typed state. Destructured props. No any in sight.

The 5 Rules That Made the Biggest Impact

After weeks of experimentation, these are the specific rules that produced the most noticeable improvement in output quality:

1. "Never use any. Use unknown for truly unknown types and create proper interfaces for everything else."

This single rule eliminated roughly 90% of my type-safety fixes. Before this rule, the AI would default to any whenever the type wasn't immediately obvious. After — it actually takes the time to define interfaces.

2. "Use Next.js 14 App Router exclusively. Never use getServerSideProps, getStaticProps, or any Pages Router API."

Next.js has been around long enough that most training data includes Pages Router patterns. Without this explicit prohibition, the AI would regularly mix paradigms, which is especially confusing for newer developers on the team who might not recognize the mismatch.

3. "All styling must use Tailwind CSS utility classes. Never use inline styles, CSS modules, or styled-components."

This eliminated the most common source of inconsistency in generated code. The AI has strong tendencies toward inline styles (probably because they're self-contained and don't require imports), so you need to be very explicit about prohibiting them.

4. "Prefer early returns to reduce nesting. Maximum nesting depth is 2 levels."

This one surprised me with how well it works. The AI actually counts nesting levels and restructures logic with guard clauses. Code readability improved significantly.

5. "When creating new files, follow this directory structure: components in /components, utilities in /lib, type definitions in /types."

Subtle but powerful. When you ask the AI to create a new utility function, it'll suggest putting it in /lib instead of dropping it in a random location. Small thing, but it keeps the project organized as it grows.

Practical Tips for Your Own Setup

A few things I've learned that might save you some trial and error:

Start minimal. Begin with 5-10 core rules that address your biggest pain points. You can always add more later. A focused rules file outperforms a comprehensive one.

Be explicit about what NOT to do. "Use Tailwind" is good. "Use Tailwind. Never use inline styles or CSS-in-JS" is better. The model responds more reliably to clear prohibitions.

Include framework version numbers. "React 18" and "Next.js 14 App Router" give the model much better context than just "React" and "Next.js."

Update your rules as your project evolves. I revisit mine roughly every two weeks. As new patterns emerge in the codebase, I add rules to codify them.

Test with edge cases. After updating your rules, ask the AI to generate something that would typically violate your conventions. If it still misbehaves, the rule needs to be rephrased or made more prominent.

The ROI Calculation

Here's the math that convinced my team to adopt this across all our projects:

  • Time to set up .cursorrules: 5 minutes (with a generator) to 2 hours (from scratch)
  • Time saved per day on code reformatting: 15-30 minutes
  • Break-even point: Day 1

Over a month, that's roughly 8-10 hours of developer time saved per person. For a team of four, that's a full work week recovered every month.

And the quality improvement is arguably even more valuable than the time savings. Fewer style inconsistencies means fewer code review comments, fewer merge conflicts from reformatting, and a more consistent codebase overall.

Getting Started

If you want to try this yourself:

Option A: Write your own. Create a .cursorrules file in your project root. Start with your 5 most important conventions. Test, iterate, refine.

Option B: Generate one. Go to ittoolshq.com/en/cursorrules-generator, select your stack and preferences, copy the output. Customize from there.

Either way, the important thing is to have one. An imperfect .cursorrules file is infinitely better than no .cursorrules file.

If you're using Windsurf, the same concept applies — just name the file .windsurfrules. For Cline users, it's .clinerules or AGENTS.md.


What rules have you found most effective in your own setup? I'm always looking to refine mine — drop your best tips in the comments.

Top comments (1)

Collapse
 
weiwei_tang_fc1fdc4e0b3de profile image
davidTang

Hey everyone, OP here! 👋

I'm planning to add more framework presets and code style options to the generator this weekend (currently thinking about Svelte and Angular).

What other specific tech stacks or niche code style rules would you want to see added to the tool? Let me know and I'll build them in!