Java vs Kotlin no és només sintaxi: mantenibilitat, equips i deute tècnic
Java vs Kotlin des de decisions d'equip i manteniment real, no des de features superficials. Experiència en projectes reals.

En un article anterior ja vaig cobrir la comparativa tècnica entre Java i Kotlin: data classes, null safety, coroutines, extensions. Les diferències de sintaxi són reals i estan ben documentades a qualsevol tutorial. Però la realitat és que quan portes uns anys mantenint projectes amb tots dos llenguatges, les decisions que més pesen no són les de sintaxi. Són les d’equip, manteniment i deute tècnic.
Aquest article no repeteix la comparativa de features. Parteix d’un punt diferent: ja saps que Kotlin és més concís, que té null safety i que comparteix la JVM amb Java. La pregunta és què passa quan això es troba amb un equip de sis persones, un projecte que porta tres anys en producció i una base de codi que creix cada sprint.
La corba d’aprenentatge que ningú t’explica
La corba d’aprenentatge de Kotlin per a un desenvolupador Java se sol presentar com a suau. I és veritat que escriure la teva primera data class o el teu primer when és intuïtiu. Però la corba real no és la d’escriure Kotlin bàsic. És la d’escriure Kotlin idiomàtic, entendre codi Kotlin escrit per altres, i navegar una base de codi que barreja tots dos llenguatges.
El que costa de veritat
- Funcions d’extensió. Un dev Java que llegeix
myList.filterNotNull().groupBy { it.category }necessita entendre no només què fa, sinó on estan definides aquestes funcions i si alguna és custom del projecte. - Scope functions.
let,run,apply,also,with. Cadascuna té un cas d’ús, però a la pràctica la gent les barreja, n’abusa, i el codi es torna opac. - Coroutines. Per a algú que ve de threads Java o CompletableFuture, les coroutines no són difícils conceptualment, però el model de concurrència estructurada requereix temps per dominar-lo sense crear bugs subtils.
- Inferència de tipus. Kotlin infereix molt. Això és còmode a l’hora d’escriure, però de vegades dificulta la lectura quan no queda clar quin tipus retorna una expressió complexa.
En la meva experiència, un desenvolupador Java sènior tarda unes 2-3 setmanes a ser productiu en Kotlin i uns 2-3 mesos a escriure Kotlin que un altre kotliner revisaria sense arrugar el nas.
La corba d’aprenentatge de Kotlin no és aprendre Kotlin. És desaprendre Java. Els patrons mentals de Java ---verbositat explícita, getters/setters, null checks manuals--- no se’n van en una setmana.
Llegibilitat: concís no sempre és més clar
Un dels avantatges més citats de Kotlin és que necessites menys codi per fer el mateix. I és cert. Un data class a Kotlin són 3 línies; a Java (sense records) eren 40. Però la concisió té un punt d’inflexió on deixa d’ajudar.
Quan Kotlin és més llegible
// Kotlin: claro, directo
val activeUsers = users
.filter { it.isActive }
.sortedByDescending { it.lastLogin }
.take(10)// Java: más verboso pero igualmente claro
List<User> activeUsers = users.stream()
.filter(User::isActive)
.sorted(Comparator.comparing(User::getLastLogin).reversed())
.limit(10)
.collect(Collectors.toList());Aquí Kotlin guanya netament. Menys soroll, mateixa intenció.
Quan Kotlin es torna opac
// Kotlin: "elegante" pero difícil de seguir
val result = data?.let { raw ->
raw.items
.filterNotNull()
.takeIf { it.isNotEmpty() }
?.groupBy { it.category }
?.mapValues { (_, items) ->
items.sumOf { it.price }
}
?.also { cache.store(it) }
} ?: emptyMap()Aquest codi és correcte, compila, i fa servir features idiomàtiques de Kotlin. Però pregunta-li a un desenvolupador que no el va escriure què fa exactament. Necessitarà un minut per seguir la cadena de let, takeIf, also i l’operador elvis. En un code review, aquest codi genera preguntes.
L’equivalent en Java seria més llarg, però cada pas seria explícit. I l’explicitat, en manteniment a llarg termini, té un valor que se subestima.
La meva regla personal
Si una expressió Kotlin necessita més de 5 segons perquè algú de l’equip entengui què fa, probablement val la pena escriure-la de forma més explícita. La concisió és una eina, no un objectiu.
Null safety a la pràctica: més enllà de la feature
El sistema de tipus nullable de Kotlin és, al meu parer, la seva millor feature. Obliga a pensar en nuls en temps de compilació en lloc de descobrir-los en producció. Però el seu impacte real depèn de com es faci servir.
El bo: NullPointerExceptions que no arriben a producció
En un projecte Java que mantinc, el top 3 de bugs en producció són NullPointerExceptions. En l’equivalent Kotlin, els NPE són pràcticament inexistents. No perquè el codi sigui perfecte, sinó perquè el compilador t’obliga a gestionar cada cas nul abans de compilar.
// El compilador no te deja ignorar el nulo
fun getOrderTotal(order: Order?): BigDecimal {
// Esto no compila:
// return order.total
// Tienes que ser explícito:
return order?.total ?: BigDecimal.ZERO
}El dolent: l’abús de l’operador !!
L’operador !! (non-null assertion) existeix per als casos on tu saps que un valor no és nul però el compilador no pot verificar-ho. A la pràctica, es converteix en una vàlvula d’escapament que elimina la garantia del null safety.
// Esto es básicamente volver a Java
val name = user!!.name!!.uppercase()
// Si user o name son null, NPE en runtime. Igual que Java.He vist codebases de Kotlin amb centenars de !!. A aquestes altures, el null safety és decoratiu. Si permets !! sense restricció al teu equip, estàs pagant el cost de Kotlin sense obtenir el seu major benefici.
El cas problemàtic: interoperabilitat amb Java
Quan el teu codi Kotlin crida a una llibreria Java, els tipus que tornen són “platform types” ---ni nullable ni non-null. El compilador no t’avisa. Si la llibreria Java retorna null on no esperes, el teu codi Kotlin explota igual que Java.
// Una librería Java que devuelve String (platform type)
val result = javaLibrary.getResult()
// result es String! (platform type), no String ni String?
// Si es null, NPE en runtime sin warning del compiladorAixò és rellevant perquè la majoria de projectes Kotlin en backend fan servir llibreries Java. Spring Framework, Jackson, Hibernate, la pròpia JDK. Els platform types són l’esquerda per on es colen els nuls que el compilador de Kotlin no pot detectar.
Ecosistema i tooling
Spring Boot: la història d’èxit
Spring Boot funciona bé amb Kotlin. Hi ha suport oficial, extensions, i la comunitat és activa. Si el teu stack és Spring Boot, migrar a Kotlin és viable i l’experiència és bona.
Però “funciona bé” no significa “funciona igual”. Hi ha matisos:
- Classes obertes. Spring necessita que moltes classes siguin obertes per fer proxies. Kotlin les tanca per defecte. Necessites el plugin
kotlin-springperquè les obri automàticament. - Data classes com a entitats JPA. Hibernate necessita constructors sense arguments. Les data classes de Kotlin no en tenen per defecte. Necessites el plugin
kotlin-jpa. - Anotacions. Algunes anotacions de Spring no es comporten igual a Kotlin.
@RequestParamamb valors per defecte,@Valueamb propietats, etc. Són solucionables, però són sorpreses que costen temps.
Llibreries de l’ecosistema
La immensa majoria de llibreries JVM són Java. Funcionen des de Kotlin, però l’experiència no sempre és idiomàtica. Hi ha llibreries Kotlin-first (Ktor, Exposed, kotlinx.serialization), però són un ecosistema més petit.
| Aspecte | Java | Kotlin |
|---|---|---|
| Llibreries disponibles | Totes | Totes les de Java + les natives de Kotlin |
| Documentació i exemples | Abundant | Creixent, però encara menor |
| Stack Overflow | Moltíssim | Bastant, però menys |
| Tooling IntelliJ | Excel·lent | Excel·lent |
| Plugins Gradle/Maven | Madurs | Madurs (Gradle millor que Maven) |
Contractació: la conversa que ningú vol tenir
Aquest és l’elefant a l’habitació. Si estàs decidint entre Java i Kotlin per a un projecte nou, la disponibilitat de talent importa tant com les features del llenguatge.
La realitat del mercat
- Desenvolupadors Java senior: Abundants. Fàcil de trobar. Ampli rang d’experiència.
- Desenvolupadors Kotlin senior (backend): Menys. La majoria vénen d’Android. Els que fan backend Kotlin són un subconjunt més petit.
- Desenvolupadors Java que aprendrien Kotlin: Molts. Però necessiten temps de rampeo.
He participat en processos de selecció on el requisit era “Kotlin backend”. El pool de candidats es reduïa a una fracció comparat amb “Java backend”. No perquè Kotlin sigui impopular, sinó perquè l’adopció en backend encara és menor que en Android.
El cost ocult
Quan contractes algú amb experiència en Java però no en Kotlin:
- Els primers 1-2 mesos produeix menys.
- El codi que escriu al principi és “Java amb sintaxi Kotlin” (no idiomàtic).
- Necessita mentoring d’algú de l’equip que sí que domini Kotlin.
- Els code reviews són més lents perquè el reviewer ha d’explicar patrons idiomàtics.
Això no és un argument en contra de Kotlin. És un argument a favor d’incloure el cost de rampeo en la decisió.
Equips mixtos: Java i Kotlin al mateix projecte
La interoperabilitat Java-Kotlin és real i funciona. Pots tenir mòduls en Java i mòduls en Kotlin al mateix projecte Gradle/Maven. IntelliJ els gestiona sense problemes. Però un projecte mixt té costos que no són tècnics.
El que funciona
- Mòduls separats per llenguatge. El mòdul legacy està en Java, els mòduls nous en Kotlin. Cadascun té el seu estil, el seu compilador, les seves dependències.
- Migració gradual. Vas convertint classes Java a Kotlin a mesura que les toques. IntelliJ té un conversor automàtic que, amb revisió manual, funciona raonablement bé.
El que genera fricció
- Dos estils al mateix repo. Els PRs barreja Java i Kotlin. Els desenvolupadors han de dominar tots dos per fer reviews.
- Convencions dividides. Les funcions utilitàries van en companion objects o en top-level functions? Els DTOs nous són data classes o records Java? Es permet
!!o es rebutja en review? - Build complexity. La compilació d’un projecte mixt Java+Kotlin és més lenta que Java pur o Kotlin pur. El compilador de Kotlin necessita analitzar les classes Java i viceversa en un ordre específic.
// Kotlin llamando a Java
val config = JavaLegacyConfig.getInstance() // returns Config!
// ¿Es nullable? Depende de la implementación Java.
// Kotlin no lo sabe. Tú tampoco sin leer el código Java.// Java llamando a Kotlin
KotlinService service = new KotlinService();
String result = service.process(input);
// ¿Puede devolver null? Depende de si en Kotlin es String o String?
// Kotlin genera anotaciones @Nullable/@NotNull, pero no siempre.La meva recomanació per a equips mixtos
- Defineix una guia d’estil que cobreixi tant Java com Kotlin. Quins patrons es fan servir a cada llenguatge, què està prohibit, com s’anomenen les coses.
- No converteixis codi Java que funciona només per convertir-lo. Si un mòdul Java és estable, està testejat i ningú el toca, deixar-lo en Java és una decisió vàlida.
- Kotlin per a codi nou, Java per al manteniment del legacy. És el patró que menys fricció genera a la pràctica.
- Assegura’t que tot l’equip pot llegir tots dos llenguatges. No cal que siguin experts en els dos, però sí que puguin fer review.
Deute tècnic: el que importa no és el de sintaxi
Quan algú proposa migrar de Java a Kotlin, l’argument sol ser “reduïm boilerplate, el codi és més net, menys deute tècnic”. I és parcialment cert. Kotlin elimina verbositat innecessària. Però el deute tècnic real d’un projecte no està als getters i setters.
El deute tècnic que importa és:
- Arquitectura mal dissenyada. Un servei God Class no millora per reescriure’l en Kotlin.
- Falta de tests. Migrar a Kotlin sense afegir tests és canviar el color del deute, no reduir-lo.
- Acoblament entre mòduls. Si els teus serveis estan acoblats, seguiran acoblats en Kotlin.
- Codi que ningú entén. Si un mètode Java de 200 línies és incomprensible, un mètode Kotlin de 80 línies amb scope functions imbricades pot ser-ho igual.
Canviar de llenguatge no redueix el deute tècnic. Redissenyar sí. El llenguatge és una eina; el deute tècnic és un problema de disseny.
He vist projectes on la migració a Kotlin es va fer servir com a excusa per reescriure mòduls sencers. I en alguns casos va funcionar, no perquè Kotlin fos millor que Java, sinó perquè la reescriptura va forçar un redisseny. Però això ho hauries pogut fer també en Java.
El meu criteri per a projectes nous
Si començo un projecte backend avui i tinc llibertat d’elecció:
| Situació | La meva elecció | Per què |
|---|---|---|
| Equip amb experiència Kotlin | Kotlin | Aprofites null safety, concisió, coroutines |
| Equip només Java, projecte llarg | Java (amb possibilitat de Kotlin futur) | No pagues cost de rampeo a l’inici |
| Microservei petit, equip reduït | Kotlin | Menys codi, menys superfície de bugs |
| Projecte amb moltes integracions Java legacy | Java | Menys fricció, menys platform types |
| MVP o prototip ràpid | Tant se val | El llenguatge no és el coll d’ampolla |
| Llibreria que altres equips consumiran | Java | Maximitza compatibilitat |
El que no apareix als benchmarks
Els benchmarks de Java vs Kotlin mesuren rendiment de compilació, rendiment en runtime, mida dels binaris. Cap d’ells mesura el que de veritat importa a llarg termini:
- Quantes hores tarda un nou membre de l’equip a ser productiu.
- Quants bugs arriben a producció per mal maneig de nuls.
- Quant tarda un code review en un projecte mixt.
- Quant costa trobar un candidat amb experiència en el teu stack.
- Quantes vegades algú ha trencat alguna cosa perquè no entenia una cadena de scope functions.
Aquestes mètriques són les que determinen si Kotlin és la decisió correcta per al teu equip. I depenen del teu context, no de les features del llenguatge.
Java no és un llenguatge obsolet. Kotlin no és una bala de plata. La millor decisió és la que el teu equip pot mantenir durant els propers tres anys sense acumular deute tècnic invisible. De vegades això és Kotlin. De vegades és Java. I de vegades són tots dos, amb un pla clar de convivència.


