skip to content

Common Processors (Room/Dagger/Moshi)

Room, Dagger and Hilt, and Moshi are the processors most Kotlin projects actually configure, and which of them support KSP decides your build times. Knowing the ksp versus kapt configuration line for each is practical interview currency.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

You're adding Room to a Kotlin module. Which Gradle dependency configuration do you use to wire up Room's annotation processor, and what else must you add to the build?

level: juniorimportance: must knowfreq 70%

answer

  1. ksp(room-compiler), not implementation
  2. Apply the KSP Gradle plugin first
  3. room-runtime + room-ktx + ksp compiler
  4. Room generates *_Impl classes
  5. Room is KSP-native — prefer ksp over kapt

basics

~20 s

You add the Room library as a normal dependency, and put Room's compiler on the ksp configuration (the modern way) instead of a plain dependency. You also apply the KSP Gradle plugin so ksp(...) exists.

solid answer

~30 s

Apply the KSP plugin (com.google.devtools.ksp), then in dependencies use implementation("androidx.room:room-runtime") and the Kotlin extensions implementation("androidx.room:room-ktx"), and crucially ksp("androidx.room:room-compiler") so the compiler that generates the *_Impl DAO/database classes runs. Room ships native KSP support, so prefer ksp(...) over kapt(...). The ksp() configuration tells KSP which artifact contains the SymbolProcessorProvider; without the plugin applied, ksp(...) is unresolved. With kapt you'd instead apply org.jetbrains.kotlin.kapt and use kapt("androidx.room:room-compiler"), but that path is slower and discouraged for Room.

code

kotlin · 10 lines
kotlin
plugins {
    id("org.jetbrains.kotlin.jvm")
    id("com.google.devtools.ksp")
}

dependencies {
    implementation("androidx.room:room-runtime")
    implementation("androidx.room:room-ktx")
    ksp("androidx.room:room-compiler")
}

go deeper

for a junior

Knows the compiler goes on ksp(...) and that the KSP plugin must be applied.

for a middle

Explains why implementation() doesn't invoke the processor and that Room is KSP-native.

for a senior

Discusses generated-source wiring, room-ktx vs runtime split, and the kapt fallback tradeoff.

for a principal

Can reason about migrating a whole multi-module build off kapt and standardizing the KSP version via the version catalog/convention plugins.

## What Room needs at build time Room generates code (DAO implementations like `UserDao_Impl`, the database `_Impl`) from your `@Entity`, `@Dao`, and `@Database` annotations. That code is produced by an **annotation processor**, which must run during compilation. In Gradle you express "run this processor" by placing the processor artifact on a special dependency **configuration**. ## The two configurations - **`ksp(...)`** — Kotlin Symbol Processing. The modern, Kotlin-native processor API. Faster because it reads Kotlin source directly (no Java stubs). - **`kapt(...)`** — Kotlin Annotation Processing Tool. The legacy bridge that generates Java stubs so Java-based (`javax.annotation.processing`) processors run. Slower. Room **ships native KSP support**, so use `ksp(...)`. ## Required pieces 1. **Apply the KSP plugin** so the `ksp(...)` configuration exists: ```kotlin plugins { id("com.google.devtools.ksp") } ``` 2. **Add dependencies** — runtime as a normal dependency, compiler on `ksp`: ```kotlin dependencies { implementation("androidx.room:room-runtime") implementation("androidx.room:room-ktx") // coroutines/Flow support ksp("androidx.room:room-compiler") // the processor } ``` ## Why not just `implementation` for the compiler? If you put `room-compiler` on `implementation`, the processor jar lands on the runtime classpath but is **never invoked**, so no `_Impl` classes are generated and you get "cannot find implementation" errors. The configuration name is what activates the processor. ## kapt alternative (discouraged for Room) ```kotlin plugins { id("org.jetbrains.kotlin.kapt") } dependencies { kapt("androidx.room:room-compiler") } ``` This works but is slower. Prefer KSP.

  • What happens if you apply the KSP plugin but forget the ksp("androidx.room:room-compiler") line?
    The configuration exists but no Room processor is registered, so no DAO/database implementation classes are generated and the build fails with missing _Impl classes.
  • Where does the generated code end up?
    Under build/generated/ksp/<variant>/kotlin (or .../java), and KSP automatically adds it to the source set so it compiles.

ksp(...) is like a backstage pass: it lets the processor onstage to generate code; a plain dependency just leaves it in the audience.

saying these in an interview costs you the question

  • Putting room-compiler on implementation or api
  • Forgetting to apply the KSP (or kapt) plugin
  • Thinking ksp() and implementation() are interchangeable
  • Claiming Room requires kapt

context

open as a page

A teammate wired Dagger/Hilt with kapt and asks whether they can just switch to ksp(...) for faster builds. How do you advise them, and how does the Gradle wiring differ for Hilt specifically?

level: middleimportance: should knowfreq 55%

basics

~10 s

Recent Dagger/Hilt versions support KSP, so they can move the compilers from kapt(...) to ksp(...). Hilt also needs its own Gradle plugin applied, regardless of kapt or KSP.

open as a page

How do you configure Moshi so that its adapters are generated at compile time, and how does that wiring differ from Moshi's reflection-based approach?

level: middleimportance: should knowfreq 45%

basics

~10 s

For compile-time adapters you add Moshi plus its codegen artifact on the ksp(...) configuration and mark classes @JsonClass(generateAdapter = true). The reflection approach instead adds moshi-kotlin and needs no processor at all.

open as a page

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%

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.

open as a page

In a multi-module Kotlin build using Room and Hilt via KSP, how do you pass processor options (e.g., Room's schema export directory) and keep the KSP/processor versions consistent across modules?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

You pass processor options through the ksp { arg(...) } block in the module (for example Room's schema directory), and you keep versions aligned by centralizing them in a version catalog or a shared convention plugin so every module uses the same KSP and processor versions.

open as a page