skip to content

Across Room, Dagger/Hilt, and Moshi, which processors ship native KSP support and which were historically kapt-only? How does that influence how you set up a module's build today?

level: seniorimportance: should knowfreq 50%

answer

  1. Room + Moshi codegen = native KSP (long-standing)
  2. Dagger/Hilt = kapt-only historically, KSP since 2.48
  3. Goal: drop kotlin-kapt entirely → no stub pass
  4. Keep kapt only for still-kapt-only libraries
  5. KSP version pinned to Kotlin version

basics

~20 s

Room and Moshi codegen have supported KSP for a while; Dagger/Hilt were kapt-only for years but modern versions added KSP. Today you wire all three via ksp(...) and only fall back to kapt for processors that still lack KSP.

solid answer

~40 s

Room has long shipped native KSP. Moshi's moshi-kotlin-codegen ships native KSP. Dagger/Hilt were kapt-only historically but gained native KSP support in Dagger 2.48+. So a modern module can put all three compilers on ksp(...) and skip kapt entirely, which removes the Java-stub pass and speeds up builds. You keep the kotlin-kapt plugin only if some remaining library (e.g., an older third-party processor) is still kapt-only; in that case you apply both plugins and mix configurations. Practically: apply the KSP plugin, align its version with your Kotlin version, move each compiler to ksp(...), and verify generated sources and tests still build, since processors can occasionally behave differently between the two backends.

code

kotlin · 10 lines
kotlin
plugins {
    id("com.google.devtools.ksp")
    id("com.google.dagger.hilt.android")
}

dependencies {
    ksp("androidx.room:room-compiler")
    ksp("com.squareup.moshi:moshi-kotlin-codegen")
    ksp("com.google.dagger:hilt-android-compiler")
}

go deeper

for a junior

Recognizes that ksp(...) is preferred and that some processors used kapt.

for a middle

Names which of Room/Moshi/Dagger support KSP and when Dagger gained it.

for a senior

Designs a kapt-free module, explains the stub-pass cost, and handles mixed kapt+KSP correctly.

for a principal

Drives a build-wide kapt elimination, benchmarks the speedup, and manages version-catalog coupling across many modules.

## Why this matters The choice between `ksp(...)` and `kapt(...)` is dictated by **what the processor supports**. kapt forces a slow Java-stub generation pass; KSP reads Kotlin directly and is much faster. Knowing each library's status lets you avoid kapt where possible. ## Status of the three common processors - **Room** (`androidx.room:room-compiler`) — **native KSP** for a long time. Use `ksp(...)`. - **Moshi** (`com.squareup.moshi:moshi-kotlin-codegen`) — **native KSP**. Use `ksp(...)`. - **Dagger / Hilt** (`dagger-compiler`, `hilt-android-compiler`) — **historically kapt-only**; **Dagger 2.48+ added native KSP**. Modern versions: use `ksp(...)`. (Hilt additionally needs its own Gradle plugin for bytecode transformation, regardless of backend.) ## How this shapes a modern build ```kotlin plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") // Hilt-only extra } dependencies { ksp("androidx.room:room-compiler") ksp("com.squareup.moshi:moshi-kotlin-codegen") ksp("com.google.dagger:hilt-android-compiler") } ``` No `kotlin-kapt` plugin is needed if every processor supports KSP — that's the goal because it deletes the stub pass. ## When you still need kapt If one library in the module is **still kapt-only** (some older third-party processors are), apply **both** plugins and mix: ```kotlin plugins { id("org.jetbrains.kotlin.kapt") id("com.google.devtools.ksp") } dependencies { ksp("androidx.room:room-compiler") kapt("com.legacy:old-processor") // no KSP support yet } ``` ## Operational notes - **KSP versions are pinned to Kotlin versions** — bump them together (ideally via a version catalog). - After migrating a processor from kapt to KSP, **re-run the build and tests**: the two backends can surface annotations/symbols slightly differently in edge cases. - Dropping kapt entirely typically yields a noticeable clean-build speedup because the stub-generation phase is gone.

  • Why is removing the kotlin-kapt plugin a build-speed win?
    kapt runs a separate Java-stub generation pass over your Kotlin sources before processors run; KSP skips that by reading Kotlin symbols directly, so deleting kapt removes that whole phase.
  • Your module has Room (KSP) and one legacy processor with no KSP support. What's the setup?
    Apply both the KSP and kotlin-kapt plugins, put room-compiler on ksp(...) and the legacy processor on kapt(...); they run as independent passes in the same compilation.

saying these in an interview costs you the question

  • Saying Dagger/Hilt can never use KSP
  • Believing Room or Moshi codegen require kapt
  • Keeping kotlin-kapt applied with no kapt(...) dependency left
  • Ignoring KSP/Kotlin version coupling when bumping Kotlin

context