KSP vs KAPT: A Practical Guide for Kotlin Annotation Processing
A decision guide for Kotlin projects: when to use KSP vs. KAPT, constraints, a comparison table, and a step-by-step migration with validation commands.
01 Jul 2026, 13:59 UTC

The Problem
Kotlin projects using annotation processors must choose between the legacy kapt plugin and the newer Kotlin Symbol Processing (KSP). This decision directly impacts build performance, multi-platform support, and long-term maintenance. Teams often struggle with whether migration is worth the effort or if they should stick with what works.
The Decision
Choose KSP for new modules and existing modules where the processor provides a KSP artifact. Retain KAPT only when a critical processor lacks KSP support or when migration cost outweighs benefits in a legacy codebase.
Constraints
- KSP requires Kotlin 1.7.0+ and a compatible
com.google.devtools.kspplugin version. - Processor authors must ship a KSP implementation (often as a separate artifact).
- Configuring both
kaptandkspfor the same processor in one module generates duplicate sources, causing compilation failures.
Supported Options Comparison
| Aspect | KSP | KAPT |
|---|---|---|
| Plugin ID | com.google.devtools.ksp | org.jetbrains.kotlin.kapt |
| Processor compatibility | Only processors with a KSP implementation | Any javax.annotation.processing.Processor |
| Incremental compilation | Native support; typically faster | Stub generation can break incrementality |
| Multi-platform targets | Kotlin/JVM, Kotlin/Native, Kotlin/JS | JVM only |
| Generated source location | build/generated/ksp/<sourceSet>/ | build/generated/kapt/<sourceSet>/ |
| Migration effort | Add KSP plugin, replace kapt deps with ksp deps | None (status quo) |
Trade-offs
Build performance
KSP skips the Java stub generation step that KAPT performs, yielding 10‑30% faster clean builds on medium-sized modules. The actual gain depends on the processor and module size—always benchmark on your own codebase.
Processor availability
Popular libraries (Room 2.5+, Dagger/Hilt 2.48+, Moshi 1.14+, kotlinx-serialization) ship KSP processors. If a dependency lacks a KSP artifact, KAPT remains necessary for that processor.
Multi-platform projects
Only KSP can generate code for Kotlin/Native or Kotlin/JS targets. KAPT is limited to JVM compilation.
Concrete Migration Example
Consider a Gradle module :data using Room with KAPT.
1. Apply the KSP plugin in settings
// settings.gradle.kts
pluginManagement {
plugins {
id("com.google.devtools.ksp") version "1.9.20-1.0.14" // match Kotlin version
}
}
2. Enable KSP and switch dependency
// data/build.gradle.kts
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-runtime:2.6.1")
// Remove: kapt("androidx.room:room-compiler:2.6.1")
}
3. Verify plugin activation
Run from project root (requires execute permission on ./gradlew):
./gradlew tasks --all | grep -i ksp
Expected output includes kspKotlin and kspTestKotlin. Missing tasks suggest plugin/Kotlin version incompatibility.
4. Clean build and compare timing
./gradlew clean compileKotlin compileTestKotlin
Record elapsed time, then revert changes and repeat with KAPT to establish a baseline. Compare on the same machine for valid results.
5. Inspect generated sources
After a successful build, generated files appear under:
data/build/generated/ksp/main/kotlin/...
Confirm Room DAO implementations and database classes exist. Their absence indicates the processor did not run.
Validation Checklist
- KSP plugin version matches Kotlin version (see KSP releases).
- All
kaptdependencies for migrated processors replaced withkspequivalents. - No module declares both
kaptandkspfor the same processor. - Clean build succeeds and generated sources exist under
build/generated/ksp/. - Build time improvement (or no regression) measured on the developer machine.
Limitations & Risks
- Missing KSP artifact: Libraries without a published KSP processor force continued KAPT usage for that dependency.
- Incremental quirks: Some KSP processors still trigger full recompilation on unrelated changes; test incremental behavior post-migration.
- Version drift: KSP releases are tied to specific Kotlin versions. Upgrading Kotlin without updating the KSP plugin breaks the build.
- Rollback: Reverting migration requires only restoring
build.gradle.ktschanges; no persistent state exists outside build directories.
Practical Verification Method
Create a CI job that runs ./gradlew clean compileKotlin compileTestKotlin on both KAPT and KSP configurations (separate branches or build profiles). Compare reported build times and verify artifact outputs (APK, JAR, or test results) are identical. This provides a reproducible signal that migration is safe.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.