DEV Community

Cover image for .cursorrules: Stop AI From Breaking Your Codebase
jedrzejdocs
jedrzejdocs

Posted on

.cursorrules: Stop AI From Breaking Your Codebase

Your AI assistant just suggested var in a TypeScript strict project. Then it imported from a path that doesn't exist. Then it used class components in your hooks-only codebase.

The problem isn't the AI. It's the missing configuration.

Modern AI coding tools support project-level instruction files. Most developers don't use them. Here's how to set them up properly.

The Configuration Files You're Missing

Different tools, different files:

Tool Config File Location
Cursor .cursorrules Project root
GitHub Copilot .github/copilot-instructions.md .github/ folder
Windsurf .windsurfrules Project root
Cline .clinerules Project root
Aider .aider.conf.yml Project root

Same concept: a file that tells AI how your project works before it generates anything.

.cursorrules: The Basics

Create .cursorrules in your project root:

# Project: my-app
# Stack: Next.js 14, TypeScript, Tailwind CSS, Prisma

## Code Style
- Use functional components with hooks only
- No default exports except for pages
- Use named exports for everything else
- Prefer const arrow functions over function declarations

## Imports
- Use @/ alias for src/ imports
- Group imports: external, internal, relative, types
- No barrel imports from large modules

## TypeScript
- Strict mode enabled
- No 'any' type unless explicitly justified
- Interface for objects, type for unions

## State Management
- Zustand for global state
- React Query for server state
- useState for local UI state only

## File Naming
- Components: PascalCase.tsx
- Hooks: use[Name].ts
- Utils: camelCase.ts
- Types: [name].types.ts
Enter fullscreen mode Exit fullscreen mode

Now when you ask Cursor to "add a user profile component", it generates code matching these rules.

What to Include

Effective .cursorrules files cover:

Architecture decisions

## Architecture
- Feature-based folder structure (src/features/[feature]/)
- Co-locate tests with source files
- Shared components in src/components/ui/
Enter fullscreen mode Exit fullscreen mode

Dependencies and their usage

## Key Dependencies
- Data fetching: @tanstack/react-query (useQuery, useMutation)
- Forms: react-hook-form + zod validation
- Date handling: date-fns (not moment, not dayjs)
- HTTP client: ky (not axios, not fetch)
Enter fullscreen mode Exit fullscreen mode

Anti-patterns to avoid

## DO NOT
- Use CSS modules (we use Tailwind)
- Use Redux (we use Zustand)
- Use relative imports beyond one level (use @/ alias)
- Create god components over 200 lines
- Use useEffect for data fetching
Enter fullscreen mode Exit fullscreen mode

Naming conventions

## Naming
- Boolean props: is[State], has[Feature], can[Action]
- Event handlers: handle[Event] or on[Event]
- Async functions: fetch[Resource], create[Resource]
Enter fullscreen mode Exit fullscreen mode

GitHub Copilot Instructions

Copilot uses markdown in .github/copilot-instructions.md:

# Copilot Instructions

## Project Context
This is a Next.js 14 application using the App Router.
All components are Server Components unless marked with 'use client'.

## Code Generation Rules

### TypeScript
- Always use strict TypeScript
- Define return types for all functions
- Use `satisfies` over type assertions when possible

### React Patterns
- Prefer Server Components for data fetching
- Use Suspense boundaries for loading states
- Keep Client Components small and leaf-level

### File Structure
When creating new features:
1. Create folder in src/features/[name]/
2. Add index.ts barrel file
3. Co-locate types in [name].types.ts
4. Co-locate tests in __tests__/

### Testing
- Use Vitest for unit tests
- Use Playwright for E2E
- Mock API calls with MSW
Enter fullscreen mode Exit fullscreen mode

Real-World Before/After

I spent two hours debugging why Cursor kept generating class components in a hooks-only project. The fix took 30 seconds — one .cursorrules file.

Task: "Create a user avatar component with loading state"

Without .cursorrules

import React, { useState, useEffect } from 'react';
import './Avatar.css';

class Avatar extends React.Component {
  state = { loading: true, error: false };

  componentDidMount() {
    // ...
  }

  render() {
    return <div className="avatar-container">...</div>;
  }
}

export default Avatar;
Enter fullscreen mode Exit fullscreen mode

Problems: class component, CSS file, default export, no TypeScript.

With .cursorrules

'use client';

import { useState } from 'react';
import { cn } from '@/lib/utils';

interface AvatarProps {
  src: string;
  alt: string;
  size?: 'sm' | 'md' | 'lg';
  fallback?: string;
}

export function Avatar({ src, alt, size = 'md', fallback }: AvatarProps) {
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);

  const sizeClasses = {
    sm: 'h-8 w-8',
    md: 'h-12 w-12',
    lg: 'h-16 w-16',
  };

  return (
    <div className={cn('relative rounded-full overflow-hidden', sizeClasses[size])}>
      {isLoading && (
        <div className="absolute inset-0 bg-gray-200 animate-pulse" />
      )}
      {hasError ? (
        <div className="flex items-center justify-center h-full bg-gray-100">
          {fallback ?? alt.charAt(0)}
        </div>
      ) : (
        <img
          src={src}
          alt={alt}
          onLoad={() => setIsLoading(false)}
          onError={() => setHasError(true)}
          className="h-full w-full object-cover"
        />
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Matches: functional component, Tailwind, named export, TypeScript interface, proper naming conventions.

Advanced Patterns

Use sections for different parts of your codebase:

## API Routes (src/app/api/)
- Always validate input with zod
- Return consistent error shapes: { error: string, code: string }
- Use HTTP status codes correctly
- Log errors to external service

## Database (src/db/)
- Use Prisma transactions for multi-step operations
- Never expose internal IDs in API responses
- Use soft deletes (deletedAt) not hard deletes

## Next.js 14 Specifics
- Use App Router patterns (not Pages Router)
- Prefer Server Actions over API routes for mutations
- Use 'use server' directive for server actions

## PR Requirements
- Components must have JSDoc description
- New features need tests
- Breaking changes need migration notes
Enter fullscreen mode Exit fullscreen mode

Common Mistakes

Too vague

# Bad
Write good code and follow best practices.
Enter fullscreen mode Exit fullscreen mode

Too verbose

# Bad  
When writing React components, you should always remember that 
functional components are preferred because they are more modern
and align with React's direction as a library, and hooks provide
a cleaner way to manage state and side effects compared to the
older class-based approach which required lifecycle methods...
Enter fullscreen mode Exit fullscreen mode

Contradicting project reality

# Bad (if your codebase actually uses axios)
Use fetch API for all HTTP requests
Enter fullscreen mode Exit fullscreen mode

Check that rules match your actual codebase. AI will get confused if .cursorrules says one thing but existing code does another.

Maintenance

.cursorrules is documentation. It rots if ignored.

Update when you:

  • Add major dependencies
  • Change architectural patterns
  • Update framework versions
  • Adopt new conventions

Add to your PR checklist:

- [ ] Updated .cursorrules if conventions changed
Enter fullscreen mode Exit fullscreen mode

Quick Setup Checklist

Check Item
Stack and versions listed
Import alias documented
Component patterns defined
State management approach specified
File naming conventions
Anti-patterns listed (what NOT to do)
Testing framework and patterns
Key dependencies with usage notes

Starter Template

Copy this and customize:

# Project: [name]
# Stack: [framework], [language], [styling]
# Last updated: [date]

## Code Style
- [component pattern]
- [export pattern]
- [function style]

## Imports
- Alias: @/ → src/
- Order: external → internal → relative → types

## TypeScript
- [strict mode?]
- [type vs interface preference]

## State Management
- Global: [library]
- Server: [library]
- Local: useState

## Key Dependencies
- [package]: [what for]

## DO NOT
- [anti-pattern 1]
- [anti-pattern 2]

## File Structure
[your structure]
Enter fullscreen mode Exit fullscreen mode

Conclusion

Five minutes of configuration saves hours of fixing AI-generated code that doesn't match your project.

Your .cursorrules file is your codebase's instruction manual for AI. Write it like you'd onboard a new developer — clear constraints, specific examples, explicit anti-patterns.

The AI can only follow rules it knows about.


What rules do you include in your AI configuration? Share your .cursorrules in the comments.


About me: I'm a Technical Documentation Specialist helping developers create README files, API docs, and technical guides. Check out my services or connect on LinkedIn.

Top comments (0)