The Kotlin Language Committee has to approve every incompatible change before it lands in a stable version. The committee determines whether the change is appropriate and motivated enough to be introduced and what deprecation procedures have to be carried out to ensure comfortable migration for end users.
These guidelines describe the process the Committee follows and the criteria used for decision making. We realize that these criteria can not be fully formalized and treat them as general guidelines, not a mechanical set of rules.
Incompatible changes are submitted to the Language Committee by the Lead Language Designer. The Committee evaluates the implications of such changes, considering the following:
When in doubt, we inspect large bodies of code available to us and test the implementations in EAP builds to collect information about the expected impact of a change.
A proposal is usually written by a developer responsible for the change or the relevant subsystems. See the Guide to submitting incompatible changes.
When an issue has all the necessary details, it can be scheduled for review by the Language Committee. Approved proposals are to be implemented by the development team and shipped in the appropriate stable release.
Only features and components published as Stable are in the scope of Language Committee. Besides, the Language Committee's scope is limited to the following:
kotlin-stdlib-jdk7
)-X/-XX
keysFor example, the following matters are out of scope for the Language Committee:
Also, see Appendix A for examples of changes that are not considered to be compatibility issues.
Normally, incompatible changes to stable features only land in feature releases. We announce the changes in a previous release (it may be an incremental update to the previous feature release, but should allow enough time for users to migrate their code).
The announcement is best done through compiler warnings, or in some cases IDE inspections and/or other tools. If places in the code that should be changed in advance to prevent compatibility issues in the future can be detected automatically, we announce the change and publish detection tools.
If the necessary changes to user code can be automated, we publish a migration tool along with the announcement. Preferably, the IDE should suggest running the migration when it encounters deprecated constructs in the code.
When automatic detection and/or migration is not practically possible, we publish instructions on how user code should be adjusted.
Some types of changes can be made without a deprecation cycle:
The typical deprecation cycle for an incompatible language change:
-progressive
flag enables the change before version B (e.g. in version A or an incremental update to it)-language-version
and -api-version
) support the behavior of version AThe typical deprecation cycle for libraries:
@Deprecated(level = WARNING)
@Deprecated(level = ERROR)
@Deprecated(level = HIDDEN)
Changes to these guidelines need to be approved by the Kotlin Language Committee.
Any proposed change needs to be published in advance providing a reasonable time to allow for comments on the change by the Kotlin Community.
The following cases are not considered to be compatibility issues and thus are out of scope for the Language Committee.
@InlineOnly
are not changing the ABI on the JVM.hashCode()
are not breaking changes.toString()
on other than Boolean
, Numeric
, and String
types are not breaking changes.We recognize that runtime performance and bytecode size are important metrics, and will make reasonable effort to keep them in a good shape, but we don't consider every slowdown (e.g. in edge cases or in very cold code) and every extra byte in the classfile a breaking change.
We make decisions on language changes under the assumption that most user code is designed with the following considerations in mind. We do not optimize for smooth migration that disregards these considerations.
All Kotlin code should be linked and run against the Standard Library of at least the same version as specified by the -api-version
upon compilation.
Improvements in type inference algorithms may result in more precise static types known for some expressions, this may cause changes in overload resolution and even in signatures of declarations that don't specify return types explicitly.
Overloads of the same function should be intended to do the same thing. Language improvements may cause a different overload to be selected in a new language version.
Declarations sensitive to API/ABI stability (e.g. public APIs) should specify return types explicitly. Language improvements may cause a different type to be inferred for a given body expression. Also, some innocuous-looking changes in the source code, done by the user, may cause similar effects.
Internal declarations have no separate compilation guarantees. While sometimes accessible from other languages (e.g. Java), declarations marked internal in Kotlin should not be called from outside the module they are declared in.
Private and synthetic declarations should not be relied upon. On some platforms, reflection has access to declarations marked private and synthetic. We do not provide any compatibility guarantees for code that relies on such access.
A Kotlin API exposed to another language may yield values that work in that language differently from Kotlin. For example, Kotlin's unsigned integers will look signed for Java clients, and the programmer that works with the same API in the Java code will be surprised by getting different result. While an undesirable situation, this is sometimes inevitable, and should not be considered a breaking change (it does not fall under the intuitive definition of one anyway).