Kotlin code style


    За полтора года работы с языком Kotlin, мы перевели на него все свои проекты и фреймворки. Чтобы разработчики могли быстрее включаться в работу над проектом, а код ревью не превращался в бесконечный спор, мы решили формализовать накопленный опыт и разработали собственный код-стайл.


    Поехали!


    Каждый разработчик пишет код исходя из собственного опыта и привычек. Если на проекте один разработчик или код пишется для себя, это не проблема. Но в больших командах, работающих над несколькими проектами, неизбежно возникают проблемы:


    • на ревью кода уходит много времени и нервов — без единого стандарта спорить можно до бесконечности;
    • нужно куда больше времени и сил для погружения нового человека в код;
    • сложнее концентрироваться на функции, которую выполняет код;
    • в результате проект выглядит, как отпечаток всех специалистов, которые принимали в нем участие.

    И даже если сейчас вы единственный разработчик на проекте, не забывайте — когда-нибудь это закончится. И только от вас зависит, будет ли новый человек в команде вам благодарен или первым делом попытается разузнать ваш адрес.


    Наличие единого стиля написания кода позволяет решает все эти проблемы.


    Почему сейчас и почему мы


    Kotlin довольно популярен в сообществе Android-разработчиков и с каждым днем собирает все больше сторонников. Но отыскать стиль написания кода, которым можно руководствоваться, по-прежнему проблема, а соглашения, опубликованные на официальном сайте языка не дают исчерпывающих ответов, так как то количество правил, которые там присутствуют, едва ли могут покрыть большинство потребностей команды. Например, не содержат правил форматирования функций, их вызов и описания.


    Во время написания статьи вышел официальный гайд от Google с ответами на вопросы о написании кода на Kotlin, что в некоторой степени повлияло на статью. Нам было интересно сравнить результаты и проанализировать, в чем наши мнения сошлись и с чем мы оказались не согласны.


    О процессе разработки правил


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


    Конечно, с течением времени набор правил может пересматриваться и дополняться — мы планируем поддерживать свой набор соглашений в актуальном состоянии.


    Спорные моменты


    С нашим представлением о том, как должен выглядеть код, вы сможете ознакомиться по ссылке в конце статьи, а ниже мы хотели бы представить особо спорные и противоречивые моменты.


    1. Имена полей View и Kotlin Android Extensions


    Правило

    Для полей View из Kotlin Android Extensions используется стиль именования lower_snake_case


    При разработке стандарта мы старались абстрагироваться от внешних зависимостей или возможностей использования IDE и сосредоточится на языке в чистом виде. Но все же сделали исключение для Kotlin Android Extensions. Поскольку чаще всего мы используем Kotlin в Android-разработке, а приложения не обходятся без использования XML, так как в этом формате в стиле lower_snake_case описывается большинство ресурсов. Таким образом, мы сделали исключение для идентификаторов View (их мы описываем как раз в таком стиле) и обращений к этим полям (хотя если посмотреть как эта магия работает в рантайме, окажется, что это не переменные, а ключи для получения значений из HashMap). Из кода выглядит как обращение к полю, написанному в стиле lower_snake_case, но при этом в коде всегда видно, что происходит обращение к полю View.


    2. Порядок private методов


    Правило

    Структура класса:
    1) companion object
    2) Поля: abstract, override, public, internal, protected, private
    3) Блок инициализации: init, конструкторы
    4) Абстрактные методы
    5) Переопределенные методы родительского класса (желательно в том же порядке, в каком они следуют в родительском классе)
    6) Реализации методов интерфейсов (желательно в том же порядке, в каком они следуют в описании класса, соблюдая при этом порядок описания этих методов в самом интерфейсе)
    7) public методы
    8) internal методы
    9) protected методы
    10) private методы
    11) inner классы


    Еще одним спорным моментом было расположение приватных методов. Есть два распространенных варианта: писать их сразу после метода, где они использовались, или в структуре класса после всех методов. Мы сошлись на втором варианте. Так как мы рассматриваем класс как некоторый интерфейс, в первую очередь интересно узнать, что он делает — благодаря тому, что все приватные методы располагаются в самом конце, можно быстро понять ответственность этого класса по его интерфейсу. В приватных же методах как правило содержатся уже конкретные реализации, а обращаемся мы к ним зачастую лишь в случае, если хотим сделать изменения. И в таком случае структура любого класса всегда останется консистентной.


    3. Константы


    Правило

    Неизменяемые поля в (Companion) Object и compile-time константы именуются в стиле SCREAMING_SNAKE_CASE


    Вопрос об именовании констант влечет за собой еще одну важную проблему: а что сейчас является константой в Kotlin? Язык предоставляет ключевое слово для обозначения compile-time констант "const", но compile-time константы могут быть только примитивного типа и String-и. В качестве констант хотелось бы использовать и другие неизменяемые переменные, расположенные в блоке Object — для таких случаев мы расширили понимание констант при именовании, включив в их число и такие неизменяемые переменные. Интересно, что в этом наше мнение совпало с позицией Google-а.


    4. Имена пакетов


    Правило

    Пакеты именуются в стиле lower_snake_case


    В процессе работы у нас часто возникала проблема с именованием пакетов: разные фичи могут быть настолько не очевидными, что при создании для них нового пакета нужно использовать два или более слова, чтобы точно отразить смысл. В правилах именования пакетов для Java от Oracle именовать пакеты предлагается слитно без разделителей, что в наших проектах могло привести к появлению пакетов со сложно читаемыми названиями. Поэтому мы добавили возможность разделять пакеты символом underscore. Стоит подчеркнуть, что использование такого стиля должно быть скорее вынужденным исключением, чем правилом, и стоит этого максимально избегать.


    5. Аннотации


    Другой пункт, по которому наше видение отличается от распространенного, оказалось использование одиночных аннотаций: в некоторых рекомендациях разрешается записывать несколько аннотаций без параметров в одну строку или даже оставлять одну аннотацию без параметров на одной строке с описываемым методом/полем. Такой подход, на наш взгляд, не несет особой пользы (разве что экономия места), а код из-за таких исключений становится менее консистентным, поэтому мы решили оставить единое правило для всех аннотаций: указывать их всегда с новой строки.


    6. Функции как выражения


    Также мы ограничили возможности по использованию function expression – описывать функцию как выражение разрешено только в случае, если она помещается в одну строку (для этого, кстати, мы увеличили максимальную длину строки до 120 символов). Если выражение не помещается в одну строку, вероятно, что при дальнейших изменениях может возникнуть потребность перевести эту функцию в обычный вариант написания, да и читаться такое выражение будет ничуть не проще.


    Что имеем


    В конце хотелось бы обозначить почему даже не смотря на то, что Google выпустил свой style guide мы все же публикуем свое представление. Как уже было сказано выше набор соглашений от разработчика самого языка – JetBrains из-за своей лаконичности едва ли покрывает все нужды команды, очень надеюсь, что команда разработки Kotlin-а не останется в стороне и будет в дальнейшем развивать этот список. При более внимательном просмотре замечаешь, что большинство правил Google были скопированы или переформулированы с их style guide по Java, мы же старались учитывать опыт и других смежных языков программирования, но в большей степени отталкивались от применения разных подходов к стилям, и как раз такой подход позволил нам раскрыть некоторые пункты (структуру класса, описание, вызов функций и др. правила).


    Что дальше


    Даже после принятия единого стандарта написания кода могут возникать проблемы: кто-то может забыть правила, или что еще хуже – начать саботировать их. Хорошо, если этот код будет забракован на этапе code review, но проверяющий может не заметить этих ошибок или даже участвовать в подрывной деятельности. Несмотря на молодость Kotlin, для него уже существуют инструменты по статическому анализу кода, для которых имеется возможность описать все правила и отслеживать любое нарушение автоматически, что уже на подходе в нашем бэклоге. Сервис поможет разработчикам быть более дисциплинированными и освободит Reviewer’а от необходимости проверять соответствие кода принятому стандарту, и тогда эти инструменты станут помощниками в поддержании чистоты на проекте.


    Ссылки:


    1. Redmadrobot Kotlin Style Guide
    2. Coding Conventions c официального сайта Kotlin-a
    3. Style Guide в репозитории Дмитрия Жемерова
    4. Style Guide для Kotlin-а от Google
    Redmadrobot 104,21
    №1 в разработке мобильных решений для бизнеса
    Поделиться публикацией
    Комментарии 18
    • +8
      Для полей View из Kotlin Android Extension используется стиль именования lower_snake_case

      Печаль.

      • 0
        Это было тяжелое решение — на самом деле тут trade off между camelCase в xml и snake_case в котлин файлах. Мы выбрали второй вариант, хотя тут команда разбилась на два лагеря.

        На самом деле — это вкусовщина.
        • 0
          Кстати можете форкнуть наш кодстайл на гитхабе и переписать часть под свою команду и привычки ;)

          Если вспомните моменты, которые мы упустили то кидайте пулреквесты / issue
          • 0

            Ну и стаилгайды в общем вкусовщина. Имхо, camelCase в xml меньшее зло. И так пишут те же гугловцы. Например, Reto Mayer в одном видео так писал.

            • 0
              На счет описания ID у вас есть хорошие практики? При Java было удобно именовать ID по описанному в интернете принципу What-Where-Description (textview_authorization_password_hint). В Kotlin Android Extension такое именование выходит чересчур длинное, а дальше кто на что горазд. Как пример: tvPasswordHint, passwordHintTextView.
              • +1
                Мы используем следующее правило: Where-What-Description (fragment_authorization_text_view_password_hint), так оказывается удобнее, т.к. IDE при использовании Kotlin Android Extensions может предлагать все View проекты, которые как-то идентифицированы, а когда мы работаем в контексте AuthorizationFragment и хотим использовать какие-л. View мы заранее знаем с чего начать их писать что бы IDE предлагала более актуальный автокомплит
            • 0
              Не согласен с Вами, учитываю, что вы не объявляете нигде переменную вьюхи в коде, другой стиль именования делает её визуально отличной от остального кода, проще разобраться, сразу понятно что это вьюха и т.п
              • 0

                Как написал ниже yanex можно включить подсветку для них. А использовать в коде имена не по конвенции не очень хорошо.

                • 0
                  Не знал, что можно настроить подсветку именно для Kotlin Android Extensions, спасибо!) К сожалению, комментария yanex еще не было, когда я писал свой (мой проходил модерацию)
              • +1

                Если более точно – названия синтетических свойств совпадают с идентификаторами View (для @+id/text_view будет text_view). Это консистентно с R.id.<id>, и дополнительный манглинг имён редко повышает читаемость кода.
                Кстати, в Preferences → Color Scheme → Kotlin можно выбрать собственную подсветку для свойств Android Extensions.

                • +2
                  вот кстати поэтому я именую id вьюх в lowCamelCase стиле, в IDE и так видно, что это вьюха среди других переменных. Именование с подчеркиванием напоминает устаревшие префиксы m и s, которые стали ненужны с появлением умных IDE с подсветкой.
                • 0
                  А какая логика работы для вас была бы более приемлемой? Манглинг имён?
                  • +1

                    Я считаю абсолютно правильным то как сейчас сделано. То, что имена в файлах такие же как в xml.
                    И попробовав, считаю, что camelCase в лейаутах гораздо удобнее.

                • 0
                  Так как мы рассматриваем класс как некоторый интерфейс, в первую очередь интересно узнать, что он делает — благодаря тому, что все приватные методы располагаются в самом конце, можно быстро понять ответственность этого класса по его интерфейсу.

                  Гораздо проще оценить интерфейс класса при помощи вкладки Structure (Cmd+7) и тогда не придется отрывать приватные методы от места их использования
                  • 0
                    Тут дело не только в интерфейсе, но и в структуре класса.

                    Мы рассматриваи следующие альтернативы:

                    1. порядок по модификаторам
                    2. порядок по регионам
                    3. порядок не регламентируется

                    Вариант порядок по регионам слабо коррелирует с Single Responsibility из солид
                    Вариант порядок не регламентируется мы пробовали, но это было неудобно, особенно при переопределении методов ЖЦ Активити код был довольно разнородный и непредсказуемый

                    В итоге остановились на варианте с порядоком по модификаторам
                    • 0

                      Если надо оценить структуру типа в среде разработки, то да, проще.


                      Если смотреть на diff в пул-реквесте вне среды разработки в каком-нибудь веб интерфейсе, когда видны только изменённые строки и не очевидно, что вокруг, и когда идентификаторы, используемые в этих строках, определены где-то в другом месте, то хороший (или для начала хотя бы единообразный) кодстайл очень помогает.

                    • +1
                      Можете рассказать подробнее какого рода проекты были переписаны?
                      • 0

                        Интересно по поводу


                        Избегать использования Destrucion Declaration в лямбда-выражениях.

                        Были какие-то проблемы?

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое