Чистый код: зачем он нужен (и почему это не занудство)
Чистый код - это не «красота ради красоты». Это скорость.
Код читают в 10 раз чаще, чем пишут. И чаще всего его читаешь ты сам... через месяц, когда уже забыл, что хотел сказать автор (спойлер: автор - ты).
Что даёт чистый код
- быстрее добавлять фичи - не нужно разбираться, что тут происходит
- проще чинить баги - видно, где логика сломалась
- легче тестировать - маленькие понятные функции покрываются за минуты
- меньше страха трогать старые места
Стоимость грязного кода: технический долг
Грязный код - это технический долг (tech debt). Как и финансовый долг, он растёт с процентами:
Месяц 1: «Потом поправлю, сейчас горит»
Месяц 3: «Не трогай этот файл, он хрупкий»
Месяц 6: «Новую фичу проще написать заново, чем встроить»
Месяц 12: «Нам нужен рефакторинг на 3 спринта»
В проектах с высоким tech debt разработчики тратят до 40% времени на борьбу с плохим кодом вместо создания ценности. Это не абстрактная проблема - это реальные часы, за которые платит команда.
Как понять, что код «грязный»
Есть шутливая, но точная метрика:
Качество кода = количество "WTF" за минуту при ревью
А есть измеримые показатели:
Метрика Что показывает
────────────────────────── ─────────────────────────────────
Цикломатическая сложность Сколько путей выполнения в функции
Когнитивная сложность Насколько сложно человеку понять логику
Глубина вложенности Сколько уровней if/for/switch
Длина функции Слишком длинная → делает слишком много
Покрытие тестами Есть ли сеть безопасности
В Go для проверки есть golangci-lint - он считает десятки метрик за секунды:
// Цикломатическая сложность 1 - идеально
func IsActive(user User) bool {
return user.Status == "active"
}
// Цикломатическая сложность 5 - уже стоит задуматься
func ProcessOrder(order Order) error {
if order.User == nil { return ErrNoUser }
if order.Items == nil { return ErrNoItems }
for _, item := range order.Items {
if item.Price <= 0 { return ErrInvalidPrice }
if item.Qty <= 0 { return ErrInvalidQty }
}
return nil
}
<?php
declare(strict_types=1);
// Цикломатическая сложность 1 - идеально
final readonly class UserStatus
{
public static function isActive(User $user): bool
{
return $user->status === 'active';
}
}
// Цикломатическая сложность 5 - уже стоит задуматься
final readonly class OrderProcessor
{
public function process(Order $order): void
{
if ($order->user === null) {
throw new \DomainException('order has no user');
}
if ($order->items === []) {
throw new \DomainException('order has no items');
}
foreach ($order->items as $item) {
if ($item->price <= 0) {
throw new \DomainException('invalid item price');
}
if ($item->qty <= 0) {
throw new \DomainException('invalid item qty');
}
}
}
}
В PHP сложность измеряет phpmd (правило CyclomaticComplexity) или phpstan через phpstan/phpstan-strict-rules. Symfony-проекты часто настраивают порог через phpmd ruleset.xml в CI - аналог golangci-lint для Go.
Правило бойскаута
Роберт Мартин (автор «Чистого кода») предложил простое правило:
Оставь код чище, чем нашёл.
Не нужно переписывать весь файл. Достаточно каждый раз делать одно маленькое улучшение:
- переименовал непонятную переменную
- вынес дублирующийся блок в функцию
- добавил контекст к ошибке
- удалил мёртвый комментарий
Без правила бойскаута: С правилом бойскаута:
Код деградирует с каждым PR Код улучшается с каждым PR
───────────────────────────── ─────────────────────────────
Sprint 1: ████████░░ 80% Sprint 1: ████████░░ 80%
Sprint 5: ██████░░░░ 60% Sprint 5: █████████░ 85%
Sprint 10: ████░░░░░░ 40% Sprint 10: █████████░ 90%
Самая частая боль новичка
«Я боюсь что-то менять, потому что сломаю всё».
Чистый код уменьшает этот страх: изменения становятся маленькими и предсказуемыми. Но есть и второй компонент - тесты (Go, PHP). Чистый код без тестов - как ремень безопасности без подушки: лучше, чем ничего, но защита неполная.
Чистый код + тесты → «Меняю уверенно, тесты покажут если сломал»
Чистый код - тесты → «Выглядит понятно, но менять всё равно страшно»
Грязный код + тесты → «Тесты зелёные, но что именно они тестируют?»
Грязный код - тесты → «Не трогай, работает»
Чистый код в Go: культура языка
Go как язык поощряет чистый код на уровне дизайна:
gofmt- единый формат, споры о стиле исключеныgo vet- статический анализ ошибок- Короткие имена для коротких скоупов, длинные - для широких
- Ошибки как значения - нельзя «случайно» проигнорировать
- Неявные интерфейсы - меньше связности между пакетами
Если ты пишешь на Go и следуешь конвенциям языка - ты уже на полпути к чистому коду.
Пять принципов чистого кода
Этот трек построен вокруг пяти ключевых идей:
1. Понятные имена → урок 2
2. Маленькие функции → урок 3
3. Плоская структура → урок 4 (ранние return)
4. Явные ошибки → урок 5
5. Осмысленная структура → урок 7
Каждый принцип - маленький шаг. Вместе они дают код, который приятно читать и безопасно менять.
Мини-задание
- Найди в своём проекте файл, который страшно открывать
- Посчитай максимальную глубину вложенности (сколько уровней
if/for) - Примени правило бойскаута: сделай одно маленькое улучшение и закоммить
- Запусти
golangci-lint run ./...и посмотри, какие метрики он покажет