KAPT vs KSP

You picked solid tools like Room for your database, Hilt for dependency injection, maybe Retrofit or Moshi for networking and all good decisions.
But now something feels off.
You change a small thing like a model class or a DAO method and suddenly you're just…waiting. Gradle keeps running, the build drags on, and your flow gets interrupted before you even had a chance to get into it.
The main reason is often KAPT (Kotlin Annotation Processing Tool) which was never really built for Kotlin. It's more like a workaround to support Java-style annotation processing in Kotlin projects. As your project grows, it becomes heavier and slower.
Simple takeaway: more annotation processing means longer build times and a slower development experience.
So what exactly is annotation processing?
Before we talk about the fix, it helps to understand what's actually happening.
Libraries like Room, Hilt, and Dagger don't just sit in your project and do things at runtime. A big part of what makes them powerful is that they generate code at compile time.
When you write a @Dao interface or annotate a class with @HiltViewModel, the annotation processor reads what you wrote and generates the actual implementation for you, the boilerplate you'd otherwise have to write yourself. You just use the result.
That generation step happens between writing your code and running it. And that step has a cost.
Why KAPT is the problem
Kotlin doesn’t handle annotation processing the same way Java does, so when KAPT runs, it doesn't work with your Kotlin code directly
First, it converts your Kotlin files into Java-like stubs (basically a rough Java version of your code) and then passes those to the annotation processors. That extra conversion takes time, and it happens on every build. On top of that, since it’s looking at a Java version of your code, it doesn’t fully understand Kotlin-specific features
And incremental compilation, the thing that's supposed to make rebuilds faster doesn't work reliably with KAPT because of this.
plugins {
id("kotlin-kapt")
}
dependencies {
kapt("androidx.room:room-compiler:2.6.1")
kapt("com.google.dagger:hilt-compiler:2.51.1")
// Tests have their own separate configs
kaptTest("com.google.dagger:hilt-compiler:2.51.1")
kaptAndroidTest("com.google.dagger:hilt-compiler:2.51.1")
}
KAPT got the job done for years. But it was always a workaround, not a real solution. And the bigger your project gets, the more you feel that.
Enter KSP
KSP (Kotlin Symbol Processing) is built by Google specifically for Kotlin, and it takes a very different approach. Instead of converting your code into Java stubs, KSP works directly with your Kotlin code using the compiler itself.
It understands your code the way Kotlin sees it in the form of classes, functions, properties, annotations, all in native Kotlin terms with no conversion needed. Because of that, there’s no extra overhead like in KAPT.
It also supports proper incremental builds, so if you change just one DAO method, only that part gets processed again instead of rebuilding everything
The result is builds that are roughly 2× faster on average. On larger projects, the difference is even more noticeable.
kotlin
plugins {
id("com.google.devtools.ksp") version "2.0.0-1.0.22"
}
dependencies {
ksp("androidx.room:room-compiler:2.6.1")
ksp("com.google.dagger:hilt-compiler:2.51.1")
// Same pattern for tests
kspTest("com.google.dagger:hilt-compiler:2.51.1")
kspAndroidTest("com.google.dagger:hilt-compiler:2.51.1")
}
How KAPT and KSP actually compare
| KAPT | KSP | |
|---|---|---|
| How it reads your code | Converts to Java stubs first | Reads Kotlin directly |
| Kotlin type awareness | None — works on Java stubs | Full Kotlin type system |
| Incremental builds | Unreliable | Works properly |
| Speed | Slower | ~2× faster on average |
| Where it's headed | Maintenance mode | Actively developed |
Migrating from KAPT to KSP
The migration itself is mostly straightforward. You're not touching your app logic — just the build configuration.
Step 1 : Add the KSP plugin
kotlin
// In your project-level build.gradle.kts
plugins {
id("com.google.devtools.ksp") version "2.0.0-1.0.22" apply false
}
// In your app-level build.gradle.kts
plugins {
id("com.google.devtools.ksp")
}
Step 2 : Replace kapt() with ksp()
// Before
kapt("androidx.room:room-compiler:2.6.1")
kapt("com.google.dagger:hilt-compiler:2.51.1")
// After
ksp("androidx.room:room-compiler:2.6.1")
ksp("com.google.dagger:hilt-compiler:2.51.1")
Step 3 : Remove the KAPT plugin
kotlin
// Just delete this line from your plugins block
// id("kotlin-kapt")
Step 4 : Clean build and you're done!!
Room has supported KSP since version 2.4.3. Hilt added KSP support in 2.48. If you're on current versions and you probably are then both can be migrated right now.
What if a library doesn't support KSP yet?
Not everything has moved over. Some older or less maintained libraries still only support KAPT.
The good news is you don't have to choose one or the other. You can run KAPT and KSP in the same project and they won't conflict. Migrate what you can, leave the rest on KAPT, and revisit when those libraries catch up.
Before migrating any dependency, quickly check their GitHub or changelog for when KSP support was added. Most libraries that support it will mention it clearly.
Should new projects start with KSP?
Short answer is yes!!
If you’re starting a new project today, there’s really no reason to begin with KAPT
KSP is stable, well-supported, and clearly where things are headed.
For existing projects, you don’t have to switch everything at once
Start small, migrate libraries like Room and Hilt first since they already support KSP well. You’ll likely notice faster builds almost immediately.
KAPT isn’t disappearing anytime soon, Google isn’t removing it
But it’s mostly in maintenance mode now, while KSP is getting the real attention
The bigger point
Slow build times aren’t something you just have to accept, they’re a result of your setup and you can improve that without a complete rewrite.
Faster builds aren't just a nice-to-have. They mean you iterate faster, stay in flow longer, and ship with more confidence. That's worth 10 minutes of migration.
If this helped you, drop a reaction and share it with your team. And if you're migrating a large project, I'd love to hear how much build time you saved, drop a comment below.



