skip to content

As a tech lead migrating a large Kotlin project to the K2 compiler (Kotlin 2.0), what compatibility concerns and migration steps would you plan for?

level: principalimportance: nice to knowfreq 18%

answer

  1. K2 auto-default on 2.0 — risk is in tooling
  2. kapt compat path; prefer KSP2
  3. Plugins (serialization/all-open/Compose) must match Kotlin version
  4. Fix unsound casts / ambiguous overloads explicitly
  5. Full module + KMP-target + test matrix in CI

basics

~20 s

Bump to Kotlin 2.0 so K2 is the default, update plugins like kapt/serialization to K2-compatible versions, fix the small set of newly-rejected code, and run the full test/build matrix to confirm nothing broke before rolling out.

solid answer

~40 s

Plan it as a controlled upgrade. (1) Bump the Kotlin Gradle plugin to 2.0+; K2 then becomes the default frontend automatically. (2) Audit compiler-facing tooling: annotation processing (**kapt** runs through a compatibility path; prefer migrating to **KSP2** where possible), and **compiler plugins** (kotlinx.serialization, all-open/no-arg, Compose) must be on K2-compatible versions bundled with the Kotlin release. (3) Expect a small set of newly-failing compilations — unsound smart casts and genuinely ambiguous overloads — and fix them with explicit casts/disambiguation rather than suppressing. (4) Treat new warnings seriously; some become errors in later 2.x. (5) Run the whole build/test matrix (all modules, KMP targets if any) in CI, and roll out behind a branch. Optionally pin `languageVersion`/`apiVersion` to ease the source/binary compatibility window. The risk is concentrated in tooling/plugins, not application logic.

go deeper

for a junior

Knows you bump to Kotlin 2.0 and that K2 then applies automatically.

for a middle

Adds that plugins/annotation processors need compatible versions and that a little code may need fixing.

for a senior

Sequences a real migration: tooling audit, fix categories for rejected code, warning budget, full test matrix.

for a principal

Owns rollout strategy and risk: KSP2 path, KMP per-target verification, revert plan, and a policy for the warnings-becoming-errors timeline.

## Framing Migrating to K2 is mostly an **infrastructure** change. The frontend swap is automatic on Kotlin 2.0+; the real work is **tooling compatibility** and a small tail of **newly-correct diagnostics**. ## Step 1 — Bump the version (K2 is then default) ```kotlin // build.gradle.kts plugins { kotlin("jvm") version "2.0.0" // K2 is the default frontend on 2.0+ } ``` No `languageVersion=2.0` opt-in needed anymore. You *may* still set `languageVersion`/`apiVersion` to control which language features and stdlib API surface you allow, easing a staged rollout. ## Step 2 — Audit compiler-facing tooling (highest risk) - **kapt** (Kotlin annotation processing): runs under K2 via a compatibility mode. Where you depend on annotation processors, prefer migrating to **KSP2**, the K2-native symbol-processing API; verify each processor (Dagger/Hilt, Room, Moshi, etc.) has a K2/KSP2-ready version. - **Compiler plugins**: these hook into compiler internals and *must* match the compiler. Bundled-with-Kotlin plugins — **all-open**, **no-arg**, **kotlinx.serialization**, **Compose** — need versions aligned to the Kotlin release. A mismatched plugin is the classic migration failure. ## Step 3 — Fix newly-rejected code Because K2 is more correct, a handful of previously-compiling files now error. Categories and fixes: - **Unsound smart cast** → add an explicit `as`/`as?` or restructure so the value is a stable `val`. - **Ambiguous overload** → disambiguate with explicit argument types or named arguments. - **Stricter nullability/inference edges** → annotate the type explicitly. Fix, don't blanket-`@Suppress`; these are real latent bugs. ## Step 4 — Treat warnings as a budget K2 emits new/clearer warnings; some are slated to become errors in later 2.x minors. Track and burn them down so the *next* upgrade is cheap. ## Step 5 — Verify across the whole matrix - All modules build; all tests pass. - If **Kotlin Multiplatform**, verify every target (JVM/JS/Native) — K2 unified the frontend across platforms, which is a benefit but still needs per-target validation. - IDE: ensure the team's IDE/version uses the K2 IDE mode consistently. ## Step 6 — Roll out safely Do it on a branch, run full CI, and merge when green. Keep the old toolchain pinned so you can revert quickly if a plugin proves incompatible. ## What is NOT a concern - The **backend/bytecode** is largely shared — runtime behavior of correct code is unchanged. - You don't rewrite application logic; the diff is dominated by version bumps and a small set of targeted fixes.

  • Why are compiler plugins the riskiest part of the migration?
    They hook into compiler internals (now FIR/IR), so a plugin built for the old frontend won't load under K2; each must be on a K2-compatible version matched to the Kotlin release.
  • What's the difference between kapt and KSP2 here?
    kapt processes annotations by generating Java stubs and runs under a compatibility path on K2; KSP2 is the K2-native symbol-processing API — faster and the recommended target where processors support it.

saying these in an interview costs you the question

  • Treating it as a pure app-code rewrite rather than a tooling/version upgrade
  • Forgetting that compiler plugins must match the Kotlin version
  • Suppressing newly-surfaced errors instead of fixing the underlying unsoundness
  • Ignoring KMP per-target validation
  • Assuming runtime/bytecode behavior changes for correct code

context