Kingfisher Technology — это большая группа команд, разрабатывающих приложения и сервисы на различных языках и платформах. Мы пытаемся дать командам возможность самим выбирать технологии; но мы также понимаем, что принятие стандартных подходов или следование проторенной дороге может облегчить адаптацию и снизить стоимость владения.
Как главный инженер в Kingfisher, я обеспечиваю техническое руководство для поддержки нескольких команд, для серверной разработки я рекомендую нашим командам по умолчанию использовать Kotlin.

Почему Котлин? Начну с того, что расскажу, как мы сюда попали:

Первоначальная пробная версия

Почти 5 лет назад я был техническим руководителем небольшой команды, которая собиралась начать разработку нового микросервиса. Инженеры в этой команде в основном работали над устаревшими системами, а языком, с которым они были лучше всего знакомы, была Java, особенно Java 6, жизнь которой определенно подошла к концу; поэтому, прежде чем начать работу над новой услугой, мы рассмотрели наши возможности.

Нам были известны идеи Дэна МакКинли об инновационных токенах и выборе скучных технологий; так как команда была знакома с инструментами Java и экосистемой Java, это было пространство, которое мы рассмотрели в первую очередь.

Срок службы Java 8 подходил к концу, но Java 11 (следующая версия с долгосрочной поддержкой) еще не была выпущена.

Clojure был языком, которым я интересовался в течение некоторого времени, но это был бы новый язык для компании, и кривая обучения при переходе с объектно-ориентированного языка на LISP выглядела довольно крутой.

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

NodeJS использовался в другом месте в Kingfisher и шире, чем Kotlin на стороне сервера, поэтому мы рассмотрели Javascript; но это означало бы изучение не только нового языка, но и совершенно новой экосистемы/среды выполнения.

Команда считала (из-за ожидаемого сокращения стандартного кода) разумным предположением, что инженеры с таким же опытом будут более продуктивны в Kotlin, чем в Java. время) не будет значительно больше, чем усилия по изучению Java 11.

Исходя из этого, команда решила попробовать Kotlin в сочетании с фреймворком Vert.x; это был новый фреймворк для команды, но, поскольку он поддерживал несколько языков, он обещал нам легкий путь назад к Java 11, если мы обнаружим какие-либо непредвиденные проблемы с Kotlin.

После того, как мы закончили работу над новым сервисом, мы провели небольшой опрос мнения команды инженеров о Kotlin. Новый язык стал явным победителем, а «нет предпочтений» оказался на втором месте — никто не говорил, что теперь предпочитает Java.

Более широкое распространение

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

Основываясь на опыте нашего предыдущего испытания и опыте других команд Kingfisher, занимающихся разработкой на стороне сервера, мы выбрали Kotlin в качестве языка по умолчанию для серверных служб, на этот раз с Spring Boot в качестве фреймворка по умолчанию.

Примечание: это не означало, что все серверные компоненты должныиспользовать Kotlin — инженеры могли свободно использовать другие «распространенные в Kingfisher» языки, такие как Typescript или Javascript. , или Java без дополнительного одобрения, но Kotlin можно было использовать, если не было явной причины предпочесть что-то другое.

Как же сработало это решение?
Со значительно большей командой разработчиков прошло некоторое время, прежде чем все начали писать полностью идиоматический Kotlin, а не «Kotlin со вкусом Java» или «Java с синтаксисом Kotlin», но никто не пытался понять язык.

На сегодняшний день программа еще продолжается (подход душитель фиг может занять много времени на отработку!), но мы уже успешно мигрировали много функционала.

У нас есть небольшое количество серверных компонентов, использующих Typescript/Javascript, и даже несколько, использующих Java, но командам нравится разрабатывать на Kotlin, и подавляющее большинство серверного кода — это Kotlin.

На самом деле в настоящее время программа имеет более 100 тысяч строк серверного Kotlin, работающего в производственной среде (что составляет почти половину всего кода Kotlin в Kingfisher, включая код мобильных приложений).

Какие преимущества мы начинаем замечать?

«Программирование — это искусство рассказывать другому человеку, что он хочет от компьютера». ― Дональд Кнут

У Kotlin есть несколько функций, которые устраняют некоторые «церемонии» вокруг кодирования; уменьшение отвлечения внимания от второстепенного (синтаксис) и упрощение концентрации внимания на главном (проблемная область). Эти функции включают в себя:

  • Нет терминаторов оператора
  • Вывод типа
  • Классы данных

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

  • Именованные параметры
  • Обнуляемые типы
  • Запечатанные классы и исчерпывающий when
  • Встроенные классы

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

Однако не все идеально

Ряд инструментов, которые мы обычно использовали в прошлом, в настоящее время могут добавить больше ценности коду Java, чем коду Kotlin.

Качество кода
Для проверки качества кода мы используем SonarQube. Начиная с версии 9.9 SonarQube имеет около 1200 проверок кода Java и только 385 проверок кода Kotlin. Некоторые из отсутствующих проверок связаны с тем, что потенциальные проблемы устраняются самим языком Kotlin, но в других случаях это связано с (сравнительным) недостатком зрелости механизма анализа.

Качество модульного тестирования
Для обеспечения качества модульного тестирования мы иногда проводим мутационное тестирование с использованием Pitest.
Поскольку это работает с байт-кодом, а не с исходным кодом, можетотчитываться о коде, созданном на Kotlin, но это приводит к большому количеству ложных срабатываний, поэтому автор рекомендует не использовать его для анализа кода Kotlin. Поддержка Kotlin доступна через дополнительный плагин, который является платным.

Мониторинг производительности приложений (APM)
Наш инструмент APM включает в себя машинное обучение для отслеживания изменений в поведении выполнения. Он использует имена методов для понимания отслеживаемых JVM.
Когда компилятор Kotlin использует искажение имен, развертывание приложений может сбить с толку инструмент APM, заставив его думать, что старый поток больше не выполняется, а новые потоки получают много вызовов.
Это может привести к ложным предупреждениям о существенных изменениях в поведении приложения.

Есть вещи, которые мы можем сделать, чтобы позволить инструменту APM понять, что на самом деле происходит, и усилия для этого относительно невелики, но это все же дополнительная работа по сравнению с приложениями Java.

Смешивание Java и Kotlin

Совместимость с Java очень хорошая, но не совсем прозрачная. В частности, мы столкнулись с тремя областями, где взаимодействие с классом Java не так приятно, как взаимодействие с классом Kotlin:

Именованные параметры
Мы не можем называть параметры при вызове метода Java. Создание дополнительных переменных для добавления более описательного имени, конечно, возможно, но это раздувает код и потенциально меняет область действия значения. Альтернативный метод, которому мы научились у Николаса Френкеля, заключается в использовании встроенных классов с сопутствующими объектами.

Параллелизм и асинхронные вызовы
Стандартная идиома Kotlin для этого — сопрограммы, но классы Java, скорее всего, будут использовать Futures, Callables или Runnables; наличие нескольких подходов в одной кодовой базе увеличивает умственные усилия, необходимые для ее понимания.

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

Будущее

Итак, теперь вы знаете, почему «для серверной разработки я по умолчанию рекомендую нашим командам использовать Kotlin», но что дальше?

Остальное в Kingfisher
Мы хотим обсудить, стоит ли нам расширять использование Kotlin. Другие команды Kingfisher уже присматриваются к Kotlin Multiplatform Mobile, о котором мы писали в блоге несколько месяцев назад.

Gradle недавно объявил, что Kotlin является новым языком по умолчанию для сборок Gradle, поэтому для команд, использующих Gradle, вполне может иметь смысл перейти с Groovy.

И Kotlin/JS существует, так должны ли мы все на Kotlin?

Возможно нет.

Как я упоминал ранее, в Kingfisher мы верим в то, что команды могут сами выбирать технологии; команде с опытом работы в экосистеме Javascript, вероятно, будет сложнее внедрить Kotlin, чем команде с опытом работы с Java.

И даже среди команд с опытом работы с Java в настоящее время нет всеобщего желания переключиться. Некоторые из этих команд отмечают (справедливо), что Java добилась большого прогресса с тех пор, как мы впервые рассмотрели альтернативы после Java 6:

  • Проблема null была частично устранена введением Optional в Java 8.
  • Вывод типов появился в Java 10
  • Записи прибыли в Java 16
  • Запечатанные классы появились в Java 17
  • Сопоставление шаблонов для операторов switch скоро должно появиться в Java 21.

Эти команды утверждают, что, поскольку Java теперь включает в себя многие возможности Kotlin, влияние выбора нового языка перевешивает ограниченные преимущества.
Они также выразили обеспокоенность тем, что всем языкам JVM, кроме Java, суждено остаться нишевыми инструментами.

По моему опыту, преимущества Kotlin действительноперевешивают затраты на переход; и хотя это, вероятно, всегда будет нишевым языком по сравнению с Java, это будет большая ниша — в основной презентации на Kotlin Conf 2023 Google подчеркнул, что теперь у них есть ~ 15 миллионов строк серверного Kotlin, что удваивает год в год, и ~ 45% всей разработки Kotlin в Google приходится на серверную часть.

Основная программа замены
Мы хотим исследовать некоторые инструменты, специфичные для Kotlin:

  • Стрелочный анализ может помочь нам выявить определенные ошибки на более ранних этапах цикла разработки (во время компиляции, а не в тестах), даже если мы не хотим применять более функциональный стиль кодирования.
  • ArcMutate может помочь нам в тестировании мутаций.

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

Мы продолжим быть «Kotlin по умолчанию».

Если вы хотите присоединиться к нам в нашем путешествии, пожалуйста, посетите нашу страницу вакансий.

Спасибо за прочтение.