Pollito Blog
February 12, 2025

Construí la misma app tres veces

Posted on February 12, 2025  •  8 minutes  • 1656 words  • Other languages:  English

Inspiración

Este blog está fuertemente inspirado en el video de Theo “I built the same app with 5 different stacks”.

Así que decidí hacer mi versión, pero usando dos lenguajes que ya conozco bien (Java y Groovy ) más un lenguaje nuevo que quería probar desde hace tiempo: Kotlin .

Acá tenés el código de los repositorios:

Entendiendo la aplicación

Hice el típico ejercicio de programación “Roundest Pokémon”:

Agregué un giro:

Acá tenés el código del frontend en Next.js .

Métricas rápidas

Este blog es, sobre todo, una comparación de backends. Spoiler: en términos de rendimiento en este pequeño proyecto, son iguales.

Hagamos una comparación de las bases de código:

Las siguientes tablas fueron generadas usando cloc .

Aplicación de backend Groovy

Language files blank comment code
Groovy 22 174 1 727
YAML 3 0 0 296
SQL 1 1 2 157
Bourne Shell 1 28 118 106
Gradle 2 12 0 96
DOS Batch 1 21 2 71
Markdown 1 10 0 47
Dockerfile 1 1 2 7
Properties 1 0 0 7
SUM: 33 247 125 1514

Aplicación backend Java

Language files blank comment code
Java 22 131 2 660
YAML 3 0 0 296
SQL 1 1 2 157
Gradle 2 12 0 108
Bourne Shell 1 28 118 106
DOS Batch 1 21 2 71
Markdown 1 9 0 45
Dockerfile 1 1 2 7
Properties 1 0 0 7
SUM: 33 203 126 1457

Aplicación backend Kotlin

Language files blank comment code
Kotlin 24 134 4 589
YAML 3 0 0 301
Gradle 2 25 0 167
SQL 1 1 2 157
Bourne Shell 1 28 118 106
DOS Batch 1 21 2 71
Markdown 1 9 0 44
Dockerfile 1 1 2 8
Properties 1 0 0 7
SUM: 35 219 128 1450

En cuanto a los tiempos de despliegue, tampoco hay nada destacable:

Tengo todos los backends con los mismos límites de recursos muy conservadores:

resource-limits.png

En estado inactivo, tienen un uso aceptable de CPU y memoria. Todos muestran:

No hay lo bueno, lo malo y lo feo

Las tres opciones son totalmente válidas para un proyecto grande y serio, y entrarían en la categoría de “Lo bueno”.

Diría que una frase mejor sería “Lo bueno, el primer amor y la decepción”. Vamos uno por uno.

Lo bueno: Java

Empecemos intercalando el obvio chiste de public static void String main args acá.

Dato curioso, public static void String main args ya no es necesario desde Java 21 .

En una palabra, Java es confiable:

Todo funcionó a la perfección, probablemente porque Java es lo que he estado haciendo 8 horas por día, 5 días a la semana, por más de 2 años a estas alturas.

Java no es glamoroso, pero es cómodo.

honest-work-meme-c7034f8bd7b11467e1bfbe14b87a5f6a14a5274b.jpg

El primer amor: Groovy

Mi viaje con Groovy empezó allá por 2021. Recordé que en la entrevista de laburo solo me preguntaron dos cosas:

Era tiempos más simples.

Sin darme cuenta, me sumé a un proyecto construido con Grails , un framework monolítico muy de nicho que usa Groovy como lenguaje principal.

Me enamoré rápidamente de su sintaxis expresiva y de cómo buscaba mejorar Java reduciendo el código innecesario y adoptando un estilo más dinámico.

Aun así, Groovy sigue siendo el artista indie de los lenguajes JVM: querido por quienes escriben scripts para Gradle y por los pocos desarrolladores de Grails que existen, sin alcanzar el prestigio académico de Scala ni la fama respaldada por JetBrains de Kotlin.

El tipado relajado de Groovy

El tipado relajado de Groovy es un arma de doble filo.

Durante la escritura de la versión en Groovy, tuve un problema con CORS. Mi primer sospechoso fue una mala configuración en el application.yml (ya que leía los orígenes permitidos desde ese archivo), pero la solución fue esta:

Screenshot2025-02-11190416.png

Tenía as String, probablemente por una sugerencia de IntelliJ o un copy-paste de ChatGPT, pero eso fue suficiente para romper CORS en la aplicación. Este tipo de errores simplemente no ocurren en Java.

Escribiendo tests con Spock

Podés usar JUnit en un proyecto basado en Groovy, pero sería un desperdicio no utilizar Spock (es como ir a Madrid y no comerse una tortilla).

Siempre me pareció que la sintaxis de Spock era más legible, por preferencia personal. Acá tenés un fragmento de código en Java JUnit y en Groovy Spock, ambos probando la funcionalidad de buscar un Pokémon por ID.

Java JUnit:

@Test
void whenFindByIdThenReturnPokemon() {
    when(pokemonRepository.findById(anyLong())).thenReturn(Optional.of(mock(Pokemon.class)));
    assertNotNull(pokemonService.findById(1L));
}

Groovy Spock:

def "when findById then return Pokemon"(){
    given: "a mocked repository behaviour"
    pokemonRepository.findById(_ as Long) >> Optional.of(new Pokemon())

    when: "finding a pokemon"
    def result = pokemonService.findById(1L)

    then: "result is not null"
    result != null
}

Volvería a usar Groovy si tuviera la chance

No es porque sea objetivamente superior, sino porque mantener código debería sentirse como volver a casa. Aunque en esa casa haya un chequeo de tipos flojo y fantasmas misteriosos de NoSuchMethodError en el clóset. Supongo que extraño formar parte de un proyecto que realmente me importe, y Groovy me recuerda esos días.

La decepción: Kotlin

Descargo de responsabilidad: Esta fue la primera vez que emprendí un proyecto en Kotlin por mi cuenta, así que quizás mi mala experiencia se deba a cuestiones de habilidad.

skill-issue-skill-3427506110.gif

OpenAPI Generator no funcionó de inmediato

Soy un gran fan de OpenAPI Generator, y no quiero volver a escribir un DTO nunca más. Usar el OpenAPI Generator Gradle Plugin era básico en Java y Groovy, pero en Kotlin tuve dos problemas:

Esos pasos adicionales se sintieron como un retroceso en términos de eficiencia. Podés decir “Bro, simplemente escribí los DTOs vos mismo”, a lo que yo le respondo “No tuve que hacerlo en Java y Groovy, ¿por qué tengo que escribirlos acá?”

Manejando Java Time en tests

También podés usar JUnit en un proyecto basado en Kotlin, pero sería un desperdicio no probar MockK . Es bastante cercano a la sintaxis de JUnit.

El problema surgió cuando no pude simular java.time.Instant y java.time.format.DateTimeFormatter.

Si tenés curiosidad de cómo es MockK, acá te dejo un test sobre la funcionalidad de buscar un Pokémon por ID:

@Test
fun `when findById then return Pokemon`() {
    val pokemon = Pokemon(name = "Bulbasaur", spriteUrl = "url")
    every { pokemonRepository.findById(any<Long>()) } returns Optional.of(pokemon)
    
    assertNotNull(pokemonService.findById(1L))
}

No estuvo mal

Pero fueron los pequeños detalles los que no me convencieron. Quizás mis expectativas sobre Kotlin eran un poco demasiado altas, o tal vez simplemente tomé algunos desvíos equivocados en el camino. A pesar de estas frustraciones, no estoy cerrando la puerta a Kotlin por completo.

Conclusión

Hey, check me out!

You can find me here