Kotlin в продакшене, что мы получили, и что мы потеряли?

https://distillery.com/blog/using-kotlin-production-wins-losses-lessons-learned/
  • Перевод
KotlinС того времени, как Google сделал Kotlin новой любимой женой уже прошло достаточно времени. И сразу же после этого объявления наша команда начала новый проект полностью на Котлине. Проясню: не тестовый или просто внутренний проект, а новый модуль для живого приложения с 600+ тысячами активных пользователей в месяц. Какой опыт мы из этого извлекли? Что мы выиграли и что потеряли?

Для начала, я не буду повторять обещания JetBrains на счет времени сборки, полноценного взаимодействия с Джавой и производительности. Это правда, что Котлин код практически так же быстр, и что оверхед не превышает размера типичной PNG картинки из ресурсов. Но зачем нам вообще нужен Котлин, и в чем все-таки разница?

Фан-фактор


Очевидно, Котлин надо сначала выучить. Даже несмотря на то, что кривая обучения не выглядит как гора К2, плоской она тоже не является: один из коллег заметил, что на выполнение задач на Котлине у него уходило примерно в два раза больше времени, тем не менее, никто ни разу не пожаловался. Наоборот, все были только рады возможности применить новую крутую игрушку со всеми ее свистелками в продакшене. Более того, для привлечения лучших кандидатов мы стали не сговариваясь упоминать в ходе интервью, что пишем на Котлине. Так что чистая победа в этой категории +100 очков Котлину.

Скорость


Работа с новым языком это не только изучение его синтаксиса, это — совокупность написания, чтения, рефакторинга, тестирования и т.д. Остановимся на этом немного детальнее.

Скорость написания


В среднем на то, чтобы почувствовать себя более-менее комфортно с Котлином ушло около двух недель. И еще примерно две недели понадобилось, чтобы начать писать на нем быстрее чем на Джаве. Тем, у кого был опыт Scala и Groovy было проще, так как авторы Котлина во многом вдохновлялись ими. Поддержка со стороны IDE все еще не идеальна: много удобных и привычных по Джаве рефакторингов и автоподстановок просто не работают сейчас. Но даже несмотря на эту проблему, в итоге мы все равно немного в плюсе по скорости. Слабая победа Котлина в этой категории, впрочем мы ждем большего от финальной версии Android Studio 3.0 (на момент написания статьи мы все еще пользовались бетой).

Скорость чтения


Хоть код на Котлине и получается ощутимо короче, разбирать пулл-реквесты на ГитХабе приходится дольше. Почему так? Во-первых, ни Google ни JetBrains не подготовили полноценный стайл-гайд, так что члены команды стали проводить больше времени за дискуссиями на тему «красоты». Даже официальные примеры кода не во всем однородны. Во-вторых, некоторые новые (для Джавы) конструкции могут быть использованы во вред, в то время как на Джаве так написать попросту не получится. Итого: небольшой но все же проигрыш на этом направлении, хотя, опять же, мы ожидаем улучшений в ближайшем будущем, потому что по поводу 99% спорных кейсов уже пришли к согласию.

Инструменты и библиотеки


Страшная правда в том, что Checkstyle и Lint пока еще абсолютно бесполезны для Котлина, а мы их используем и активно, но с библиотеками проблем почти нет, тот же Dagger работает отлично. А еще мы получили Kotlin-X и собираемся втянуть в проект Anko. Тут опять проигрыш Котлина, и хоть Anko с Kotlin-X могут подсластить пилюлю, мы особо не ожидаем драматических улучшений в работе стайлчекеров в ближайшее время.

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


А вот тут мы неожиданно получили преимущество. С Котлином не поставляется никаких новых инструментов или библиотек для тестирования, но новые языковые фичи, особенно type inference, extensions, и reified-типы существенно сократили количество бойлерплейта в тестовых классах. Чистая победа тут, даже несмотря на то, что IDE иногда пытается запустить Espresso как будто это чистые JVM юнит-тесты и, очевидно, не справляется с задачей.

Саморазвитие


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

П.С. Изначально статья была написана на английском для корпоративного блога, но я решил, что она должна существовать и на русском языке, так что «совпадение» имен авторов не случайно.
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 64
  • –8
    Лучше писать Java, а не Джава
    • +6
      А еще Котлин не склонять. Такие вещи пишут обычно в личку :) К тому же, ничего страшного в таком варианте нет, это ведь не Активность вместо Activity.
    • –8
      Почему не Ява или Джява?
      • +15
        Потому же, почему jazz это джаз, а не язь :)
        • +2
          А остров Ява на самом деле Джава?
          • –1
            В английском вроде как говорят джас, но ява. Разве нет?
            • 0
              Нет. В Google Translate есть кнопка для озвучивания текста, можете убедиться.
              • –1
                Я бы не доверял сильно гугл-транслейту, сегодня спрошу у носителей :)
                • 0
                  Известные мне носители обычно довольно четко артикулируют З.
              • 0
                Если очень примерно, то они говорят «джез» и «джава».
                • –1
                  J всегда читается как дж.
              • –4
                Наш лектор в универе очень доходчиво объяснял, почему «джава»:
                — Если вы говорите «ява», то скажите мне, как произносится слово «job»?
                • +5
                  В русском языке часть слов заимствована из одних языков, часть из других.
                  Поэтому английские Джерри, джаз и прочие, уживаются в нашем языке с Юпитер, Юнкерс, Ява из немецкого скандинавских и других языков.
                  Вообще, откуда у наших программистов манера произносить слово java именно так, я не знаю. Немцы и норвежцы говорят «ява», потому что это обычное для них произношение. Испанцы говорят «хава» по той же причине. Ну и довольно странно выглядит, когда это слово именно пишут русскими буквами, потому что произношение — штука тонкая, не у всех оно идеально, а вот раскладку переключить в состоянии каждый.
                  • –1
                    В Корее инженеры говорят на английском, чтобы не засорять свой язык заимствованиями.
                    • 0
                      Ну и довольно странно выглядит, когда это слово именно пишут русскими буквами, потому что произношение — штука тонкая, не у всех оно идеально, а вот раскладку переключить в состоянии каждый.

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

                • –1
                  Благо, что не «Ява». Остальное уже читается более-менее адекватно :)
                • 0
                  Щас бы орфографию обсуждать в 2к17)
                  по сабжу

                  а еще какие нибудь метрики замеряли, хотя бы на глаз? например, правка багов, их поиск и т.д.
                  • 0
                    Замеряли число регрессий и просто новых багов, тут новый модуль вышел наджежнее всех прошлых, есть предположение, что в основном из-за null-safety. Время прогонки тестов и время сборки тоже замеряются на сервере, но там результат простой — Котлин во всей цепочке сборки несущественно мал. Размер приложения от релиза к релизу тоже мониторится, и тут опять, подрезка картинок и чистках старых зависимостей внесли больший вклад. Ну а время на дискуссии замерялось, очевидно, на глаз, но явно подросло вначале и скоратилось до нормы сейчас.
                  • 0
                    Во-вторых, некоторые новые (для Джавы) конструкции могут быть использованы во вред, в то время как на Джаве так написать попросту не получится

                    Можете написать поподробнее?
                    • 0
                      В качестве примера у нас был кусок кода, где товарищ решил использовать фунциональные конструкции как замену if/else просто потому, что они теперь есть, и он был уверен, что для Котлина не рекомендуется пользоваться «устаревшими» способами управления потоком исполнения. Ну и новый мощный аналог switch/case часто эксплуатировали. У JetBrains в документации четвертый пример не очень красивый, он явно сделан для демонстрации того, что может синтаксис, но кто-то принял это зай гайд и получились примерно такие контрукции.
                      when (x) {
                          -1 -> ...//some fallback here
                          validate(x) -> print("ok")
                          anotherOptionToValidate(x) -> ...//some action again
                          else -> processSomeError(x)
                      }
                      

                      такой вариант читаемости уже не добавляет, и нужно держать в уме, что код выполняется не только по правую сторону от стрелок, но и по левую, когда функции validate и anotherOptionToValidate содержат сайд эффекты получается просто ужасно.
                      • +1

                        А мне нравится.
                        Проблема сайд эффектов при валидации, кмк, вообще из другой оперы, замеченных за написанием такого кода надо пьезоэлементом из зажигалки щёлкать.

                        • 0

                          Это не функциональный стиль программирования, это стремление создать запутанную конструкцию там, где она не нужна) Вопрос в понимании что такое ФП, а не в языке)

                          • 0
                            Именно! Но приходится в итоге рассказыать отдельно, что во-первых «старый» понятный стиль, не отменяли, а во-вторых в ФП вообще-то не должно быть сайд-эффектов вообще.
                          • +1
                            когда функции validate и anotherOptionToValidate содержат сайд эффекты

                            … то ваши проблемы совсем не в котлине

                            • +1
                              Ну что тут скажешь? Да, именно так. Просто новый новый язык эти проблемы вскрыл.
                              • 0

                                Что говорит о том что конструкция хороша, а не плоха, разве не так?

                                • 0
                                  Она не хороша и не плоха, она другая, более гибкая, позволяет сделать больше, но и позволяет сильнее ошибиться тоже. С ней больше не возможна простая строгая проверка на полноту как в случае с switch/case + enum, и кому-то это важнее. Остальное вопрос вкуса, на мой вкус — конструкция удобная.
                                  • 0
                                    С ней больше не возможна простая строгая проверка на полноту как в случае с switch/case + enum, и кому-то это важнее.

                                    А вот тут вы не правы. Есть два подвида when — с аргументом, вроде switch, и без аргумента, аналог цепочки if-ов. Когда тип аргумента when перечисление или запечатанный (sealed) класс такая проверка как раз возможна.

                                    • 0
                                      When-оператор в общем виде он не предполагает строгой проверки на этапе сборки, даже в случае с перечислениями. Например такой вариант абсолютно корректен синтаксически
                                              var someEnumValue = SomeEnum.VALUE;
                                              when (someEnumValue) {
                                                  in SomeEnum.values() -> doAction()
                                                  someFunctionThatReturnsEnum() -> doAnotherAction()
                                              }
                                      

                                      Более того, он останется корректным, даже если убрать первое условие и оставить только вызов функции someFunctionThatReturnsEnum. Справедливости ради стоит отметить, что Котлин в таких случаях все-таки вежливо попросит сделать явное перечисление, но если мы не сделаем этого, или перечислим не все варианты, код останется корректным. Полнота проверяется компилятором, если when используется как выражение, а не как оператор, но тогда в нашем примере мы получим ложную ошибку и просьбу добавить else ветку, потому что компилятору «не известно» что SomeEnum.values() уже является полным вариантом.
                                      Собственно, исходный посыл и был в том, что так писать не надо :)
                        • –3
                          Все равно c# лучший )
                          • +4
                            МС в последние годы делает очень крутую работу по развтию шарпа, так что тут не буду спорить, но под JVM его нет :)
                            • 0

                              Ага, лучший. Вы посмотрите как на нем pattern matching запилили через switch, break инструкции в pattern matching, где это видано? А inline функции, с параметрами которые можно менять когда и как угодно… Я интересовался у одного из лидов roslyn, насчет, когда же они наконец запилят tail recursion в шарпы, его отвел был — скорее всего никогда из за параллельной поддержки VB.


                              В Котлине по умолчанию все immutable, есть tail recursion оптимизация и pattern matching красиво выполнен, так что не сравнивайте бараньи тестикулы с северным сиянием.

                            • +1
                              Наоборот, все были только рады возможности применить новую крутую игрушку со всеми ее свистелками в продакшене.


                              так себе причина.
                              • +2
                                Командный дух — достаточно серьезная причина, на самом деле.
                                • –2
                                  Как соотносится командный дух с желанием напихать в продакшен побольше новомодных штучек?
                                  • +1
                                    Нет желания напихать побольше, есть желание получить доступ к технологии, которая потенциально станет основой Андроид разработки в самое ближайшее время, как и желание освоить новый язык и сделать код на проекте чище и лаконичнее. Когда менеджмент разрешает программистам выбирать технологии, это всегда положительно сказывается на командном духе, обратное тоже верно, команда, застрявшая на старых технологиях, начинает терять мотивацию.
                                    • –2

                                      Я вас удивлю, но есть команды с обратным эффектом. Вы явно фанат новых технологий, но поверьте, не все люди такие)

                                      • +1
                                        Удивитесь, но фанат я довольно сдержанный. Я думаю, вопрос тут в том, команда ли принимает решение, или отдельный человек. Когда «новые» технологии насаждает начальник или просто один ретивый участник наперекор остальным, обычно бывает плохо, но я ни разу не работал в команде, которая была не рада получить более удобный инструмент после совместного обсуждения. Когда-то давно мы переезжали с WinForms на WPF для Windows разработки всем отделом, после той истории мне написал сотрудник другого департамента, и пожаловался, что у них одно старье, и что он не знал, что в компании так можно.
                                        • –1
                                          Вы меня немного не поняли. Я только сказал, что существуют люди, кому интереснее поддержка legacy кода. И из таких людей даже команды собираются. То, что вы в таких не работали говорит только о том, что у вас другой взгляд и другие приоритеты.
                                          И ваше утверждение про
                                          команда, застрявшая на старых технологиях, начинает терять мотивацию.

                                          верно не везде и всегда, а только в конкретных случаях для конкретных команд.
                                          • 0
                                            Теперь понял, согласен. Но в такой команде я, действительно, работать бы не захотел.
                              • +2
                                Вот этот вот fun меня смешит, а так, ничего, пойдёт.
                                • 0
                                  А в плане читабельности и простоты рефакторинга, можете ли вы оценить упростил ли что-нибудь Kotlin?
                                  • +1
                                    Рефакторинг, как отмечено выше, стал чуть-чуть хуже, банальное вынесение строки в константу пока еще не работает, к примеру. Но ничего драматичного. А сам код, после того как дебаты улеглись, стал выглядеть куда приятнее. Дата классы — очень хорошо подчистили код, properties (наконец-то!) — тоже, все Util классы стали коллекциями extensions методов и код опять же стал читаться куда проще. Однозначно читаемость сейчас ощутимо выше.
                                    • 0
                                      банальное вынесение строки в константу пока еще не работает, к примеру

                                      Да, пока не все возможности Java реализованы для Kotlin, но в тоже время в Kotlin многие вещи наоборот упрощают различные изменения и рефакторинг. Те же именованные параметры, необязательными типы, destructuring и многое другое.
                                    • +1
                                      Забыл упомянуть, очень радует то, что Андроид Студия при вставке Джава-сниппетов в Котлин-код сама переводит код на Котлин.
                                    • +1

                                      Сам недавно сталкивался с Котлином, и, на мой взгляд, самой главной фичей этого языка можно назвать возможность применять функциональный стиль программирования без приплясыванием с лямбдами или адом анонимных классов. Вообще данный язык бессмысленен если писать "как привык", но когда мозг перестраивается на реактивный и/или компонентный подход в проектировании приложений, то тогда весь "сахар" языка становится необходимостью, без которой читаемость кода будет на нуле.


                                      Ну и не понимаю я людей, которые идут работать "по тому, что тут пишут на новомодном языке"

                                      • +2
                                        Соглашусь с первым утверждением, а со вторым не очень. Для людей, которые пишут проект второй-третий год подряд на Java 7, возможность выйти из зоны комфорта, выучить новый язык, получить доступ к нормальным удобным конструкциям из мира функуционального программирования, это не просто прихоть, это — возможность вложить силы и время в саморазвитие. Не у всех есть возможность регулярно после работы по вечерам осваивать новые технологии. А так уже завтра на рынке труда они будут чуть-чуть более востребованы как специалисты.
                                        • +1

                                          Есть в ваших словах доля истины, но есть и то с чем не согласен.
                                          Для начала, про востребованность. Кто лучше, кандидат, который знает вдоль и поперек UI движок платформы и владеет только одним языком или тот, кто знает пять языков программирования но поверхностно? К сожалению рынок труда у нас построен так, что хотят видеть тех, кого могут проверить, а проверить базовые знания по языкам проще, чем глубокие по архитектуре или фундаментальные по программированию. Да и в 90% случаев не нужны эти глубокие знания. Вот и получается, что второй кандидат востребованнее, вот только технологии создают и развивают люди с глубокими и фундаментальными знаниями.


                                          Писать на Котлине это не "выход из зоны комфорта", это переход на более совершенную модель того же инструмента. Развитие происходит от смены парадигмы мышления и паттернов проектирования. Писать объектный код можно и на C и на многих языках, просто это не так удобно. То же касается и функционального кода. Его никто не мешает писать и на Java 7 и на Java 6. Просто это не так удобно. А говорить "я не могу писать функциональный код, по тому что я пишу на Java" — это уход от ответственности. По тому, что в данной фразе виноват не я, а обстоятельства.

                                          • +1
                                            По традиции соглашусь с первым и поспорю со вторым :)
                                            Рынок труда устроен именно так, к счастью или к сожалению, и умеение держать баланс — это сугубо личное. Мое мнение — пять но плохо, хуже чем один но хорошо, но один досконально, хуже, чем два хорошо. В нашем случае эксперимент показал, что Котлин к продакшену готов, поэтому все новые модули мы теперь пишем на нем, так метаний между языками становится меньше, а знания платформы с любым языком в нашем случае остаются одинаковыми, что Котлин, что Джава живут поверх одной JVM и одного Андроида.

                                            Никто не мешает, но идти против приниципов принятых платформой (язком, фреймворком, операционной системой — не важно) обычно не просто не так удобно, но еще и не так продуктивно, особенно если разработка идет в команде. Поэтому да, каждый отвечает сам за свои действия, но влияние среды все-таки надо учитывать, и менять среду, если есть такая возможность.
                                        • 0
                                          Ну и не понимаю я людей, которые идут работать «по тому, что тут пишут на новомодном языке»

                                          Ну вот представьте — появился новый язык. Человек его изучил. Думаю, желание изучить что-то новое, вопросов ни у кого не вызывает. А какой должен быть следующий шаг? Очевидно, применить новые знания на практике. Что может быть естественней?
                                          • 0

                                            Не соглашусь с тем, что "применить на практике" = "выбрать работу, где его используют". Можно писать библиотеки для open source, можно делать интересные утилиты и выкладывать их в GP. Можно брать на аутсорсинг проекты и там применять. Возможностей много если стоит острое желание попробовать. Я довел два проекта до релиза полностью написав их на Котлине и ни один из них не был привязан к моей постоянной работе. Так что не согласен.

                                            • 0
                                              Так я и не утверждал что «применить на практике» = «выбрать работу, где его используют». Но это один из возможных вариантов. Выше вы перечислили ещё несколько. А уж какой вариант выбрать — каждый решает сам.
                                        • 0
                                          Больше проблем возникает если писать backend код.
                                          Особенно если у вас будет Spring, появляется еще плагин к сборке gradle, нужно добавлять kotlin-spring plugin, без которого все классы нужно обозначать как open, чтобы они не имели final модифактор.
                                          Также столкнулись с большой проблемой использования kotlin не для банальных unit тестов, а в связке с cucumber, из-за сложностей использования reflection — на 100% чистом котлине не взлетело, пришлось делать вставки на джаве.
                                          В целом — сделали ряд микросервисов, в том числе и на akka написаных на котлине без единого джава класса.
                                          Еще вспомнились проблемы с байт кодом и kotlin plugin в Idea, почему в последней версии плагина — байт код несколько изменился, так что порой вовсе не видит каких-то классов при компиляции. Решилось откатом на старую версии идеи и котлин плагина.
                                          • 0
                                            А Сucumber уперся во что-то специфическое, не в закрытость классов для расширения?
                                            Чтобы решить проблему с тем, что Mockito не работает с такими классами, есть обходной пусть: можно создать конфиг файл, который сделает все котлин-классы открытыми для мокито, но только для тестов.
                                          • 0
                                            Котлином не поставляется никаких новых инструментов или библиотек для тестирования

                                            а как же https://github.com/JetBrains/spek? (http://spekframework.org/)

                                            • 0
                                              Согласен, плохо выразился. Имел ввиду, никаких новых инструментов или библиотек для упрощения написания имеющихся unit/unstrumented-unit тестов. Под Спек пришлось бы заново переписать значительно.
                                            • +2

                                              Пару месяцев назад втащил в текущий проект котлин. Напишу здесь несколько своих пространных мыслей.
                                              Проект древний (по меркам Android, первые коммиты сделаны в 2010-ом), поэтому одной из главных целей было добавление хоть какого-то фана. Эту цель я действительно достиг, фана добавилось :) На данный момент примерно 25% кода уже на котлине. Часть — это новый код, писавшийся на протяжении последних месяцев, часть — сконвертированный имевшийся код.
                                              Новые языковые фичи использовать по большей мере приятно. Extension-методы удобны. data-классы, дефолтные методы в интерфейсах, лямбды несколько уменьшили boilerplate. Стандартная библиотека фактически лишила смысла присутствие guava в зависимостях. Но всё-таки null-safety на границах с джавой меня смущает. Нет консистентности, типы со знаком восклицания, по сути, имеют ту же проблему, что и «голая» джава — отсутствие compile-time null-safety. Изначальный подход «все, что приходит от джавы, считаем null-небезопасным, если аннотации не указывают иного» мне как-то больше импонировал. Также некоторые фичи накладывают неожиданные penalties. К примеру, forEach оказался чуть ли не в три раза медленнее, чем «классическое» итерирование. Поэтому надо быть внимательным.
                                              Также пока так и не смог осилить делегаты. Чувствую, что они могут решить достаточно много проблем, к примеру, все эти ужасные колбеки от фрагментов в activity через реализацию интерфейсов. Но все примеры с делегатами, которые я встречал, довольно искусственные и четкого понимания так и не дали. И в целом best practices пока не очень развиты.
                                              Конвертация из джавы есть и это хорошо, но я поймал себя на постоянном исправлении одних и тех же шероховатостей после каждой конвертации. Это же чувство при написании кода на джаве подтолкнуло авторов вообще котлин создавать, так что надеюсь, этот функционал будет улучшаться.
                                              Скорость компиляции значительно просела. Kapt сыроват, часто при переключении между гитовыми ветками приходится делать rebuild. Инкрементальная компиляция сломалась, пока не разбирался, можно ли что-то с этим сделать. И плагин котлина регулярно сыплет краши, что отвлекает, раздражает и вообще не сильно хорошо.
                                              В целом, на острие прогресса быть весело, но немного напряжно.

                                              • 0
                                                Из практических применений делегатов нам больше всего понравился lazy (по правде говоря на него и приходится 99%), но он и правда очень удобен для вьюх, когда надо проинициализировать какой-то объект, зависящий от контекста, и при этом инициализация тривиальна, и захломлять ею onCreate/onCreateView не хочется. Например так
                                                val radioButtons: List<RadioButton> by lazy { some_radio_group.getButtonsList() }
                                                

                                                some_radio_group — это прямая «ссылка» на вьюху через Kotlin-X, но она нам не нужна, а нужно только ее содержимое. Аналогично инициализируем все объекты строк, анимаций и т.п, если они переиспользуются в активити/фрагменте несколько раз.
                                                С проблемой отсутствия консистентности на стыке с нативным кодом очень согласен, досаждает регулярно, и получается не так красиво, как должно быть.
                                                • +1

                                                  Да, спасибо, lazy использую, но это очень частный случай делегатов.
                                                  Ещё вспомнилось из разряда: «Как раньше без этого жили?» Это интерполяция строк и дефолтные параметры методов. Реально нужные вещи.
                                                  А особенно весело стало, когда я начал использовать корутины. Только ради них уже можно вылезти из уютного мирка джавы.

                                                  • +1
                                                    Я еще observable делегат пару раз использовал — автоматом вызывать какой-то код, когда изменилась переменная.

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