Skip to content

Приложение Г: Чеклист код-ревью — на что смотреть при проверке своего кода

🎯 Зачем этот чеклист

Код-ревью — это не только проверка чужого кода. Самопроверка перед коммитом экономит время команды и помогает избежать стыдных багов в продакшене. Этот чеклист — твой контрольный список перед тем, как нажать "Create Pull Request".

Как пользоваться:

  • Пробегись по списку перед коммитом
  • Используй как основу для code review в команде
  • Адаптируй под свой проект (убери лишнее, добавь специфичное)

📋 Общие принципы

✅ Работает ли код?

  • [ ] Код выполняет то, что заявлено в задаче
  • [ ] Нет очевидных багов или логических ошибок
  • [ ] Обработаны граничные случаи (пустые массивы, null, 0, отрицательные числа)
  • [ ] Код работает с неожиданными данными (слишком длинные строки, спецсимволы)

✅ Читаемость и понятность

  • [ ] Код легко читается без комментариев
  • [ ] Названия переменных и функций понятны и точны
  • [ ] Нет магических чисел (замени if ($status == 3) на if ($status == self::STATUS_ACTIVE))
  • [ ] Функции делают одно дело и названы по действию
  • [ ] Классы имеют чёткую ответственность

✅ Избыточность и дублирование

  • [ ] Нет копипасты (DRY — Don't Repeat Yourself)
  • [ ] Повторяющаяся логика вынесена в методы/функции
  • [ ] Не переизобретается велосипед (есть ли готовое решение в Laravel?)

🔒 Безопасность

SQL-инъекции

  • [ ] Все SQL-запросы используют prepared statements или Eloquent
  • [ ] Нет конкатенации пользовательских данных в SQL
  • [ ] В raw-запросах параметры передаются через биндинг
php
// ❌ ПЛОХО
DB::select("SELECT * FROM users WHERE email = '$email'");

// ✅ ХОРОШО
DB::select("SELECT * FROM users WHERE email = ?", [$email]);
// или
User::where('email', $email)->first();

XSS (межсайтовый скриптинг)

  • [ ] Данные от пользователя экранируются при выводе ( в Blade, не {!! !!})
  • [ ] HTML от пользователя фильтруется (HTMLPurifier)
  • [ ] JSON-ответы не строятся вручную (используй response()->json())
blade
{{-- ❌ ПЛОХО --}}
{!! $user->bio !!}

{{-- ✅ ХОРОШО --}}
{{ $user->bio }}

{{-- ✅ ХОРОШО если нужен HTML --}}
{!! Purifier::clean($user->bio) !!}

CSRF

  • [ ] Все POST/PUT/DELETE формы имеют @csrf
  • [ ] API использует Sanctum/Passport токены
  • [ ] Изменяющие данные GET-запросы запрещены

Авторизация и аутентификация

  • [ ] Проверка прав доступа перед действиями (policies/gates)
  • [ ] Чувствительные маршруты защищены middleware auth
  • [ ] Нет хардкода ролей (используй константы или enum)
  • [ ] Пароли хешируются через Hash::make() или bcrypt()
php
// ❌ ПЛОХО
if ($user->role == 'admin') { ... }

// ✅ ХОРОШО
if ($user->hasRole(Role::ADMIN)) { ... }
// или
$this->authorize('update', $post);

Загрузка файлов

  • [ ] Проверка MIME-типа и расширения
  • [ ] Ограничение размера файла
  • [ ] Файлы сохраняются вне публичной директории или с уникальными именами
  • [ ] Нет исполнения загруженных файлов

🗄️ База данных

Запросы

  • [ ] Нет N+1 проблемы (используется with() для связей)
  • [ ] Тяжёлые запросы вынесены в scopes
  • [ ] Используются индексы на часто фильтруемых полях
  • [ ] Пагинация для больших списков
php
// ❌ ПЛОХО — N+1
foreach ($posts as $post) {
    echo $post->author->name; // запрос на каждую итерацию
}

// ✅ ХОРОШО
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name;
}

Миграции

  • [ ] Миграции idempotent (можно откатить и применить заново)
  • [ ] Есть метод down()
  • [ ] Изменения БД описаны в коммите
  • [ ] Внешние ключи с каскадным удалением/обновлением где нужно

Eloquent

  • [ ] $fillable или $guarded определены
  • [ ] Нет массовых присвоений без валидации
  • [ ] Связи названы правильно (единственное число для belongsTo, множественное для hasMany)

✨ Laravel специфика

Валидация

  • [ ] Данные валидируются до использования (FormRequest или $request->validate())
  • [ ] Правила валидации покрывают все граничные случаи
  • [ ] Кастомные правила вынесены в классы (Rule)
  • [ ] Сообщения об ошибках понятны пользователю
php
// ❌ ПЛОХО
$user = User::create($request->all());

// ✅ ХОРОШО
$validated = $request->validate([
    'email' => 'required|email|unique:users',
    'password' => 'required|min:8|confirmed',
]);
$user = User::create($validated);

Маршруты

  • [ ] Используются именованные маршруты
  • [ ] Группировка маршрутов с общим middleware
  • [ ] RESTful naming (index, show, store, update, destroy)
  • [ ] Маршруты API в routes/api.php

Middleware

  • [ ] Логика авторизации/проверок в middleware, а не в контроллерах
  • [ ] Middleware применяется к нужным маршрутам
  • [ ] Нет дублирования логики middleware в контроллерах

Контроллеры

  • [ ] Тонкие контроллеры (логика в сервисах/экшенах)
  • [ ] Один метод — одна задача
  • [ ] Resource-контроллеры для CRUD
  • [ ] Использование dependency injection вместо new Class()
php
// ❌ ПЛОХО — толстый контроллер
public function store(Request $request) {
    $validated = $request->validate([...]);
    $user = User::create($validated);
    $profile = Profile::create(['user_id' => $user->id]);
    Mail::to($user)->send(new WelcomeMail($user));
    event(new UserRegistered($user));
    return redirect('/dashboard');
}

// ✅ ХОРОШО — делегирование
public function store(StoreUserRequest $request, UserService $service) {
    $user = $service->register($request->validated());
    return redirect('/dashboard');
}

🧪 Тестирование

  • [ ] Критическая логика покрыта тестами
  • [ ] Тесты проходят (запусти php artisan test)
  • [ ] Тесты изолированы (не зависят друг от друга)
  • [ ] Используется RefreshDatabase в feature-тестах
  • [ ] Моки и фейки для внешних сервисов

🚀 Производительность

Общее

  • [ ] Нет лишних запросов к БД в циклах
  • [ ] Тяжёлые операции вынесены в очереди
  • [ ] Кеширование для часто запрашиваемых данных
  • [ ] Нет SELECT * без необходимости

Laravel специфика

  • [ ] Используется ленивая коллекция для больших данных (cursor())
  • [ ] Chunk для обработки массивов записей
  • [ ] Eager loading для связей
php
// ❌ ПЛОХО — вся таблица в память
User::all()->each(fn($user) => $user->notify(...));

// ✅ ХОРОШО
User::chunk(100, fn($users) => $users->each(...));

📝 Код-стиль и стандарты

PSR

  • [ ] PSR-12 соблюдён (запусти ./vendor/bin/pint)
  • [ ] Отступы 4 пробела
  • [ ] Открывающая фигурная скобка класса/метода на новой строке
  • [ ] declare(strict_types=1); в начале файлов

Названия

  • [ ] Классы в PascalCase (UserController)
  • [ ] Методы и переменные в camelCase (getUserName)
  • [ ] Константы в UPPER_CASE (MAX_ATTEMPTS)
  • [ ] Таблицы БД в snake_case множественного числа (user_profiles)

Комментарии

  • [ ] Комментарии объясняют "почему", а не "что"
  • [ ] Нет закомментированного кода
  • [ ] PHPDoc для публичных методов с непонятной сигнатурой
  • [ ] TODO с контекстом, а не просто "fix this"
php
// ❌ ПЛОХО
// Получаем пользователя
$user = User::find($id);

// ✅ ХОРОШО
// Используем find вместо findOrFail, потому что на этом этапе
// пользователь мог быть удалён асинхронно
$user = User::find($id);

🌳 Git и коммиты

  • [ ] Коммит содержит логически завершённое изменение
  • [ ] Сообщение коммита понятно (не "fix", а "Fix N+1 query in posts index")
  • [ ] Нет лишних файлов (.env, node_modules, .DS_Store)
  • [ ] Конфликты разрешены
  • [ ] Ветка обновлена относительно main/develop

🔧 Конфигурация и окружение

  • [ ] Чувствительные данные в .env, не в коде
  • [ ] Используются конфиги (config('app.name') вместо env('APP_NAME'))
  • [ ] .env.example обновлён при добавлении новых переменных
  • [ ] Дефолтные значения для env() в конфигах
php
// ❌ ПЛОХО
$apiKey = env('API_KEY');

// ✅ ХОРОШО в config/services.php
return [
    'stripe' => [
        'key' => env('STRIPE_KEY'),
    ],
];
// и в коде
$apiKey = config('services.stripe.key');

🐛 Обработка ошибок

  • [ ] Нет dd(), var_dump(), print_r() в коммите
  • [ ] Используется try-catch для внешних сервисов
  • [ ] Ошибки логируются (Log::error())
  • [ ] Пользователю показываются понятные сообщения, не технические детали
  • [ ] HTTP статус-коды корректны
php
// ❌ ПЛОХО
$data = file_get_contents($url);

// ✅ ХОРОШО
try {
    $data = file_get_contents($url);
} catch (\Exception $e) {
    Log::error("Failed to fetch data from $url: " . $e->getMessage());
    return response()->json(['error' => 'Service unavailable'], 503);
}

📱 API (если применимо)

  • [ ] Версионирование (/api/v1/...)
  • [ ] Консистентные JSON-ответы
  • [ ] Пагинация для списков
  • [ ] Rate limiting
  • [ ] CORS настроен правильно
  • [ ] API Resources для форматирования ответов
php
// ✅ Консистентный формат
return response()->json([
    'data' => $users,
    'meta' => [
        'total' => $total,
        'page' => $page,
    ],
]);

🎨 Frontend (Blade)

  • [ ] Нет PHP-логики в шаблонах (вынеси в контроллер/view composer)
  • [ ] Используются компоненты для переиспользуемых элементов
  • [ ] @error, @auth, @guest вместо @if
  • [ ] CSRF токены в формах
  • [ ] Нет инлайн-стилей и скриптов (используй Vite)
blade
{{-- ❌ ПЛОХО --}}
@if ($errors->has('email'))
    <span>{{ $errors->first('email') }}</span>
@endif

{{-- ✅ ХОРОШО --}}
@error('email')
    <span>{{ $message }}</span>
@enderror

🧹 Финальная проверка

Перед созданием PR:

  • [ ] Запусти composer test — всё зелёное?
  • [ ] Запусти ./vendor/bin/pint — код-стиль в порядке?
  • [ ] Открой изменённые файлы в IDE — нет ошибок от PHPStan/Psalm?
  • [ ] Протестируй в браузере основной сценарий
  • [ ] Проверь консоль браузера на JS-ошибки
  • [ ] Прочитай diff ещё раз — нет лишнего?

💡 Советы

Не проверяй всё сразу
Используй этот чеклист поэтапно. Сначала — безопасность и баги, потом — читаемость, в конце — стиль.

Автоматизируй

  • Настрой Git hooks с Pint/PHPStan
  • Используй CI/CD для автоматического прогона тестов
  • Линтеры в IDE подскажут проблемы до коммита

Адаптируй под проект
Добавь в чеклист специфичные для твоего проекта проверки:

  • Используем ли мы сервисный слой?
  • Обязательны ли докблоки?
  • Нужны ли интеграционные тесты?

Учись на ревью
Если тебе часто указывают на одно и то же — добавь это в свой чеклист и проверяй перед коммитом.


📚 Дополнительные ресурсы


✅ Краткая версия (для ежедневного использования)

Распечатай и держи перед глазами:

□ Код работает и покрыт тестами
□ Нет SQL-инъекций (prepared statements)
□ Нет XSS ({{ }} экранирует)
□ Данные валидируются
□ Нет N+1 запросов (with())
□ Нет dd(), var_dump()
□ PSR-12 соблюдён (Pint)
□ Нет чувствительных данных в коде
□ Коммит-сообщение понятно
□ Тесты проходят

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

Выпущено под лицензией MIT