Be Specific
Vague rules lead to inconsistent code. Include concrete examples.
Ta treść nie jest jeszcze dostępna w Twoim języku.
Custom rules and templates in Cursor transform AI assistance from generic help into a precision tool tailored to your team’s specific needs. They ensure consistency, enforce standards, and capture institutional knowledge.
Rules are instructions that shape how Cursor’s AI behaves in your project. They can:
Cursor applies rules in a specific order:
# [Rule Category]: [Specific Topic]
## Context
Brief description of when and why this rule applies.
## Requirements
- Specific requirement 1- Specific requirement 2- Specific requirement 3
## Examples
### Good Example
```language// Code that follows the rule```
// Code that violates the rule
Explanation of why this rule exists and its benefits.
Cases where this rule might not apply.
### Real-World Rule Examples
<Tabs> <TabItem label="Architecture Rules"> ```markdown # Architecture: Service Layer Pattern
## Context All business logic must be implemented in service classes, not in controllers or models.
## Requirements - Controllers only handle HTTP concerns (request/response) - Services contain all business logic - Services are stateless and testable - Services inject dependencies via constructor - One service per domain entity
## Examples
### Good Example ```typescript // UserController.ts export class UserController { constructor(private userService: UserService) {}
async createUser(req: Request, res: Response) { const userData = req.body; const user = await this.userService.create(userData); res.json(user); } }
// UserService.ts export class UserService { constructor( private userRepo: UserRepository, private emailService: EmailService ) {}
async create(data: CreateUserDto): Promise<User> { // Business logic here const user = await this.userRepo.save(data); await this.emailService.sendWelcome(user); return user; } } ```
### Bad Example ```typescript // UserController.ts - WRONG: Business logic in controller export class UserController { async createUser(req: Request, res: Response) { const user = new User(req.body);
// Business logic should not be here! if (await this.checkDuplicateEmail(user.email)) { throw new Error('Email exists'); }
await db.users.insert(user); await sendEmail(user.email, 'Welcome!');
res.json(user); } } ```
## Rationale - Separation of concerns - Easier testing (mock services) - Reusable business logic - Clear boundaries between layers ``` </TabItem> <TabItem label="Security Rules"> ```markdown # Security: Input Validation
## Context All user input must be validated before processing.
## Requirements - Use validation library (Joi, Yup, Zod) - Validate at controller level - Sanitize strings to prevent XSS - Use parameterized queries for SQL - Never trust client-side validation alone
## Examples
### Good Example ```typescript import { z } from 'zod';
const CreateUserSchema = z.object({ email: z.string().email(), password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/), name: z.string().min(2).max(100).transform(sanitizeHtml), age: z.number().int().min(18).max(120) });
async createUser(req: Request, res: Response) { try { const validated = CreateUserSchema.parse(req.body); const user = await this.userService.create(validated); res.json(user); } catch (error) { if (error instanceof z.ZodError) { res.status(400).json({ errors: error.errors }); } } } ```
### Bad Example ```typescript // WRONG: No validation async createUser(req: Request, res: Response) { const user = await this.userService.create(req.body); res.json(user); }
// WRONG: SQL injection vulnerability const query = `SELECT * FROM users WHERE email = '${email}'`; ```
## Exceptions - Internal service-to-service calls may skip validation - But always validate at system boundaries ``` </TabItem> <TabItem label="Performance Rules"> ```markdown # Performance: Database Query Optimization
## Context Optimize all database queries for performance.
## Requirements - Always use indexes for WHERE clauses - Paginate results over 100 records - Use SELECT with specific columns, not SELECT * - Batch operations when possible - Monitor slow queries
## Examples
### Good Example ```typescript // Optimized query with index usage async findActiveUsers(page: number, limit: number = 50) { return this.db.users .select(['id', 'name', 'email', 'lastActive']) .where('status', 'active') .where('lastActive', '>', thirtyDaysAgo) .orderBy('lastActive', 'desc') .limit(limit) .offset(page * limit); }
// Batch operation async updateMultipleUsers(updates: UserUpdate[]) { const chunks = chunk(updates, 1000);
for (const batch of chunks) { await this.db.transaction(async (trx) => { await Promise.all( batch.map(update => trx.users.update(update.id, update.data) ) ); }); } } ```
### Bad Example ```typescript // WRONG: No pagination, SELECT * async getAllUsers() { return this.db.query('SELECT * FROM users'); }
// WRONG: N+1 query problem const users = await getUsers(); for (const user of users) { user.posts = await getPosts(user.id); } ``` ``` </TabItem></Tabs>
## Advanced Rule Patterns
### Dynamic Rules with Context
```markdown# Dynamic Rule: API Response Format
## ContextAPI responses must follow our standard format, which varies by environment.
## Implementation```typescriptinterface ApiResponse<T> { success: boolean; data?: T; error?: { code: string; message: string; details?: any; }; metadata: { timestamp: string; version: string; environment: 'dev' | 'staging' | 'prod'; };}
// In development, include debug infoif (process.env.NODE_ENV === 'development') { response.metadata.debug = { query: req.query, headers: req.headers, processingTime: Date.now() - startTime };}
When generating API endpoints:
### Technology-Specific Rules
<Tabs> <TabItem label="React Rules"> ```markdown # React: Component Patterns
## Functional Components Only Use functional components with hooks. No class components.
## Component Structure ```tsx // 1. Imports import React, { useState, useEffect } from 'react'; import { useUser } from '@/hooks/useUser'; import { Button } from '@/components/ui/Button'; import type { UserProps } from '@/types';
// 2. Types interface ComponentProps { userId: string; onUpdate?: (user: User) => void; }
// 3. Component export function UserProfile({ userId, onUpdate }: ComponentProps) { // 4. Hooks const { user, loading, error } = useUser(userId); const [isEditing, setIsEditing] = useState(false);
// 5. Effects useEffect(() => { // Effect logic }, [userId]);
// 6. Handlers const handleEdit = () => { setIsEditing(true); };
// 7. Render if (loading) return <Spinner />; if (error) return <ErrorDisplay error={error} />;
return ( <div> {/* JSX */} </div> ); } ```
## State Management - Use React Query for server state - Use Zustand for client state - Avoid prop drilling - use context sparingly ``` </TabItem> <TabItem label="Node.js Rules"> ```markdown # Node.js: Backend Patterns
## Error Handling ```typescript // Custom error classes export class AppError extends Error { constructor( public statusCode: number, public message: string, public code: string ) { super(message); } }
// Global error handler app.use((err: Error, req: Request, res: Response, next: NextFunction) => { if (err instanceof AppError) { return res.status(err.statusCode).json({ success: false, error: { code: err.code, message: err.message } }); }
// Log unexpected errors logger.error('Unexpected error:', err); res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } }); }); ```
## Async Handler Pattern ```typescript const asyncHandler = (fn: Function) => ( req: Request, res: Response, next: NextFunction ) => { Promise.resolve(fn(req, res, next)).catch(next); };
// Usage router.post('/users', asyncHandler(async (req, res) => { const user = await userService.create(req.body); res.json(user); })); ``` ``` </TabItem></Tabs>
## Template System
### Creating Reusable Templates
```markdown# Template: New Microservice
When creating a new microservice, generate the following structure:
## Directory Structure
service-name/ ├── src/ │ ├── api/ │ │ ├── controllers/ │ │ ├── middlewares/ │ │ └── routes/ │ ├── core/ │ │ ├── domain/ │ │ ├── services/ │ │ └── repositories/ │ ├── infrastructure/ │ │ ├── database/ │ │ ├── cache/ │ │ └── messaging/ │ └── index.ts ├── tests/ │ ├── unit/ │ ├── integration/ │ └── e2e/ ├── .env.example ├── Dockerfile ├── docker-compose.yml └── package.json
## Base Files to Generate
1. **package.json** with our standard dependencies2. **tsconfig.json** with our TypeScript settings3. **Dockerfile** with multi-stage build4. **.env.example** with required variables5. **src/index.ts** with health check and graceful shutdown6. Basic controller, service, and repository examples7. Standard middleware (auth, logging, error handling)8. Test setup with examples
// Template generator scriptclass TemplateGenerator { async generateFromTemplate(templateName: string, variables: Record<string, any>) { const template = await this.loadTemplate(templateName);
// Process template with variables const processed = this.processTemplate(template, variables);
// Generate files for (const file of processed.files) { await this.createFile(file.path, file.content); }
// Run post-generation hooks await this.runHooks(processed.hooks); }
private processTemplate(template: string, vars: Record<string, any>): ProcessedTemplate { // Replace variables like {{serviceName}} let content = template;
for (const [key, value] of Object.entries(vars)) { const regex = new RegExp(`{{${key}}}`, 'g'); content = content.replace(regex, value); }
// Handle conditionals content = this.processConditionals(content, vars);
// Handle loops content = this.processLoops(content, vars);
return this.parseToFiles(content); }}
Establish Rule Ownership
Rule Documentation
# Rule Metadata
- **Author**: @teamlead- **Created**: 2024-01-15- **Modified**: 2024-03-20- **Version**: 2.1- **Reviewers**: @senior-dev, @architect- **Affected Teams**: Backend, Frontend
Rule Testing
// Test that generated code follows rulesdescribe('Architecture Rules Compliance', () => { test('Verify services follow dependency injection pattern', () => { // Create test prompt file const testPrompt = ` Create a UserService following these rules: - Use constructor dependency injection - Inject Repository as a dependency - Do not instantiate dependencies with 'new' - Follow our architecture patterns from @Cursor Rules `;
// Open in Cursor and manually verify the generated code // Use Agent mode (Ctrl+I) with the prompt above // Then run this test against the generated file:
const generatedCode = fs.readFileSync('src/services/UserService.ts', 'utf8');
expect(generatedCode).toMatch(/constructor\s*\(/); expect(generatedCode).toMatch(/private.*Repository/); expect(generatedCode).not.toMatch(/new.*Repository/); });});
Rule Distribution
// Track rule complianceclass RuleComplianceTracker { async analyzeCodebase() { const violations = [];
// Check each rule for (const rule of this.rules) { const ruleViolations = await this.checkRule(rule); violations.push(...ruleViolations); }
// Generate report return { totalFiles: this.fileCount, violations: violations, complianceRate: this.calculateCompliance(violations), byRule: this.groupByRule(violations), byTeam: this.groupByTeam(violations), }; }}
Be Specific
Vague rules lead to inconsistent code. Include concrete examples.
Explain Why
Always include rationale. Developers follow rules they understand.
Show Examples
Good and bad examples make rules crystal clear.
Keep Updated
Review rules quarterly. Remove outdated ones, add new learnings.
Over-Constraining
❌ Bad: "Never use forEach loops"✅ Good: "Prefer map/filter/reduce for transformations. Use forEach for side effects only."
Contradictory Rules
❌ Bad: Having both "Always use async/await" and "Use callbacks for performance"✅ Good: Clear guidelines on when to use each approach
Technology Lock-in
❌ Bad: "Always use library X for everything"✅ Good: "Use library X for Y use case because Z"
Cursor can generate rules from your development sessions:
/generate-rules
Cursor will analyze this conversation and create rules based on:- Patterns you've established- Corrections you've made- Preferences you've expressed- Standards you've enforced
# Generated Rule: API Error Handling
Based on conversation from 2024-03-20
## Pattern Detected
You consistently correct API endpoints to:
1. Use try-catch blocks2. Return standardized error responses3. Log errors with context4. Use appropriate HTTP status codes
## Recommended Rule
All API endpoints must implement error handling that:
- Catches all possible errors- Returns AppError instances- Logs with correlation IDs- Never exposes internal errors to clients
## Code Template
[Generated template based on your corrections]
name: Rule Compliance Check
on: [push, pull_request]
jobs: check-rules: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Run Cursor Rule Checker run: | npx @company/cursor-rule-checker \ --rules .cursor/rules \ --source src \ --report compliance-report.json
- name: Comment PR if: github.event_name == 'pull_request' uses: actions/github-script@v6 with: script: | const report = require('./compliance-report.json'); const comment = generateComplianceComment(report); github.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });
Master custom rules and templates:
Rules and templates are living documents. They should evolve with your team’s learning and your project’s growth. Master this, and you’ll have AI assistance that truly understands your specific needs.