Kotlin в команде: как перестать спорить о форматировании и начать писать код
Вы когда-нибудь проводили code review, где половина комментариев сводилась к «поставь пробел», «убери пробел» или «почему у тебя CamelCase, а не snake_case»? Если да — добро пожаловать в клуб. В любой команде рано или поздно встаёт вопрос: code style и конвенции Kotlin. И если не договориться заранее, каждый будет тащить свою «идеальную» расстановку скобок.
Эта статья — не сухой перечень правил. Это практическое руководство, как сделать код на Kotlin читаемым, единообразным и удобным для всей команды. Мы разберём ключевые конвенции, инструменты автоматизации и лайфхаки, которые сэкономят вам часы споров на созвонах.
Почему code style в Kotlin — это не про эстетику, а про деньги
Давайте честно: бизнесу всё равно, ставите вы фигурную скобку на той же строке или на новой. Но бизнесу не всё равно, сколько времени уходит на вливание нового разработчика в проект. Исследования показывают, что разработчики тратят до 40% времени на чтение кода. Если код написан в едином code style Kotlin, этот процесс ускоряется в разы.
Когда в проекте используется единый стиль кода Kotlin:
- Новые члены команды быстрее понимают структуру проекта.
- Code review проходит быстрее — ревьюверы ищут ошибки логики, а не пробелы.
- Снижается количество багов из-за неверного форматирования (да, бывает и такое).
- Git-диффы становятся чище — меньше шума от переформатирования.
Поэтому конвенции Kotlin — это не прихоть лида, а инструмент повышения эффективности. И сейчас мы разберём, как их внедрить без боли.
Официальный гайд: Kotlin Coding Conventions
Начнём с базы: у JetBrains есть официальный документ — Kotlin coding conventions. Это не закон, но мудрые люди рекомендуют его как отправную точку. Давайте пробежимся по ключевым моментам, которые чаще всего вызывают споры.
Именование: CamelCase forever
Kotlin использует CamelCase для всего: классы — с большой буквы (MyAwesomeClass), функции и переменные — с маленькой (myAwesomeFunction). Константы верхнего уровня или внутри объекта — SCREAMING_SNAKE_CASE. Никаких подчёркиваний в именах переменных — это не Python.
// Правильно
class UserProfile
val userName: String
fun getUserData(): UserData
// Неправильно
class user_profile
val user_name: String
fun get_user_data(): UserData
Фигурные скобки: открываем на той же строке
Kotlin следует Java-традиции: открывающая скобка — на той же строке, что и объявление. Это не обсуждается, если вы не хотите прослыть еретиком.
// Kotlin way
fun processData(data: List<String>) {
data.forEach { item ->
println(item)
}
}
// Не-Kotlin way (но если вся команда так пишет — ок)
fun processData(data: List<String>)
{
data.forEach { item ->
println(item)
}
}
Пробелы: не экономьте на них
Ставьте пробелы после ключевых слов (if, for, when), вокруг бинарных операторов и после запятых. Это повышает читаемость.
// Читаемо
if (x > 0 && y < 10) {
println("Hello, World!")
}
// Тяжело для глаз
if(x>0&&y<10){
println("Hello, World!")
}
Автоформатирование: ваш лучший друг
Ручная проверка code style — это прошлый век. В Kotlin есть встроенный форматтер в IntelliJ IDEA: Ctrl+Alt+L (или Cmd+Option+L на Mac). Но если у вас в команде кто-то использует VS Code или другой редактор — проблемы неизбежны.
Решение: ktlint и Spotless.
ktlint: универсальный солдат
ktlint — это инструмент, который проверяет и автоматически исправляет форматирование Kotlin-кода. Он не зависит от IDE и работает из командной строки, CI/CD и pre-commit хуков.
// Запуск проверки
ktlint "src/**/*.kt"
// Автоисправление
ktlint -F "src/**/*.kt"
Добавьте ktlint в build.gradle.kts, и код будет проверяться при каждой сборке:
plugins {
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
}
Теперь никто не сможет закоммитить код с лишними пробелами — CI просто упадёт.
Соглашения, которые выходят за рамки форматирования
Code style Kotlin — это не только пробелы и скобки. Есть более глубокие конвенции, которые делают код по-настоящему «котиновым».
Используйте scope functions осознанно
let, apply, run, with, also — мощные инструменты, но их легко переиспользовать. Договоритесь в команде, для чего вы используете каждую:
let— для выполнения действий с non-null объектами (часто с?.)apply— для конфигурации объекта (возвращает сам объект)run— для вычислений в контексте объектаalso— для побочных эффектов (логирование, отладка)
// Хороший пример: конфигурация через apply
val user = User().apply {
name = "Alice"
age = 30
email = "alice@example.com"
}
// Плохой пример: let там, где нужен apply
val user = User().let {
it.name = "Alice"
it.age = 30
it
}
Избегайте цепочек из scope functions
Это антипаттерн. Если вы пишете something?.let { ... }?.apply { ... }?.also { ... }, скорее всего, вы делаете что-то не так. Один вызов — хорошо, два — терпимо, три — пора рефакторить.
Data class — ваш друг, но не злоупотребляйте
data class автоматически генерирует equals, hashCode, toString, copy и componentN. Это прекрасно, но не используйте data class для классов с бизнес-логикой. Data class — это просто контейнер данных.
// Отлично
data class Address(val street: String, val city: String, val zip: String)
// Плохо: data class с методами