Глава 8. Рефакторинг и оптимизация кода в Cursor
8.1. Умный рефакторинг кода
Рефакторинг с AI выходит за рамки простого переименования переменных — это интеллектуальное улучшение структуры кода.
Автоматический рефакторинг
Extract Function
Выделите блок кода → Right click → Refactor → Extract Function
Или используйте AI в Chat:
You: [Выделите повторяющийся код]
Извлеки это в отдельную функцию
AI: [Создаёт функцию с подходящим именем и параметрами]Пример:
// До
function processOrder(order) {
if (!order.customerId) throw new Error('Missing customer');
if (!order.items || order.items.length === 0) {
throw new Error('Order must have items');
}
if (order.total <= 0) throw new Error('Invalid total');
// ... обработка заказа
}
function processRefund(refund) {
if (!refund.customerId) throw new Error('Missing customer');
if (!refund.items || refund.items.length === 0) {
throw new Error('Refund must have items');
}
if (refund.amount <= 0) throw new Error('Invalid amount');
// ... обработка возврата
}
После рефакторинга с AI:
function validateCustomerTransaction(transaction, type = 'order') {
if (!transaction.customerId) {
throw new Error('Missing customer');
}
if (!transaction.items?.length) {
throw new Error(`${type} must have items`);
}
const amountField = type === 'order' ? 'total' : 'amount';
if (transaction[amountField] <= 0) {
throw new Error(`Invalid ${amountField}`);
}
}
function processOrder(order) {
validateCustomerTransaction(order, 'order');
// ... обработка заказа
}
function processRefund(refund) {
validateCustomerTransaction(refund, 'refund');
// ... обработка возврата
}
Extract Component (React)
// До - монолитный компонент
function UserDashboard({ user }) {
return (
<div>
<div className="header">
<img src={user.avatar} />
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
<div className="stats">
<div>Posts: {user.postCount}</div>
<div>Followers: {user.followers}</div>
<div>Following: {user.following}</div>
</div>
<div className="recent-activity">
{user.activities.map(activity => (
<div key={activity.id}>
<span>{activity.type}</span>
<span>{activity.timestamp}</span>
</div>
))}
</div>
</div>
);
}
Попросите AI:
Разбей этот компонент на подкомпонентыПосле рефакторинга:
// UserHeader.jsx
function UserHeader({ user }) {
return (
<div className="header">
<img src={user.avatar} alt={user.name} />
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
// UserStats.jsx
function UserStats({ stats }) {
return (
<div className="stats">
<StatItem label="Posts" value={stats.postCount} />
<StatItem label="Followers" value={stats.followers} />
<StatItem label="Following" value={stats.following} />
</div>
);
}
// RecentActivity.jsx
function RecentActivity({ activities }) {
return (
<div className="recent-activity">
{activities.map(activity => (
<ActivityItem key={activity.id} activity={activity} />
))}
</div>
);
}
// UserDashboard.jsx
function UserDashboard({ user }) {
return (
<div>
<UserHeader user={user} />
<UserStats stats={user} />
<RecentActivity activities={user.activities} />
</div>
);
}
Применение Design Patterns
You: Рефактори @api/products.js используя Repository patternAI преобразует прямые database вызовы в repository структуру:
// До
async function getProducts() {
return await db.query('SELECT * FROM products');
}
// После
class ProductRepository {
async findAll(filters = {}) {
let query = 'SELECT * FROM products WHERE 1=1';
const params = [];
if (filters.category) {
query += ' AND category = ?';
params.push(filters.category);
}
return await db.query(query, params);
}
async findById(id) {
return await db.queryOne('SELECT * FROM products WHERE id = ?', [id]);
}
async create(data) {
return await db.insert('products', data);
}
async update(id, data) {
return await db.update('products', data, { id });
}
async delete(id) {
return await db.delete('products', { id });
}
}
export const productRepository = new ProductRepository();
8.2. Улучшение читаемости
AI помогает сделать код более понятным и поддерживаемым.
Улучшение именования
// До - плохие имена
function proc(d) {
const r = d.map(x => x.v * 1.2);
return r.reduce((a, b) => a + b, 0);
}
Попросите:
Улучши имена переменных и функций для читаемостиРезультат:
function calculateTotalWithTax(items) {
const itemsWithTax = items.map(item => item.value * 1.2);
return itemsWithTax.reduce((total, current) => total + current, 0);
}
Добавление комментариев и документации
You: Добавь JSDoc комментарии к этой функции/**
* Calculates the total value of items including 20% tax
* @param {Array<{value: number}>} items - Array of items with value property
* @returns {number} Total value including tax
* @example
* const items = [{ value: 100 }, { value: 200 }];
* const total = calculateTotalWithTax(items); // returns 360
*/
function calculateTotalWithTax(items) {
const itemsWithTax = items.map(item => item.value * 1.2);
return itemsWithTax.reduce((total, current) => total + current, 0);
}
Упрощение сложной логики
// До - сложная вложенная логика
function getUserDiscount(user) {
if (user) {
if (user.isPremium) {
if (user.ordersCount > 10) {
return 0.3;
} else {
return 0.2;
}
} else {
if (user.ordersCount > 5) {
return 0.1;
} else {
return 0.05;
}
}
} else {
return 0;
}
}
После AI рефакторинга:
function getUserDiscount(user) {
if (!user) return 0;
if (user.isPremium) {
return user.ordersCount > 10 ? 0.3 : 0.2;
}
return user.ordersCount > 5 ? 0.1 : 0.05;
}
// Или еще лучше - табличный подход
const DISCOUNT_RATES = {
premium: { high: 0.3, low: 0.2 },
regular: { high: 0.1, low: 0.05 }
};
function getUserDiscount(user) {
if (!user) return 0;
const tier = user.isPremium ? 'premium' : 'regular';
const level = user.ordersCount > (user.isPremium ? 10 : 5) ? 'high' : 'low';
return DISCOUNT_RATES[tier][level];
}
8.3. Оптимизация производительности
AI может выявить и исправить проблемы производительности.
Оптимизация алгоритмов
// О(n²) - неэффективно
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
duplicates.push(arr[i]);
}
}
}
return duplicates;
}
Попросите:
Оптимизируй эту функцию для лучшей производительностиAI предложит О(n) решение:
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) {
duplicates.add(item);
} else {
seen.add(item);
}
}
return Array.from(duplicates);
}
React Performance
// Проблема: ререндер при каждом изменении parent
function ProductList({ products, onSelect }) {
return (
<div>
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onClick={() => onSelect(product.id)}
/>
))}
</div>
);
}
AI предложит оптимизацию:
// Мемоизированный компонент
const ProductCard = React.memo(({ product, onClick }) => {
return (
<div onClick={onClick}>
{product.name} - ${product.price}
</div>
);
});
function ProductList({ products, onSelect }) {
// Мемоизируем callbacks
const handlers = useMemo(() => {
return products.reduce((acc, product) => {
acc[product.id] = () => onSelect(product.id);
return acc;
}, {});
}, [products, onSelect]);
return (
<div>
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onClick={handlers[product.id]}
/>
))}
</div>
);
}
Database Query Optimization
// N+1 проблема
async function getUsersWithPosts() {
const users = await db.query('SELECT * FROM users');
for (const user of users) {
user.posts = await db.query(
'SELECT * FROM posts WHERE user_id = ?',
[user.id]
);
}
return users;
}
AI предложит:
async function getUsersWithPosts() {
const users = await db.query('SELECT * FROM users');
const userIds = users.map(u => u.id);
// Один запрос вместо N
const posts = await db.query(
'SELECT * FROM posts WHERE user_id IN (?)',
[userIds]
);
// Группируем посты по пользователям
const postsByUser = posts.reduce((acc, post) => {
if (!acc[post.user_id]) acc[post.user_id] = [];
acc[post.user_id].push(post);
return acc;
}, {});
// Присваиваем посты пользователям
return users.map(user => ({
...user,
posts: postsByUser[user.id] || []
}));
}
8.4. Modernization устаревшего кода
AI отлично справляется с обновлением старого кода до современных стандартов.
ES5 → ES6+
// Старый ES5 код
var UserService = function() {
this.users = [];
};
UserService.prototype.addUser = function(user) {
this.users.push(user);
};
UserService.prototype.findUser = function(id) {
for (var i = 0; i < this.users.length; i++) {
if (this.users[i].id === id) {
return this.users[i];
}
}
return null;
};
Попросите:
Обнови этот код до ES6+ синтаксисаРезультат:
class UserService {
constructor() {
this.users = [];
}
addUser(user) {
this.users.push(user);
}
findUser(id) {
return this.users.find(user => user.id === id) ?? null;
}
}
Callbacks → Promises → Async/Await
// Callback hell
function getUser Data(userId, callback) {
db.getUser(userId, function(err, user) {
if (err) return callback(err);
db.getPosts(user.id, function(err, posts) {
if (err) return callback(err);
db.getComments(posts[0].id, function(err, comments) {
if (err) return callback(err);
callback(null, { user, posts, comments });
});
});
});
}
AI переделает в async/await:
async function getUserData(userId) {
try {
const user = await db.getUser(userId);
const posts = await db.getPosts(user.id);
const comments = await db.getComments(posts[0].id);
return { user, posts, comments };
} catch (error) {
console.error('Error fetching user data:', error);
throw error;
}
}
jQuery → Vanilla JS
// jQuery код
$(document).ready(function() {
$('#submitBtn').click(function() {
var name = $('#nameInput').val();
$.ajax({
url: '/api/users',
method: 'POST',
data: { name: name },
success: function(response) {
$('#result').html('Success: ' + response.message);
},
error: function(xhr) {
$('#result').html('Error: ' + xhr.responseText);
}
});
});
});
Современный вариант:
document.addEventListener('DOMContentLoaded', () => {
const submitBtn = document.getElementById('submitBtn');
const nameInput = document.getElementById('nameInput');
const result = document.getElementById('result');
submitBtn.addEventListener('click', async () => {
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: nameInput.value })
});
const data = await response.json();
result.textContent = `Success: ${data.message}`;
} catch (error) {
result.textContent = `Error: ${error.message}`;
}
});
});
8.5. Работа с техническим долгом
AI помогает систематически устранять технический долг.
Аудит технического долга
You: Проанализируй @src/ и найди основные проблемы
технического долга
AI: Обнаружено несколько проблем:
1. Дублирование кода (15 экземпляров):
- Валидация email повторяется в 5 файлах
- API обработка ошибок дублирована
2. Устаревшие паттерны (23 файла):
- Class components вместо hooks
- var вместо const/let
- Callbacks вместо async/await
3. Отсутствие тестов:
- 0% coverage для utils/
- Критические функции не покрыты
4. Performance проблемы:
- N+1 queries в UserService
- Отсутствие мемоизации в Dashboard
5. Security:
- SQL injection риск в 3 файлах
- Незащищенные endpoints
Рекомендации по приоритизации:
1. Security (критично)
2. Performance (высокий приоритет)
3. Дублирование (средний)
4. Modernization (низкий)Постепенное улучшение
Вместо большого переписывания, AI может помочь с инкрементальным улучшением:
You: Создай план рефакторинга @components/ на 4 недели
AI: План постепенного рефакторинга:
Неделя 1: Подготовка
- Добавить тесты к критическим компонентам
- Настроить TypeScript
- Создать style guide
Неделя 2: Базовые улучшения
- Конвертировать 10 самых используемых компонентов в hooks
- Стандартизировать prop types
- Добавить PropTypes/TypeScript types
Неделя 3: Структурные изменения
- Разбить монолитные компоненты
- Вынести общую логику в hooks
- Оптимизировать ререндеры
Неделя 4: Финализация
- Code review всех изменений
- Performance тестирование
- Документация обновлений