Приложение Г: Чеклист код-ревью — на что смотреть при проверке своего кода
🎯 Зачем этот чеклист
Код-ревью — это не только проверка чужого кода. Самопроверка перед коммитом экономит время команды и помогает избежать стыдных багов в продакшене. Этот чеклист — твой контрольный список перед тем, как нажать "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-запросах параметры передаются через биндинг
// ❌ ПЛОХО
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())
{{-- ❌ ПЛОХО --}}
{!! $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()
// ❌ ПЛОХО
if ($user->role == 'admin') { ... }
// ✅ ХОРОШО
if ($user->hasRole(Role::ADMIN)) { ... }
// или
$this->authorize('update', $post);Загрузка файлов
- [ ] Проверка MIME-типа и расширения
- [ ] Ограничение размера файла
- [ ] Файлы сохраняются вне публичной директории или с уникальными именами
- [ ] Нет исполнения загруженных файлов
🗄️ База данных
Запросы
- [ ] Нет N+1 проблемы (используется
with()для связей) - [ ] Тяжёлые запросы вынесены в scopes
- [ ] Используются индексы на часто фильтруемых полях
- [ ] Пагинация для больших списков
// ❌ ПЛОХО — 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) - [ ] Сообщения об ошибках понятны пользователю
// ❌ ПЛОХО
$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()
// ❌ ПЛОХО — толстый контроллер
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 для связей
// ❌ ПЛОХО — вся таблица в память
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"
// ❌ ПЛОХО
// Получаем пользователя
$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()в конфигах
// ❌ ПЛОХО
$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 статус-коды корректны
// ❌ ПЛОХО
$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 для форматирования ответов
// ✅ Консистентный формат
return response()->json([
'data' => $users,
'meta' => [
'total' => $total,
'page' => $page,
],
]);🎨 Frontend (Blade)
- [ ] Нет PHP-логики в шаблонах (вынеси в контроллер/view composer)
- [ ] Используются компоненты для переиспользуемых элементов
- [ ]
@error,@auth,@guestвместо@if - [ ] CSRF токены в формах
- [ ] Нет инлайн-стилей и скриптов (используй Vite)
{{-- ❌ ПЛОХО --}}
@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)
□ Нет чувствительных данных в коде
□ Коммит-сообщение понятно
□ Тесты проходятПомни: хороший код — это не тот, который работает, а тот, который легко читать, поддерживать и изменять. Этот чеклист — не бюрократия, а способ писать код, за который не будет стыдно через полгода. 🚀