skip to content

A teammate adds kotlin-reflect manually and pins it to a version different from the Kotlin compiler. What can go wrong, and how should the kotlin-reflect version be managed in a Gradle build?

level: seniorimportance: should knowfreq 30%

answer

  1. kotlin-reflect must match the compiler/stdlib version
  2. Reads @Metadata — format evolves with the compiler
  3. Use kotlin("reflect") — version inherited from plugin
  4. Or constrain via kotlin-bom platform()
  5. Gradle resolves duplicates to highest — verify it matches

basics

~10 s

kotlin-reflect must match your Kotlin compiler version. A mismatch can cause incompatible-metadata warnings or runtime failures. Let the Kotlin Gradle plugin manage the version instead of hardcoding one.

solid answer

~40 s

kotlin-reflect reads the `@Metadata` the Kotlin compiler embeds, so its version must align with the compiler/stdlib version. If kotlin-reflect is *older* than the metadata it tries to read, it may not understand newer metadata and can throw or warn ('was compiled with a newer Kotlin version'); if it drifts the other way you get an inconsistent toolchain. Best practice: don't hardcode a version — the Kotlin Gradle plugin aligns kotlin-stdlib/kotlin-reflect to the plugin version automatically, so declare `implementation("org.jetbrains.kotlin:kotlin-reflect")` with no version, or use the Kotlin BOM (`platform("org.jetbrains.kotlin:kotlin-bom")`). Use `./gradlew dependencies` to confirm a single resolved Kotlin version, and rely on a failing resolution strategy if you want mismatches to fail the build. Frameworks pulling kotlin-reflect transitively can introduce a conflicting version, which Gradle resolves to the highest — verify it still matches your compiler.

code

kotlin · 10 lines
kotlin
// build.gradle.kts
plugins {
    kotlin("jvm") version "2.1.0"
}

dependencies {
    // Version-less: aligned to the Kotlin plugin automatically
    implementation(kotlin("reflect"))
    // Equivalent to: org.jetbrains.kotlin:kotlin-reflect:2.1.0
}

go deeper

for a junior

Adds kotlin-reflect but doesn't realize version must track the compiler.

for a middle

Knows to match versions and uses kotlin("reflect") or the BOM.

for a senior

Explains @Metadata coupling, Gradle highest-version resolution, and how to verify/enforce alignment.

for a principal

Sets a build-platform convention (BOM/version catalog + failOnVersionConflict) so drift can't reach production.

## Why version alignment matters The Kotlin compiler embeds structural info in an `@Metadata` annotation on every generated `.class`. `kotlin-reflect` is the runtime that *reads* that metadata to build `KClass`/`KCallable` objects. Because the metadata format evolves with the compiler, **kotlin-reflect's version must be compatible with the compiler that produced your code** (and with `kotlin-stdlib`). Treat `kotlin-stdlib`, `kotlin-reflect`, and the compiler/Gradle-plugin version as one coupled set. ## What goes wrong on mismatch - **Old kotlin-reflect, newer code**: it may not understand newer metadata → warnings like 'class was compiled with a newer version of Kotlin' or, in worse cases, `UnsupportedOperationException`/incorrect results when reflecting. - **Mixed Kotlin versions on the classpath**: Gradle resolves duplicates to the *highest* version, which can silently upgrade or downgrade one artifact relative to the others, producing a subtly inconsistent toolchain. - **Transitive drift**: Spring Boot, Jackson's Kotlin module, etc. each declare a kotlin-reflect version; if your project pins a different one, conflict resolution decides the winner — not necessarily the one matching your compiler. ## How to manage the version correctly ### 1. Let the Kotlin Gradle plugin align it (preferred) The Kotlin Gradle plugin coordinates `kotlin-stdlib`/`kotlin-reflect` with the plugin version. Declare the dependency **without** a version: ```kotlin plugins { kotlin("jvm") version "2.x.y" } dependencies { implementation(kotlin("reflect")) // == org.jetbrains.kotlin:kotlin-reflect, version aligned } ``` `kotlin("reflect")` is the idiomatic helper and inherits the plugin's Kotlin version. ### 2. Or use the Kotlin BOM ```kotlin dependencies { implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation("org.jetbrains.kotlin:kotlin-reflect") } ``` The BOM constrains all Kotlin artifacts to one consistent version. ### 3. Verify and enforce - `./gradlew dependencies --configuration runtimeClasspath` — confirm one resolved Kotlin version across stdlib/reflect. - A `resolutionStrategy { failOnVersionConflict() }` or `eachDependency` rule can force-align Kotlin artifacts and fail the build on drift. ## Practical guidance - Never hardcode a literal kotlin-reflect version that differs from your compiler. - After bumping the Kotlin plugin, you get the matching kotlin-reflect automatically — that's the point of not pinning it. - If only tests reflect, put it on `testImplementation` so prod stays smaller and there's one fewer version to align in the shipped artifact.

  • Why is hardcoding kotlin-reflect to a fixed version risky across Kotlin upgrades?
    On every compiler bump you must remember to bump the pin too; forget, and reflect lags the metadata format, causing 'newer Kotlin version' warnings or reflective failures.
  • If two libraries bring different kotlin-reflect versions, which wins?
    By default Gradle picks the highest version. You should verify it still matches your compiler, or use a resolutionStrategy to force-align Kotlin artifacts.

saying these in an interview costs you the question

  • Hardcoding a kotlin-reflect version unrelated to the compiler
  • Thinking kotlin-reflect version is independent of stdlib/compiler
  • Not knowing kotlin("reflect") inherits the plugin version
  • Ignoring transitive kotlin-reflect from frameworks
  • Assuming Gradle picks the lowest version on conflict

context