Pollito Blog
February 12, 2025

Construí la misma aplicación tres veces

Posted on February 12, 2025  •  8 minutes  • 1646 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 propia versión, pero con dos lenguajes que ya conozco bien (Java y Groovy ) más un lenguaje nuevo que quería probar desde hace mucho tiempo: Kotlin .

Aquí está el código para los repositorios:

Entendiendo la aplicación

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

Le agregué un giro:

Aquí está el código del frontend de Next.js .

Métricas rápidas

Este blog es principalmente una comparación de backends. Alerta de spoiler: en términos de rendimiento en este pequeño proyecto de muestra, son iguales.

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

Las siguientes tablas se generaron utilizando 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 reposo tienen un uso aceptable de CPU y memoria. Todos presentan:

No existe un Lo Bueno, Lo Malo y lo Feo

Las tres opciones son totalmente válidas para un gran proyecto serio y se englobarían en “Lo Bueno”.

Yo diría que una frase más acertada sería “Lo Bueno, el Primer Amor y la Decepción”. Vayamos una por una.

Lo Bueno: Java

Comencemos insertando aquí la broma public static void String main args.

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

En una palabra, Java es confiable:

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

Java no es glamuroso, pero sí cómodo.

honest-work-meme-c7034f8bd7b11467e1bfbe14b87a5f6a14a5274b.jpg

El Primer Amor: Groovy

Mi viaje con Groovy comenzó en 2021. Recuerdo que en la entrevista de trabajo solo me preguntaron dos cosas:

Era una época más sencilla.

Sin darme cuenta, era parte de un proyecto Grails , un marco monolítico bien raro que usa Groovy como su lenguaje principal.

Rápidamente, me enamoré de su sintaxis expresiva y su forma de mejorar Java reduciendo el código repetitivo y adoptando un estilo más dinámico.

Sin embargo, Groovy sigue siendo el artista independiente de los lenguajes JVM: amado por los escritores de scripts de Gradle y los pocos desarrolladores de Grails que capaz existan, pero nunca alcanzó el prestigio académico de Scala ni la fama de Kotlin respaldada por JetBrains.

Groovy relaxed typing

La escritura relajada en Groovy es un arma de doble filo.

Durante la redacción de la versión de Groovy, tuve un problema con CORS. Mi primera sospecha inmediata fue una configuración incorrecta de application.yml (ya que leo los orígenes permitidos de ese archivo), pero la solución fue la siguiente:

Screenshot2025-02-11190416.png

Tenía “as String”, probablemente como una sugerencia de IntelliJ o un copia y pega de ChatGPT, pero eso fue suficiente para romper CORS en la aplicación. Este tipo de errores simplemente no ocurren en Java.

Writing tests with Spock

Puedes usar JUnit en un proyecto basado en Groovy, pero sería un desperdicio no usar Spock (es como ir a Madrid y no comer una tortilla).

Siempre me pareció más legible la sintaxis de Spock, aunque es una preferencia personal. Aquí tienes un fragmento de código en Java Junit y Groovy Spock, ambos probando la función de buscar 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 utilizar Groovy si tuviera la oportunidad

No porque sea objetivamente superior, sino porque mantener el código debería ser como volver a casa, incluso si en casa hay fugas de type checking y misteriosos NoSuchMethodError fantasmas en el armario. Supongo que echo de menos ser parte de un proyecto que realmente me importa, y Groovy me recuerda a esos días.

La Decepción: Kotlin

Disclaimer: Esta fue mi primera vez iniciando un proyecto Kotlin solo, por lo que tal vez mi mala experiencia se deba a skill issue. skill-issue-skill-3427506110.gif

El generador OpenAPI no funcionó de inmediato

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

Estos pasos adicionales se sintieron como un paso atrás en términos de eficiencia. Puedes decir “Bro, simplemente escribe los DTO tú mismo”, a lo que respondo “No tuve que hacer eso en Java y Groovy, ¿por qué tengo que escribirlos aquí?”.

Manejo de java time en las pruebas

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

El problema llegó cuando no pudo simular java.time.Instant y java.time.format.DateTimeFormatter.

Si tienes curiosidad sobre cómo se ve MockK, aquí tienes una prueba de la función de buscar 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. Tal vez mis expectativas sobre Kotlin eran demasiado altas, o tal vez simplemente tomé algunos caminos equivocados en el camino. A pesar de estas frustraciones, no le cierro la puerta a Kotlin por completo.

Conclusión

Hey, check me out!

You can find me here