Gradual Java-to-Kotlin migration
Do not rewrite a stable application merely to change language. Migrate where normal product work creates value.
Practical sequence
- Establish Kotlin style, compiler settings, static analysis, and CI.
- Add Java nullability annotations at shared boundaries.
- Start new leaf features/utilities in Kotlin.
- Convert tests to learn behavior before production conversion.
- Convert a class when modifying it for a real feature or defect.
- Redesign the API when needed; do not preserve poor Java shape blindly.
- Track build time, defects, binary size, and team learning.
Boundary adapter
class LegacyUserAdapter(private val api: LegacyUserApi) {
fun user(id: Long): UserResult {
val raw = api.findUser(id) ?: return UserResult.NotFound
val name = raw.name?.trim()?.takeIf(String::isNotEmpty)
?: return UserResult.Invalid
return UserResult.Success(User(id, name))
}
}
The adapter contains platform types and legacy conventions. Modern Kotlin code receives explicit domain states.
Coroutines around callbacks
Use suspendCancellableCoroutine only with correct cancellation and exactly-once resumption. Prefer library-supported adapters where available. Removing the listener on cancellation is essential when the API permits it.
Android build considerations
- Mixed Kotlin/Java modules are normal.
- KAPT/KSP, annotation processors, and generated-code order can affect build time.
- Public APIs may require binary compatibility review.
- R8 rules, reflection, serialization, and JNI may depend on generated names.
- Measure incremental and clean builds during migration.
✕ Convert thousands of files in one unreviewable change
Risk: hidden behavior changes, painful rollback, merge conflicts, no learning loop
✓ Migrate vertical slices with tests and metrics
Feature change → characterize behavior → convert boundary → review Java callers → measure
Architect takeaway
Migration is a risk-management program, not a syntax conversion. Protect interop boundaries, preserve behavior with tests, migrate incrementally, and measure outcomes.