# Code Simplification

> Упрощает код ради ясности. Используй при рефакторинге кода ради читаемости без изменения поведения. Используй, когда код работает, но читать, поддерживать или расширять его тяжелее, чем следовало бы. Используй при ревью кода, накопившего лишнюю сложность.

- Skill: `aleksandr-litvinenko/code-simplification` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aleksandr-litvinenko/code-simplification`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aleksandr-litvinenko/code-simplification/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Aleksandr-Litvinenko (https://skillmd.com/u/aleksandr-litvinenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aleksandr-litvinenko/code-simplification

---


# Упрощение кода

> Вдохновлено [плагином Claude Code Simplifier](https://github.com/anthropics/claude-plugins-official/blob/main/plugins/code-simplifier/agents/code-simplifier.md). Здесь адаптировано как процессный скилл, не привязанный к конкретной модели, для любого AI-агента для кода.

## Обзор

Упрощай код, снижая сложность и в точности сохраняя поведение. Цель — не меньше строк, а код, который легче читать, понимать, менять и отлаживать. Каждое упрощение должно проходить простую проверку: «Поймёт ли новый человек в команде это быстрее, чем оригинал?»

## Когда применять

- Фича работает и тесты проходят, но реализация ощущается тяжелее, чем должна быть
- В ходе ревью отмечены проблемы читаемости или сложности
- Встретилась глубоко вложенная логика, длинные функции или невнятные имена
- Рефакторишь код, написанный в спешке
- Собираешь воедино связанную логику, разбросанную по файлам
- После вливания изменений, которые внесли дублирование или несогласованность

**Когда НЕ применять:**

- Код уже чистый и читаемый — не упрощай ради упрощения
- Ты ещё не понял, что делает код — сначала разберись, потом упрощай
- Код критичен по производительности, и «более простая» версия будет измеримо медленнее
- Ты вот-вот перепишешь модуль целиком — упрощать одноразовый код бессмысленно

## Пять принципов

### 1. Сохраняй поведение в точности

Не меняй, что код делает, — меняй только, как он это выражает. Все входы, выходы, побочные эффекты, поведение при ошибках и краевые случаи должны остаться идентичными. Если не уверен, что упрощение сохраняет поведение, — не делай его.

```
СПРАШИВАЙ ПЕРЕД КАЖДЫМ ИЗМЕНЕНИЕМ:
→ Даёт ли это тот же результат на любом входе?
→ Сохраняется ли то же поведение при ошибках?
→ Сохраняются ли те же побочные эффекты и их порядок?
→ Проходят ли все существующие тесты без правок?
```

### 2. Следуй соглашениям проекта

Упрощение означает приведение кода к большей согласованности с кодовой базой, а не навязывание внешних предпочтений. Прежде чем упрощать:

```
1. Прочитай CLAUDE.md / соглашения проекта
2. Изучи, как соседний код решает похожие задачи
3. Подстройся под стиль проекта в части:
   - Порядка импортов и модульной системы
   - Стиля объявления функций
   - Соглашений об именовании
   - Паттернов обработки ошибок
   - Глубины аннотаций типов
```

Упрощение, ломающее согласованность проекта, — не упрощение, а перемешивание.

### 3. Ясность важнее изобретательности

Явный код лучше компактного, когда компактная версия требует умственной паузы на разбор.

```typescript
// НЕЯСНО: плотная цепочка тернарников
const label = isNew ? 'New' : isUpdated ? 'Updated' : isArchived ? 'Archived' : 'Active';

// ЯСНО: читаемое сопоставление
function getStatusLabel(item: Item): string {
  if (item.isNew) return 'New';
  if (item.isUpdated) return 'Updated';
  if (item.isArchived) return 'Archived';
  return 'Active';
}
```

```typescript
// НЕЯСНО: цепочка reduce со встроенной логикой
const result = items.reduce((acc, item) => ({
  ...acc,
  [item.id]: { ...acc[item.id], count: (acc[item.id]?.count ?? 0) + 1 }
}), {});

// ЯСНО: именованный промежуточный шаг
const countById = new Map<string, number>();
for (const item of items) {
  countById.set(item.id, (countById.get(item.id) ?? 0) + 1);
}
```

### 4. Держи баланс

У упрощения есть свой способ провалиться — переупрощение. Следи за этими ловушками:

- **Слишком агрессивное встраивание** — удаление хелпера, дававшего понятию имя, делает место вызова менее читаемым
- **Объединение несвязанной логики** — две простые функции, слитые в одну сложную, не проще
- **Удаление «ненужной» абстракции** — некоторые абстракции существуют ради расширяемости или тестируемости, а не от сложности
- **Оптимизация по числу строк** — меньше строк не цель; цель — быстрее понять

### 5. Ограничивайся тем, что изменилось

По умолчанию упрощай недавно изменённый код. Избегай попутного рефакторинга постороннего кода, если тебя явно не просили расширить границы. Неограниченное упрощение создаёт шум в диффах и рискует непреднамеренными регрессиями.

## Процесс упрощения

### Шаг 1: Пойми, прежде чем трогать (забор Честертона)

Прежде чем что-то менять или удалять, пойми, зачем оно существует. Это забор Честертона: если видишь забор поперёк дороги и не понимаешь, зачем он там, не сноси его. Сначала выясни причину, потом решай, действует ли она до сих пор.

```
ПЕРЕД УПРОЩЕНИЕМ ОТВЕТЬ:
- За что отвечает этот код?
- Что его вызывает? Что вызывает он?
- Каковы краевые случаи и пути с ошибками?
- Есть ли тесты, задающие ожидаемое поведение?
- Почему это могло быть написано именно так? (Производительность? Ограничение платформы? Историческая причина?)
- Посмотри git blame: каков был исходный контекст этого кода?
```

Если не можешь на это ответить — ты не готов упрощать. Сначала прочитай больше контекста.

### Шаг 2: Найди возможности для упрощения

Ищи эти паттерны — каждый из них конкретный сигнал, а не размытый «запах»:

**Структурная сложность:**

| Паттерн | Сигнал | Упрощение |
|---------|--------|----------------|
| Глубокая вложенность (3+ уровня) | Тяжело проследить поток управления | Вынести условия в охранные выражения или хелперы |
| Длинные функции (50+ строк) | Несколько зон ответственности | Разбить на сфокусированные функции с описательными именами |
| Вложенные тернарники | Требуют держать стек в голове | Заменить цепочкой if/else, switch или объектом-справочником |
| Булевы параметры-флаги | `doThing(true, false, true)` | Заменить объектом опций или отдельными функциями |
| Повторяющиеся условия | Одна и та же проверка `if` в нескольких местах | Вынести в предикат с понятным именем |

**Именование и читаемость:**

| Паттерн | Сигнал | Упрощение |
|---------|--------|----------------|
| Обобщённые имена | `data`, `result`, `temp`, `val`, `item` | Переименовать по содержимому: `userProfile`, `validationErrors` |
| Сокращённые имена | `usr`, `cfg`, `btn`, `evt` | Использовать полные слова, если сокращение не общепринято (`id`, `url`, `api`) |
| Вводящие в заблуждение имена | Функция с именем `get`, которая ещё и меняет состояние | Переименовать так, чтобы имя отражало реальное поведение |
| Комментарии, объясняющие «что» | `// увеличиваем счётчик` над `count++` | Удалить комментарий — код и так понятен |
| Комментарии, объясняющие «почему» | `// Повторяем, потому что API нестабилен под нагрузкой` | Оставить — они несут замысел, который код выразить не может |

**Избыточность:**

| Паттерн | Сигнал | Упрощение |
|---------|--------|----------------|
| Дублирующаяся логика | Одни и те же 5+ строк в нескольких местах | Вынести в общую функцию |
| Мёртвый код | Недостижимые ветки, неиспользуемые переменные, закомментированные блоки | Удалить (убедившись, что он действительно мёртв) |
| Ненужные абстракции | Обёртка, не добавляющая ценности | Встроить обёртку, вызывать нижележащую функцию напрямую |
| Переусложнённые паттерны | Фабрика фабрик, стратегия с одной стратегией | Заменить простым прямым подходом |
| Избыточные приведения типов | Приведение к типу, который и так выведен | Убрать приведение |

### Шаг 3: Вноси изменения инкрементально

Делай по одному упрощению за раз. После каждого изменения гоняй тесты. **Отправляй рефакторинг отдельно от фич и багфиксов.** PR, который и рефакторит, и добавляет фичу, — это два PR, разбей их.

```
ДЛЯ КАЖДОГО УПРОЩЕНИЯ:
1. Внеси изменение
2. Прогони набор тестов
3. Тесты прошли → коммить (или переходи к следующему упрощению)
4. Тесты упали → откати и подумай ещё раз
```

Не сваливай несколько упрощений в одно непротестированное изменение. Если что-то сломается, тебе нужно знать, какое именно упрощение виновато.

**Правило 500:** если рефакторинг затронет больше 500 строк, вложись в автоматизацию (кодмоды, sed-скрипты, преобразования AST), а не правь руками. Ручные правки такого масштаба чреваты ошибками и изматывают ревьюера.

### Шаг 4: Проверь результат

После всех упрощений отойди на шаг и оцени целое:

```
СРАВНИ «ДО» И «ПОСЛЕ»:
- Упрощённая версия действительно понятнее?
- Не внёс ли ты новых паттернов, несогласованных с кодовой базой?
- Дифф чистый и пригодный для ревью?
- Одобрил бы это изменение коллега?
```

Если «упрощённая» версия труднее для понимания или ревью — откатывай. Не каждая попытка упрощения удаётся.

## Рекомендации по языкам

### TypeScript / JavaScript

```typescript
// УПРОСТИТЬ: ненужная async-обёртка
// Было
async function getUser(id: string): Promise<User> {
  return await userService.findById(id);
}
// Стало
function getUser(id: string): Promise<User> {
  return userService.findById(id);
}

// УПРОСТИТЬ: многословное условное присваивание
// Было
let displayName: string;
if (user.nickname) {
  displayName = user.nickname;
} else {
  displayName = user.fullName;
}
// Стало
const displayName = user.nickname || user.fullName;

// УПРОСТИТЬ: ручная сборка массива
// Было
const activeUsers: User[] = [];
for (const user of users) {
  if (user.isActive) {
    activeUsers.push(user);
  }
}
// Стало
const activeUsers = users.filter((user) => user.isActive);

// УПРОСТИТЬ: избыточный возврат булева значения
// Было
function isValid(input: string): boolean {
  if (input.length > 0 && input.length < 100) {
    return true;
  }
  return false;
}
// Стало
function isValid(input: string): boolean {
  return input.length > 0 && input.length < 100;
}
```

### Python

```python
# УПРОСТИТЬ: многословная сборка словаря
# Было
result = {}
for item in items:
    result[item.id] = item.name
# Стало
result = {item.id: item.name for item in items}

# УПРОСТИТЬ: вложенные условия с ранним возвратом
# Было
def process(data):
    if data is not None:
        if data.is_valid():
            if data.has_permission():
                return do_work(data)
            else:
                raise PermissionError("No permission")
        else:
            raise ValueError("Invalid data")
    else:
        raise TypeError("Data is None")
# Стало
def process(data):
    if data is None:
        raise TypeError("Data is None")
    if not data.is_valid():
        raise ValueError("Invalid data")
    if not data.has_permission():
        raise PermissionError("No permission")
    return do_work(data)
```

### React / JSX

```tsx
// УПРОСТИТЬ: многословная условная отрисовка
// Было
function UserBadge({ user }: Props) {
  if (user.isAdmin) {
    return <Badge variant="admin">Admin</Badge>;
  } else {
    return <Badge variant="default">User</Badge>;
  }
}
// Стало
function UserBadge({ user }: Props) {
  const variant = user.isAdmin ? 'admin' : 'default';
  const label = user.isAdmin ? 'Admin' : 'User';
  return <Badge variant={variant}>{label}</Badge>;
}

// УПРОСТИТЬ: прокидывание пропсов через промежуточные компоненты
// Было — подумай, не решает ли это лучше контекст или композиция.
// Это вопрос суждения — отметь его, но не рефактори автоматически.
```

## Типовые самооправдания

| Самооправдание | Как на самом деле |
|---|---|
| «Работает — не трогай» | Работающий код, который трудно читать, будет трудно чинить, когда он сломается. Упростив сейчас, экономишь время на каждом будущем изменении. |
| «Меньше строк — всегда проще» | Однострочный вложенный тернарник не проще пятистрочного if/else. Простота — про скорость понимания, а не про число строк. |
| «Заодно быстренько упрощу и вот этот посторонний код» | Неограниченное упрощение создаёт шумные диффы и рискует регрессиями в коде, который ты менять не собирался. Держи фокус. |
| «Типы делают код самодокументируемым» | Типы документируют структуру, а не замысел. Хорошо названная функция объясняет *почему* лучше, чем сигнатура типа объясняет *что*. |
| «Эта абстракция может пригодиться потом» | Не сохраняй умозрительные абстракции. Если она не используется сейчас — это сложность без ценности. Удали и добавь снова, когда понадобится. |
| «У автора наверняка была причина» | Возможно. Посмотри git blame — примени забор Честертона. Но у накопленной сложности часто нет причины, это просто осадок от итераций под давлением. |
| «Отрефакторю заодно с добавлением фичи» | Отделяй рефакторинг от работы над фичей. Смешанные изменения труднее ревьюить, откатывать и понимать в истории. |

## Тревожные признаки

- Упрощение, потребовавшее правки тестов, чтобы они прошли (скорее всего, ты изменил поведение)
- «Упрощённый» код, который длиннее и труднее для понимания, чем оригинал
- Переименования под свои вкусы, а не под соглашения проекта
- Удаление обработки ошибок, потому что «так код чище»
- Упрощение кода, который ты понял не до конца
- Сваливание множества упрощений в один большой коммит, который трудно отревьюить
- Рефакторинг кода за пределами текущей задачи без запроса

## Проверка

После прохода упрощения:

- [ ] Все существующие тесты проходят без правок
- [ ] Сборка успешна, новых предупреждений нет
- [ ] Линтер/форматтер проходит (стилевых регрессий нет)
- [ ] Каждое упрощение — отдельное инкрементальное изменение, пригодное для ревью
- [ ] Дифф чистый — посторонних изменений не подмешано
- [ ] Упрощённый код следует соглашениям проекта (сверено с CLAUDE.md или аналогом)
- [ ] Обработка ошибок нигде не удалена и не ослаблена
- [ ] Мёртвый код не остался (неиспользуемые импорты, недостижимые ветки)
- [ ] Коллега или агент-ревьюер одобрил бы изменение как чистое улучшение

