Code review в Kotlin: что проверять и как давать обратную связь
Code review (ревью кода) — это не просто формальность, а мощный инструмент для повышения качества проекта и роста команды. Для начинающих разработчиков на Kotlin процесс ревью часто вызывает страх: «А вдруг я пропущу баг?», «Как сказать коллеге, что его код плохой?». В этой статье мы разберём, на что обращать внимание при проверке Kotlin-кода и как формулировать замечания, чтобы они были полезными, а не обидными.
Мы не будем учить вас основам синтаксиса — предполагается, что вы уже пишете на Kotlin. Вместо этого сфокусируемся на типичных проблемах, которые встречаются в коде новичков, и на том, как давать обратную связь, не ломая карму.
1. Null safety и работа с nullable типами
Kotlin гордится своей системой типов, которая устраняет большую часть NullPointerException на этапе компиляции. Однако новички часто злоупотребляют оператором !! (not-null assertion) или неправильно обрабатывают nullable-значения.
Что проверять:
- Используется ли
!!без веской причины? Если вы видитеsomething!!, спросите автора: «Почему ты уверен, что здесь не будет null? Может, лучше использовать?.letили?:?». - Корректно ли обрабатываются цепочки вызовов через
?.? Например,user?.address?.city— безопасно, но если нужен city, лучше использовать?:для значения по умолчанию. - Не забыт ли
lateinit varинициализировать перед использованием? Проверьте, что все lateinit поля точно будут проинициализированы до первого чтения.
// Плохо
fun getUserCity(user: User?): String {
return user!!.address!!.city // Риск NPE
}
// Хорошо
fun getUserCitySafe(user: User?): String {
return user?.address?.city ?: "Unknown"
}
Как давать обратную связь: Вместо «Ты написал плохой код» скажите: «Здесь может быть null, давай заменим !! на безопасный вызов, чтобы избежать падения в рантайме». Предложите альтернативу — это учит, а не критикует.
2. Идиоматичность Kotlin: коллекции, scope-функции и sealed class
Kotlin — не просто «Java с другим синтаксисом». Многие новички пишут на Kotlin так, будто всё ещё работают в Java: используют циклы for вместо функций коллекций, забывают про apply, let, run и не применяют sealed class для представления состояний.
Что проверять:
- Используются ли функции коллекций (
map,filter,firstOrNull) вместо ручных циклов? Это делает код лаконичнее и читаемее. - Есть ли избыточные временные переменные? Например,
val result = list.filter { ... }.map { ... }лучше, чем разбивать на три шага с мутабельными списками. - Используются ли
sealed classдля иерархий, где нужно обработать все варианты (например, Result, UiState)? Это обязательное требование в современных Kotlin-проектах.
// Плохо (Java-style)
fun getEvenSquares(list: List<Int>): List<Int> {
val result = mutableListOf<Int>()
for (i in list.indices) {
if (list[i] % 2 == 0) {
result.add(list[i] * list[i])
}
}
return result
}
// Хорошо (Kotlin-idiomatic)
fun getEvenSquaresIdiomatic(list: List<Int>): List<Int> {
return list.filter { it % 2 == 0 }.map { it * it }
}
Как давать обратную связь: Укажите на конкретную идиому: «Вместо цикла for можно использовать filter + map, это короче и понятнее. Хочешь, покажу пример?». Не навязывайте — предложите обсудить.
3. Архитектура и тестируемость
Даже если проект маленький, code review — отличный момент проверить, как код вписывается в общую архитектуру. Новички часто смешивают логику, нарушают Single Responsibility Principle или забывают про инъекцию зависимостей.
Что проверять:
- Не содержит ли функция слишком много ответственности? Если метод делает и валидацию, и сохранение, и отправку уведомления — это повод разбить его.
- Есть ли тесты? Проверьте, что для новой логики написаны unit-тесты. Если тестов нет, но код сложный — это красный флаг.
- Используются ли зависимости правильно? Например, в Android-проектах проверьте, не создаётся ли Presenter/ViewModel внутри Activity — это усложняет тестирование.
// Плохо: смешиваем логику
class OrderProcessor {
fun processOrder(order: Order) {
if (order.total > 100) { // бизнес-логика
// отправка email (инфраструктура)
EmailSender.send(order.email, "Order confirmed")
}
// сохранение в БД (работа с данными)
database.save(order)
}
}
// Хорошо: разделяем на слои
class OrderProcessor(
private val emailSender: EmailSender,
private val orderRepository: OrderRepository
) {
fun processOrder(order: Order) {
if (order.total > 100) {
emailSender.sendConfirmation(order)
}
orderRepository.save(order)
}
}
Как давать обратную связь: Сфокусируйтесь на принципах, а не на личных предпочтениях. Например: «Эта функция делает три разных дела. Если мы вынесем отправку email в отдельный класс, её будет проще тестировать и переиспользовать». Спрашивайте мнение автора — возможно, у него есть веские причины.
4. Обратная связь: как не обидеть и научить
Теперь о самом сложном — о человеческом факторе. Даже идеальные замечания могут быть восприняты в штыки, если их неправильно подать. Вот несколько правил для начинающих ревьюеров:
- Начинайте с позитива. Найдите в коде что-то хорошее: удачное название переменной,