Принципы развития Kotlin
Принципы прагматичного развития
Kotlin создан как прагматичный инструмент для программистов. Прагматичный подход к развитию языка отражают следующие принципы:
Поддерживать современность языка с течением времени.
Поддерживать постоянную обратную связь с пользователями.
Сделать переход на новые версии простым и удобным для пользователей.
Поскольку эти принципы важны для понимания развития Kotlin, рассмотрим их подробнее.
Поддержка современности языка. Мы понимаем, что со временем в системах накапливается устаревший код. То, что когда-то было передовой технологией, сегодня может безнадежно устареть. Чтобы язык оставался актуальным и соответствовал потребностям пользователей и их ожиданиям, он должен развиваться. Это подразумевает не только добавление новых возможностей, но и постепенный отказ от тех, которые больше не рекомендуются для промышленной разработки и устарели.
Удобный переход на новые версии. Несовместимые изменения, например удаление элементов языка, могут привести к болезненному переходу с одной версии на другую, если вносить их без должной осторожности. Мы всегда будем заранее объявлять о таких изменениях, помечать элементы как устаревшие и предоставлять инструменты для автоматической миграции до внесения изменений. К моменту изменения языка мы хотим, чтобы большая часть кода уже была обновлена, а переход на новую версию не вызывал проблем.
Обратная связь. Циклы устаревания требуют значительных усилий, поэтому мы хотим свести к минимуму количество несовместимых изменений в будущем. Помимо того, что мы полагаемся на собственное здравое суждение, мы считаем, что лучший способ проверить дизайн — опробовать его на практике. Прежде чем высекать что-либо в камне, мы хотим испытать это в реальных условиях. Поэтому мы используем любую возможность, чтобы сделать ранние версии наших решений доступными в промышленных версиях языка, но в одном из нестабильных статусов: Experimental, Alpha или Beta. Такие возможности нестабильны: их могут изменить в любой момент, а пользователи, решившие ими воспользоваться, явно подтверждают готовность иметь дело с возможными проблемами при переходе на будущие версии. Эти пользователи предоставляют бесценную обратную связь, которую мы используем для дальнейшей работы над дизайном и его совершенствования.
Несовместимые изменения
Если при переходе с одной версии на другую какой-либо код, который раньше работал, перестает работать, это несовместимое изменение языка (иногда его называют «ломающим изменением»). В некоторых случаях можно спорить о том, что именно означает «перестает работать», но к таким изменениям определенно относятся следующие:
Код, который успешно компилировался и выполнялся, теперь отклоняется с ошибкой (во время компиляции или компоновки). Сюда относятся удаление языковых конструкций и введение новых ограничений.
Код, который выполнялся нормально, теперь выбрасывает исключение.
К менее очевидным случаям, относящимся к «серой зоне», относятся обработка граничных случаев по-другому, выбрасывание исключения другого типа, изменение поведения, наблюдаемого только через рефлексию, изменение недокументированного или неопределенного поведения, переименование бинарных артефактов и прочее. Иногда такие изменения крайне важны и значительно влияют на процесс перехода; иногда они несущественны.
Вот несколько примеров того, что определенно не является несовместимым изменением:
Добавление новых предупреждений.
Добавление новых языковых конструкций или ослабление ограничений для существующих.
Изменение приватных и внутренних API, а также других деталей реализации.
Принципы поддержки современности языка и удобного перехода на новые версии подразумевают, что несовместимые изменения иногда необходимы, но вводить их следует осторожно. Наша цель — заранее информировать пользователей о предстоящих изменениях, чтобы они могли без проблем перенести свой код.
В идеале о каждом несовместимом изменении следует сообщать с помощью предупреждения во время компиляции, которое отображается в проблемном коде (обычно его называют предупреждением об устаревании), и предоставлять средства для автоматической миграции. Таким образом, идеальный процесс перехода выглядит следующим образом:
-
Переход на версию A (в которой объявляется об изменении)
Получение предупреждений о предстоящем изменении
Миграция кода с помощью инструментов
-
Переход на версию B (в которой вносится изменение)
Отсутствие каких-либо проблем
На практике некоторые изменения невозможно точно обнаружить во время компиляции, поэтому о них нельзя сообщить с помощью предупреждений. Однако пользователи как минимум будут уведомлены в примечаниях к выпуску версии A о том, что изменение появится в версии B.
Исправление ошибок компилятора
Компиляторы — сложное программное обеспечение, и, несмотря на все усилия разработчиков, в них бывают ошибки. Ошибки, из-за которых сам компилятор аварийно завершает работу, сообщает ложные ошибки или генерирует заведомо неработающий код, хоть и досадны и часто вызывают смущение, исправить легко, поскольку исправления не являются несовместимыми изменениями. Другие ошибки могут приводить к тому, что компилятор генерирует неправильный, но работающий код: например, не обнаруживает некоторые ошибки в исходном коде или просто генерирует неверные инструкции. Исправления таких ошибок технически являются несовместимыми изменениями (некоторый код раньше успешно компилировался, а теперь компилироваться не будет), но мы склонны исправлять их как можно скорее, чтобы предотвратить распространение плохих шаблонов кода в коде пользователей. По нашему мнению, это соответствует принципу удобного перехода на новые версии, поскольку вероятность столкнуться с проблемой у пользователей становится ниже. Разумеется, это относится только к ошибкам, обнаруженным вскоре после их появления в выпущенной версии.
Принятие решений
JetBrains, первоначальный создатель Kotlin, развивает язык при поддержке сообщества и в сотрудничестве с Kotlin Foundation.
За всеми изменениями языка программирования Kotlin следит ведущий дизайнер языка (в настоящее время — Михайл Зареченский). Ведущий дизайнер принимает окончательное решение по всем вопросам, связанным с развитием языка. Кроме того, несовместимые изменения в полностью стабильных компонентах должны быть одобрены комитетом по языку, созданным при Kotlin Foundation (в настоящее время в его состав входят Джеффри ван Гог, Вернер Дитль и Михайл Зареченский).
Комитет по языку принимает окончательные решения о том, какие несовместимые изменения будут внесены и какие конкретные меры следует принять, чтобы переход пользователей проходил как можно проще. При этом он руководствуется рекомендациями комитета по языку.
Выпуск языковых возможностей
Как описано в разделе процесс выпуска Kotlin, языковые возможности появляются в выпусках языка (2. x. 0) или в следующих за ними выпусках инструментов (2. x. 20).
Мы стараемся обеспечивать совместимость выпусков языка и инструментов, поэтому изменения компилятора в основном представляют собой оптимизации, а также добавление или удаление предупреждений. Нестабильные возможности могут добавляться, удаляться или изменяться в любое время.
В выпусках языка часто появляются новые возможности, нестабильные возможности получают стабильный статус, а ранее объявленные устаревшими возможности могут быть удалены или изменены.
Сборки EAP
Перед выпуском стабильных версий языка и инструментов мы публикуем несколько предварительных сборок, называемых EAP (от «Early Access Preview» — предварительная версия для раннего доступа). Это позволяет нам быстрее дорабатывать решения и собирать отзывы сообщества. EAP-сборки выпусков языка обычно создают бинарные файлы, которые позднее стабильный компилятор отклонит, чтобы возможные ошибки в бинарном формате не сохранялись дольше периода предварительного тестирования. На финальные релиз-кандидаты, такие как RC2 или RC3, это ограничение обычно не распространяется. Дополнительную информацию см. в разделе Участие в программе раннего доступа к Kotlin.
Нестабильные возможности
Согласно описанному выше принципу обратной связи, мы открыто дорабатываем наши решения и выпускаем версии языка, в которых некоторые возможности имеют один из нестабильных статусов и могут измениться. Такие возможности могут быть добавлены, изменены или удалены в любой момент без предупреждения. Мы делаем все возможное, чтобы пользователи не могли случайно воспользоваться нестабильными возможностями. Обычно для этого требуется явно выразить согласие на их использование в коде или конфигурации проекта.
Возможность языка Kotlin может иметь один из следующих статусов:
Исследование и проектирование. Мы рассматриваем возможность добавления в язык новой функции. Это подразумевает обсуждение того, как она будет взаимодействовать с уже существующими возможностями, сбор сценариев использования и оценку ее потенциального влияния. Нам нужны отзывы пользователей о проблемах, которые решит эта возможность, и о сценариях использования, для которых она предназначена. По возможности мы также стараемся оценить, насколько часто встречаются эти сценарии и проблемы. Обычно идеи документируются в задачах YouTrack, где продолжается обсуждение.
Обсуждение KEEP. Мы достаточно уверены, что эту возможность следует добавить в язык. Мы стремимся изложить мотивацию, сценарии использования, дизайн и другие важные детали в документе под названием Kotlin Evolution and Enhancement Process (KEEP). Мы ожидаем, что отзывы пользователей будут посвящены обсуждению всей информации, представленной в KEEP.
Предварительная версия. Прототип возможности готов, и его можно включить с помощью специального параметра компилятора. Мы хотим узнать о вашем опыте использования этой возможности: насколько легко она интегрируется в вашу кодовую базу, как взаимодействует с существующим кодом, а также о проблемах и предложениях, связанных с поддержкой в IDE. Дизайн возможности может значительно измениться или она может быть полностью отозвана на основании отзывов. Для возможности, находящейся в предварительной версии, установлен уровень стабильности Experimental или Beta. Подробнее см. уровни стабильности.
Стабильная. Эта возможность языка стала полноправной частью языка Kotlin. Мы гарантируем ее обратную совместимость и предоставление поддержки в инструментах.
Отозвана. Мы отозвали предложение и не будем реализовывать эту возможность в языке Kotlin. Мы можем отозвать возможность, находящуюся в предварительной версии, если она не подходит для Kotlin.
Полный список предложений по развитию языка Kotlin и их статусов.
Статус различных компонентов
Подробнее о стабильности различных компонентов Kotlin, таких как компиляторы Kotlin/JVM, JS и Native, а также различные библиотеки.
Библиотеки
Язык не может существовать без своей экосистемы, поэтому мы уделяем особое внимание тому, чтобы обеспечить плавное развитие библиотек.
В идеале новую версию библиотеки можно использовать как «прямую замену» старой версии. Это означает, что обновление бинарной зависимости не должно ничего нарушать, даже если приложение не перекомпилируется (это возможно при динамической компоновке).
С одной стороны, для этого компилятор должен обеспечивать определенные гарантии стабильности бинарного интерфейса приложения (ABI) в условиях раздельной компиляции. Поэтому каждое изменение языка оценивается с точки зрения бинарной совместимости.
С другой стороны, многое зависит от того, насколько осторожно авторы библиотек подходят к выбору безопасных изменений. Поэтому авторам библиотек крайне важно понимать, как изменения исходного кода влияют на совместимость, и следовать рекомендациям, позволяющим сохранять стабильность API и ABI библиотек. При рассмотрении изменений языка с точки зрения развития библиотек мы исходим из следующих предположений:
В коде библиотек всегда следует явно указывать типы возвращаемых значений публичных и защищенных функций и свойств, не полагаясь на вывод типов для публичного API. Незначительные изменения в выводе типов могут непреднамеренно изменить типы возвращаемых значений и привести к проблемам с бинарной совместимостью.
Перегруженные функции и свойства одной библиотеки должны выполнять по существу одно и то же. Изменения в выводе типов могут привести к тому, что в местах вызова станут известны более точные статические типы, и это повлияет на выбор перегрузки.
Авторы библиотек могут использовать аннотации @Deprecated и @RequiresOptIn, чтобы контролировать развитие публичного API. Обратите внимание: @Deprecated(level=HIDDEN) можно использовать для сохранения бинарной совместимости даже для объявлений, удаленных из API.
Кроме того, по соглашению пакеты с именем "internal" не считаются частью публичного API. Весь API, размещенный в пакетах с именем "experimental", считается нестабильным и может измениться в любой момент.
Мы развиваем стандартную библиотеку Kotlin (kotlin-stdlib) для стабильных платформ согласно изложенным выше принципам. Изменения контрактов ее API проходят те же процедуры, что и изменения самого языка.
Параметры компилятора
Параметры командной строки, принимаемые компилятором, также относятся к публичному API, поэтому к ним применяются те же соображения. Поддерживаемые параметры (не имеющие префикса "-X" или "-XX") можно добавлять только в выпусках языка; перед удалением их следует должным образом объявить устаревшими. Параметры "-X" и "-XX" являются экспериментальными и могут добавляться и удаляться в любое время.
Инструменты совместимости
По мере удаления устаревших возможностей и исправления ошибок меняется исходный язык, и старый код, который не был должным образом перенесен, может перестать компилироваться. Обычный цикл устаревания предоставляет достаточно времени для перехода, а даже после его завершения и включения изменения в стабильную версию остается возможность скомпилировать неперенесенный код.
Параметры совместимости
Мы предоставляем параметры совместимости, позволяющие новой версии Kotlin имитировать поведение старой:
-language-version X.Y— режим совместимости с версией языка Kotlin X.Y. Компилятор сообщает об ошибках, если в коде используются возможности языка, появившиеся в более поздних версиях.-api-version X.Y— режим совместимости с версией API Kotlin X.Y. Компилятор игнорирует объявления, использующие API стандартной библиотеки Kotlin, появившиеся в более поздних версиях, включая API, на которые ссылается сгенерированный компилятором код.
Чтобы дать вам больше времени на переход, в JVM мы поддерживаем как минимум три предыдущие версии языка и API в дополнение к последней стабильной версии. Это позволяет авторам библиотек использовать более новые версии компилятора, сохраняя совместимость с пользователями старых версий компилятора. На других платформах также можно настроить более старые версии языка и API, но, в отличие от JVM, пользователям все равно необходимо использовать последнюю версию компилятора.
В большинстве проектов следует задавать одинаковую версию для обоих параметров. Более низкая версия API нужна главным образом в тех случаях, когда требуется сохранить совместимость со старой версией стандартной библиотеки Kotlin.
В активно поддерживаемых кодовых базах может быть полезно как можно раньше получать исправления ошибок, не дожидаясь завершения полного цикла устаревания. Такие проекты могут включить параметр -progressive, чтобы применять эти изменения в выпусках инструментов до того, как они станут настройкой по умолчанию.
Эти параметры можно настроить в командной строке или с помощью систем сборки Gradle или Maven.
Развитие бинарного формата
В отличие от исходного кода, который в худшем случае можно исправить вручную, бинарные файлы гораздо сложнее перенести, поэтому для них крайне важна обратная совместимость. Несовместимые изменения бинарных файлов могут сделать переход очень неудобным, поэтому их следует вводить еще осторожнее, чем изменения синтаксиса исходного языка.
Для полностью стабильных версий компилятора по умолчанию действует следующий протокол бинарной совместимости:
Все бинарные файлы обратно совместимы: более новый компилятор может читать более старые бинарные файлы (например, версия 1.3 понимает версии с 1.0 по 1.2).
Старые компиляторы отклоняют бинарные файлы, использующие новые возможности (например, компилятор версии 1.0 отклоняет бинарные файлы, использующие корутины).
Желательно (хотя мы не можем этого гарантировать), чтобы бинарный формат был в основном прямой совместим с ближайшим следующим выпуском языка, но не с более поздними выпусками (если новые возможности не используются; например, версия 1.9 понимает большинство бинарных файлов версии 2.0, но не версии 2.1).
Этот протокол обеспечивает удобный переход: ни один проект не будет вынужден откладывать обновление зависимостей, даже если в нем используется несколько устаревшая версия компилятора.
Обратите внимание, что не все целевые платформы достигли такого уровня стабильности, но Kotlin/JVM его достиг.
Бинарные файлы Kotlin klib
В Kotlin 1.9.20 бинарные файлы Kotlin klib достигли уровня Stable. Однако необходимо учитывать некоторые особенности совместимости:
Начиная с Kotlin 1.9.20 бинарные файлы klib обратно совместимы. Например, компилятор версии 2.0.x может читать бинарные файлы, созданные компилятором версии 1.9.2x.
Прямая совместимость не гарантируется. Например, нет гарантии, что компилятор версии 2.0.x сможет читать бинарные файлы, созданные компилятором версии 2.1.x.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/kotlin-evolution-principles.html