ЧАСТЬ III. ПРОДВИНУТЫЕ ТЕХНИКИ
Глава 9. Cursor Rules: Настройка поведения AI
9.1. Что такое Cursor Rules
Cursor Rules — это мощный механизм для настройки поведения AI в соответствии с вашими предпочтениями, стандартами команды и требованиями проекта.
Зачем нужны Rules?
По умолчанию AI генерирует код на основе общих best practices. Но каждый проект уникален:
- У компании могут быть свои code style guidelines
- Проект может использовать специфические паттерны
- Команда может предпочитать определенные библиотеки
- Существуют внутренние соглашения об именовании
Rules позволяют "обучить" AI этим специфическим требованиям.
Принцип работы
Когда вы взаимодействуете с AI (Chat, Composer, Autocomplete), Cursor автоматически включает активные Rules в контекст. AI получает инструкции типа:
Системные инструкции:
- Ты помощник программиста
- Генерируй чистый, эффективный код
- Следуй указанным правилам проекта
Правила проекта:
[Содержимое ваших Rules]
Запрос пользователя:
[Ваш промпт]Это обеспечивает консистентность генерируемого кода.
Три уровня Rules
Cursor поддерживает иерархию правил:
- Глобальные Rules — применяются ко всем проектам
- Проектные Rules — специфичны для текущего проекта
- Файловые Rules — для конкретных файлов или директорий
Правила применяются каскадно: глобальные → проектные → файловые.
9.2. Глобальные правила в настройках
Глобальные правила устанавливаются в настройках Cursor и применяются ко всем вашим проектам.
Как настроить глобальные правила
- Откройте Settings (Cmd/Ctrl + ,)
- Найдите "Cursor" в поиске
- Перейдите в "Rules" секцию
- Добавьте свои правила в текстовое поле
Или через settings.json:
{
"cursor.rules": [
"Always use const instead of let when variable won't be reassigned",
"Prefer arrow functions over function declarations",
"Use TypeScript interfaces for object types",
"Add JSDoc comments to all exported functions"
]
}
Примеры полезных глобальных правил
Code Style:
- Use 2 spaces for indentation
- Use single quotes for strings
- Always use semicolons
- Maximum line length: 100 characters
- Use trailing commas in multi-line objects/arraysNaming Conventions:
- Use camelCase for variables and functions
- Use PascalCase for classes and components
- Use UPPER_SNAKE_CASE for constants
- Prefix private methods with underscore
- Use descriptive, self-documenting namesBest Practices:
- Always handle errors explicitly
- Avoid using 'any' type in TypeScript
- Prefer immutable operations
- Write pure functions when possible
- Separate concerns (single responsibility)Documentation:
- Add JSDoc comments to all public APIs
- Include @param and @returns in documentation
- Add usage examples for complex functions
- Document assumptions and edge cases9.3. Проектные правила в .cursorrules
Файл .cursorrules в корне проекта содержит специфичные для проекта инструкции.
Создание .cursorrules файла
В корне проекта создайте файл .cursorrules:
touch .cursorrules
Базовый пример
# Project: E-commerce Platform
# Stack: React, Node.js, PostgreSQL, TypeScript
## General Rules
- Use TypeScript for all new files
- Follow airbnb style guide
- Write unit tests for all business logic
- Use async/await instead of promises chains
## React Specific
- Use functional components with hooks
- No class components
- Prefer composition over HOCs
- Use React.memo for performance optimization
- Organize components: Container/Presentational pattern
## File Structure
components/
ComponentName/
index.tsx
ComponentName.tsx
ComponentName.test.tsx
ComponentName.module.css
types.ts
## Naming
- Components: PascalCase (UserProfile.tsx)
- Utilities: camelCase (formatDate.ts)
- Constants: UPPER_SNAKE_CASE
- Types/Interfaces: PascalCase with I prefix (IUser)
## API Conventions
- Use RESTful endpoints
- HTTP methods: GET, POST, PUT, DELETE
- Response format: { success: boolean, data: any, error?: string }
- Always validate request data
## Database
- Use parameterized queries (prevent SQL injection)
- Transaction wrapping for multiple operations
- Soft delete instead of hard delete (add deleted_at column)
## Error Handling
try {
// operation
} catch (error) {
logger.error('Context:', error);
throw new CustomError('User-friendly message', error);
}
## Testing
- Jest for unit tests
- React Testing Library for component tests
- Minimum 80% coverage
- Test file naming: *.test.ts(x)
## Dependencies
- Prefer: axios, date-fns, lodash
- Avoid: moment (too large), jQuery
- Always check bundle size impact
## Security
- Never commit API keys or secrets
- Use environment variables
- Validate all user input
- Sanitize data before database operations
- Use helmet.js for Express security
Продвинутый пример для микросервисной архитектуры
# Microservices Project Rules
## Architecture
- Each service is independent
- Communication via REST APIs or message queue
- Each service has its own database (no shared DB)
- Use API Gateway pattern for external requests
## Service Structure
/service-name
/src
/api - HTTP endpoints
/services - Business logic
/models - Data models
/utils - Utilities
/config - Configuration
/tests
Dockerfile
package.json
## Code Patterns
### Repository Pattern
```javascript
class UserRepository {
async findById(id: string): Promise<User | null> {
// Implementation
}
async create(data: CreateUserDTO): Promise<User> {
// Implementation
}
}
Service Layer
class UserService {
constructor(private userRepo: UserRepository) {}
async createUser(data: CreateUserDTO): Promise<User> {
// Validation
// Business logic
return this.userRepo.create(data);
}
}
Controller Layer
class UserController {
constructor(private userService: UserService) {}
async create(req: Request, res: Response) {
try {
const user = await this.userService.createUser(req.body);
res.json({ success: true, data: user });
} catch (error) {
handleError(error, res);
}
}
}
API Response Format
Success:
{
"success": true,
"data": { ... },
"meta": {
"timestamp": "2024-01-01T00:00:00Z",
"requestId": "uuid"
}
}
Error:
{
"success": false,
"error": {
"code": "USER_NOT_FOUND",
"message": "User with id X not found",
"details": { ... }
},
"meta": { ... }
}
Logging
- Use structured logging (winston/pino)
- Log levels: error, warn, info, debug
- Include request ID in all logs
- Never log sensitive data (passwords, tokens)
Environment Variables
- Use .env for local development
- Never commit .env files
- Document all required env vars in .env.example
- Validate env vars at startup
Docker
- Multi-stage builds for smaller images
- Non-root user for security
- Health check endpoints
- Graceful shutdown handling
Testing
- Unit tests: 80%+ coverage
- Integration tests for APIs
- E2E tests for critical paths
- Use test containers for database tests
### 9.4. Файл .cursor/index.mdc
Согласно [kirill-markin.com](https://kirill-markin.com/articles/cursor-ide-rules-for-ai/), новый подход к Rules использует файл `.cursor/index.mdc` в корне проекта. Это формат Markdown Context (MDC).
**Создание .cursor/index.mdc**
```bash
mkdir .cursor
touch .cursor/index.mdcПреимущества MDC формата
- Лучшая организация: Markdown структура с заголовками и секциями
- Больше контекста: Можно включать примеры кода, диаграммы
- Динамические правила: Возможность reference других файлов
- Версионирование: Легче отслеживать изменения в Git
Структура index.mdc
---
title: "Project Cursor Rules"
version: "1.0.0"
updated: "2024-01-15"
---
# Project Context
## Tech Stack
- **Frontend**: React 18, TypeScript, Tailwind CSS
- **Backend**: Node.js, Express, PostgreSQL
- **Testing**: Jest, React Testing Library
- **Build**: Vite, ESBuild
## Project Goals
This is a SaaS platform for project management. Key features:
- Real-time collaboration
- Role-based access control
- Advanced reporting and analytics
# Coding Standards
## TypeScript Configuration
```typescript
// tsconfig.json settings we follow
{
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
React Patterns
Component Structure
import React from 'react';
import styles from './ComponentName.module.css';
interface ComponentNameProps {
// Props with JSDoc
/** User object containing profile information */
user: User;
/** Callback when user clicks submit */
onSubmit: (data: FormData) => void;
}
export const ComponentName: React.FC<ComponentNameProps> = ({
user,
onSubmit
}) => {
// Hooks at the top
const [state, setState] = useState(initialState);
// Event handlers
const handleClick = useCallback(() => {
// Implementation
}, [dependencies]);
// Render
return (
<div className={styles.container}>
{/* JSX */}
</div>
);
};
Custom Hooks
// Prefix with 'use'
// Return array for multiple values, object for named returns
function useUserData(userId: string) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
// Fetch logic
}, [userId]);
return { user, loading, error };
}
API Development
Endpoint Naming
- Plural nouns:
/users,/products - Nested resources:
/users/:id/posts - Actions:
/users/:id/activate
Request Validation
Always use validation middleware:
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
age: z.number().int().positive().optional()
});
router.post('/users', validate(createUserSchema), async (req, res) => {
// req.body is now typed and validated
});
Error Handling
class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public code?: string
) {
super(message);
}
}
// Usage
throw new AppError(404, 'User not found', 'USER_NOT_FOUND');
Database Patterns
Migrations
- One migration per change
- Reversible (up/down)
- Naming:
YYYYMMDDHHMMSS_description.sql
Queries
// Use query builder or ORM
const users = await db
.select('*')
.from('users')
.where('status', 'active')
.orderBy('created_at', 'desc')
.limit(10);
// Raw queries must be parameterized
const user = await db.query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
Testing Requirements
Unit Tests
- Test file location: same directory as source
- Naming:
*.test.tsor*.spec.ts - Coverage target: 80%
Test Structure
describe('UserService', () => {
describe('createUser', () => {
it('should create user with valid data', async () => {
// Arrange
const userData = { email: 'test@example.com', name: 'Test' };
// Act
const user = await userService.createUser(userData);
// Assert
expect(user).toMatchObject(userData);
expect(user.id).toBeDefined();
});
it('should throw error for invalid email', async () => {
// Test error cases
await expect(
userService.createUser({ email: 'invalid', name: 'Test' })
).rejects.toThrow('Invalid email');
});
});
});
Performance Guidelines
React Optimization
- Use
React.memofor expensive components useMemofor expensive calculationsuseCallbackfor functions passed to children- Lazy load routes and heavy components
Database
- Index foreign keys
- Index frequently queried columns
- Use EXPLAIN ANALYZE for slow queries
- Implement pagination for large datasets
API
- Implement caching (Redis)
- Use compression (gzip)
- Rate limiting per endpoint
- CDN for static assets
Security Checklist
- Input validation on all endpoints
- Parameterized database queries
- Authentication on protected routes
- CORS configuration
- Rate limiting
- Helmet.js for HTTP headers
- Secrets in environment variables
- HTTPS in production
- SQL injection prevention
- XSS prevention
Code Review Checklist
Before submitting PR:
- All tests pass
- No console.logs or debugger statements
- TypeScript errors resolved
- ESLint warnings addressed
- Code formatted (Prettier)
- Meaningful commit messages
- Updated documentation if needed
- Performance implications considered
Common Patterns
Loading States
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
if (!data) return <EmptyState />;
return <DataDisplay data={data} />;
Form Handling
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema)
});
API Calls
// Use custom hook for data fetching
const { data, loading, error, refetch } = useQuery('/api/users');
// Or manual with error handling
try {
const response = await api.get('/users');
setData(response.data);
} catch (error) {
handleError(error);
}
Don'ts (Common Mistakes to Avoid)
Don't:
- Use
anytype in TypeScript - Mutate state directly
- Put business logic in components
- Skip error handling
- Commit commented-out code
- Use inline styles (use CSS modules/Tailwind)
- Create giant components (>300 lines)
- Skip prop validation
- Use
var(useconst/let) - Store sensitive data in localStorage
Helpful Resources
### 9.5. Создание специализированных AI-ассистентов
Rules позволяют создать AI-ассистентов для специфических задач.
**Backend-специалист**
`.cursor/backend.mdc`:
```markdown
# Backend Development Assistant
You are a backend development expert specializing in Node.js, Express, and PostgreSQL.
## Your Primary Goals
1. Write secure, scalable server-side code
2. Follow RESTful API best practices
3. Implement proper error handling
4. Ensure database query optimization
## Code Generation Rules
### Always include:
- Input validation
- Error handling with try/catch
- Logging for debugging
- TypeScript types
- JSDoc comments
### API Endpoint Template
```typescript
/**
* @route GET /api/resource/:id
* @description Get resource by ID
* @access Private
*/
router.get('/:id',
authenticate,
validate(getResourceSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const resource = await resourceService.findById(req.params.id);
if (!resource) {
throw new NotFoundError('Resource not found');
}
res.json({
success: true,
data: resource
});
} catch (error) {
next(error);
}
}
);Database Queries
- Always use parameterized queries
- Implement connection pooling
- Use transactions for multiple operations
- Index frequently queried columns
Security
- Validate and sanitize all inputs
- Use bcrypt for password hashing (10 rounds)
- Implement rate limiting
- Add CORS configuration
- Use helmet.js for headers
**Frontend-специалист**
`.cursor/frontend.mdc`:
```markdown
# Frontend Development Assistant
You specialize in React, TypeScript, and modern frontend development.
## Your Expertise
- Building responsive, accessible UIs
- State management with hooks
- Performance optimization
- Testing with RTL
## Component Generation Rules
### Always create:
- TypeScript interfaces for props
- Proper prop validation
- Accessibility attributes
- Responsive design
- Loading and error states
### Component Template
```tsx
import React, { useState, useEffect } from 'react';
import styles from './Component.module.css';
interface ComponentProps {
/** Description of prop */
prop: string;
/** Optional prop with default */
optional?: number;
}
export const Component: React.FC<ComponentProps> = ({
prop,
optional = 0
}) => {
const [state, setState] = useState<StateType>(initialState);
useEffect(() => {
// Side effects
return () => {
// Cleanup
};
}, [dependencies]);
return (
<div className={styles.container} role="region" aria-label="Component">
{/* Accessible content */}
</div>
);
};Styling
- Use CSS Modules or Tailwind
- Mobile-first approach
- Follow design system tokens
- Ensure color contrast (WCAG AA)
State Management
- Use local state for component-specific data
- Context for shared state
- Custom hooks for reusable logic
**DevOps-специалист**
`.cursor/devops.mdc`:
```markdown
# DevOps Assistant
You specialize in infrastructure, deployment, and automation.
## Focus Areas
- Docker containerization
- CI/CD pipelines
- Infrastructure as Code
- Monitoring and logging
## Dockerfile Best Practices
```dockerfile
# Multi-stage build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine
WORKDIR /app
# Non-root user for security
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]GitHub Actions Template
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
- run: npm run lint
### 9.6. Примеры эффективных правил
**Правила для стартапа (быстрая разработка)**
```markdown
# Startup Mode - Move Fast
## Priorities
1. Speed over perfection
2. MVP features first
3. Technical debt is acceptable (documented)
4. Use proven libraries, avoid custom solutions
## Code Style
- Prefer simple solutions
- Copy-paste is OK if it saves time
- TODO comments for future improvements
- Focus on user-facing features
## Testing
- Test critical paths only
- E2E tests for main user flows
- Skip unit tests for simple functions
## When to Ask AI
- "What's the fastest way to implement X?"
- "Generate boilerplate for Y"
- "Quick prototype of Z"Правила для enterprise (качество и надежность)
# Enterprise Development Standards
## Priorities
1. Reliability and stability
2. Security
3. Maintainability
4. Performance
## Code Quality
- 90%+ test coverage required
- Code review mandatory
- No warnings allowed
- Full documentation
## Security
- OWASP Top 10 compliance
- Regular dependency audits
- Security review for all PRs
- Penetration testing before release
## Performance
- Load testing required
- Performance budgets enforced
- Monitoring and alerting
- SLA compliance
## When to Ask AI
- "Implement X following our security guidelines"
- "Review this code for potential issues"
- "Generate comprehensive tests for Y"
Правила для обучения
# Learning Mode
## Your Role
You are a patient teacher helping me learn programming.
## How to Help
1. Explain concepts before showing code
2. Break down complex problems
3. Show multiple approaches
4. Explain trade-offs
5. Provide resources for deeper learning
## Code Generation
- Add detailed comments explaining each line
- Include links to documentation
- Show common mistakes and how to avoid them
- Suggest exercises to practice
## Examples
```javascript
// BAD: Hard to understand
const r = arr.filter(x => x > 10).map(x => x * 2);
// GOOD: Clear and educational
// Step 1: Filter array to keep only numbers greater than 10
const numbersGreaterThan10 = arr.filter(number => number > 10);
// Step 2: Double each number
const doubledNumbers = numbersGreaterThan10.map(number => number * 2);
// Can be written in one line for conciseness (once you understand):
const result = arr.filter(x => x > 10).map(x => x * 2);