Refactor a gran escala - Introducción a OpenRewrite

Publicado por Manuel García de Vinuesa Gómez el

Arquitectura de SolucionesJavaOpenRewriteMigracionesRefactoringValidaciones

Introducción

La deuda técnica (Ward Cunningan 1992) viene a decir que el software desarrollado es propenso a acumular deuda, y dicha deuda va generando un esfuerzo adicional (interés) a la hora de implementar nuevas funcionalidades. Buscando la similitud con una deuda financiera, a más deuda, el interés se irá incrementando y costará más implementar nuevas funcionalidades.

Pero la deuda técnica no sólo se genera alrededor del desarrollo implementado por nosotros mismos, normalmente el software que desarrollamos se apoya en otros componentes (librerías, frameworks...) que deben ser actualizados regularmente, mantenidos, migrados, etc...

El principal problema para la gestión de dicha deuda es que resolverla es complicado de "vender" a nivel de producto o de negocio, por lo que, excepto en contadas excepciones, suele ser normal que se acumule más deuda de la cuenta, aumentando el interés y el coste de desarrollar nuevo software.

Luchar contra la deuda técnica es uno de los principales handicaps a los que se enfrenta el desarrollo de software. Este artículo no viene a explicar las diferentes estrategias para abordarla, sino a presentar una herramienta que puede ayudarnos a gestionar dicho problema cuando trabajamos con software a gran escala.

cabecera - Enmilocalfunciona - Introducción a OpenRewrite - 1400x400px.jpg

¿Qué es el desarrollo de software a gran escala?

El desarrollo de software a gran escala se refiere a la creación de soluciones de software complejas adaptadas a las necesidades de las grandes empresas. No consiste simplemente en escribir código o desarrollar una aplicación, sino en orquestar una sinfonía de tecnologías, componentes, personas y procesos.

En este punto la deuda técnica escalará de la misma manera que nuestra organización, por lo que llegaríamos a tener deuda técnica a gran escala.

Es en este punto donde OpenRewrite aparece para intentar mitigar o solventar los problemas derivados de mantener una gran cantidad de software con múltiples equipos involucrados donde mantener a raya esa deuda técnica se convierte en algo fundamental.

OpenRewrite

Según su propia definición:

OpenRewrite is an automated refactoring ecosystem for source code, enabling developers to effectively eliminate technical debt within their repositories.

Es decir, es una herramienta que nos va a permitir gestionar la deuda técnica de nuestro código de manera automática y guiada. Además, es un producto OpenSource bajo la licencia de Apache License 2.0.

OpenRewrite es una herramienta que permite ejecutar "recipes" (reglas, recetas, tareas...) para migraciones comunes de frameworks, correcciones de seguridad y tareas de coherencia estilística. Por ejemplo, puede automatizar la migración a una nueva versión del framework modificando los archivos de compilación, cambiando la API obsoleta y migrando los ajustes de configuración. O arreglar ciertos errores que podamos cometer en el día a día del desarrollo para mantener un estandar en nuestro desarrollo.

Recipes

Existen cientos de recetas implementadas por el equipo de OpenRewrite, al mismo tiempo existen otras implementadas por terceros y podemos crear nuestras propias recetas.

Openrewrite comenzó su andadura para código basado en Java, pero desde hace tiempo da soporte a una gran cantidad de formatos y lenguajes como se puede ver aquí.

Todas ellas están catalogadas en la página del producto y podéis encontrarlas aquí, un ejemplo de diferentes recipes podría ser:

Y como estos ejemplos, muchísimas más.

Lo importante es saber que podríamos agrupar las recetas en dos grandes grupos:

  • Aquellas recetas que podemos aplicar en ciclos de integración continua, es decir que se pueden aplicar de manera recurrente para controlar la deuda técnica.
  • Aquellas que nos van a ayudar en momentos puntuales a la hora de aplicar grandes cambios a nuestras aplicaciones, como pueden ser de migración o actualización de versiones.

Otro aspecto a destacar es que podemos crear nuestras propias recetas, en este post no vamos a entrar en cómo crear nuestras propias recetas, quizás en función del interés podamos hacer un segundo post donde veamos cómo crearlas y distintos ejemplos de las mismas. Si quieres más información sobre este tema puedes verlo aquí.

Moderne

La empresa que está detrás de OpenRewrite es Moderne, creo que, aunque no vamos a hablar de su software es de recibo comentar que han implementado una solución comercial sobre esta herramienta. Además, la extienden dando soporte a más lenguajes y seguramente con nuevas y más potentes recetas.

Simplemente como curiosidad Moderne nos ofrece, entre otras cosas, un portal donde podemos aplicar dichas recetas a gran escala para nuestros repositorios.

65d7cc95de30b9ebc86d48cb_OpenRewrite-Moderne Overview-3.png

Para más información te invito a visitar su página: https://www.moderne.io/

Ejemplo de uso.

El uso de OpenRewrite es muy sencillo, teniendo diferentes maneras de ejecutar las reglas:

  • Mediante la inclusión de plugins, ya sea Maven o Gradle (en este post usaremos Maven para los ejemplos).
  • Mediante línea de comandos.

Si bien es cierto que en ambos casos para reglas más complejas deberemos apoyarnos en un fichero externo de configuración ambos métodos nos permiten integrarnos perfectamente con cualquier ciclo de CI/CD ya sea de manera explícita (modificando el pom.xml de nuestros proyectos por ejemplo) o simplemente ejecutando por línea de comandos la opción deseada.

Ejecución por línea de comandos

Sin necesidad de modificar nada de nuestro proyecto podemos ejecutar cualquiera de las recetas de la siguiente manera:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports

Si tuvieramos una clase donde existieran imports que no se usan:

Captura de pantalla 2024-06-24 095608.png

Al ejecutarse el plugin

[INFO] <<< rewrite:5.34.1:run (default-cli) < process-test-classes @ ecommerce-example <<<
[INFO]
[INFO]
[INFO] --- rewrite:5.34.1:run (default-cli) @ ecommerce-example ---
[INFO] Using active recipe(s) [org.openrewrite.java.RemoveUnusedImports]
[INFO] Using active styles(s) []
[INFO] Validating active recipes...
[INFO] Project [ecommerce-example] Resolving Poms...
[INFO] Project [ecommerce-example] Parsing source files
[INFO] Running recipe(s)...
[WARNING] Changes have been made to src/main/java/com/example/demo/prices/domain/model/Price.java by:
[WARNING]     org.openrewrite.java.RemoveUnusedImports
[WARNING] Please review and commit the results.
[WARNING] Estimate time saved: 5m
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  23.462 s
[INFO] Finished at: 2024-06-24T09:56:58+02:00
[INFO] ------------------------------------------------------------------------

Nos informaría que la clase Price.java ha sido modificada debido a la regla RemoveUnusedImports. Si el diff del fichero:

Captura de pantalla 2024-06-24 100209.png

El plugin ha eliminado el import que no se usa. Este es un ejemplo claro de una receta que podríamos integrar en nuestro ciclo de integración para que continuamente estuviera aplicando estos cambios.

Recetas parametrizadas

Actualmente existen muchísimas recetas que necesitan diferentes parámetros para funcionar. Openrewrite del mismo modo tiene una limitación que no permite dicha parametrización por línea de comandos.

Para ello es necesario crear un fichero rewrite.yml en la raíz desde donde ejecutamos el comando de rewrite para informar de los parámetros. La estructura de dicho yml sirve realmente para "sobrescribir" recetas y poder agruparlas, con la idea de poder parametrizar todas las recetas que se necesiten.

Para ello basta con añadir en el fichero lo siguiente:

type: specs.openrewrite.org/v1beta/recipe (1)
name: com.example.CambioDePaqueteria (2)
recipeList: (3)
  - org.openrewrite.java.ChangePackage: (4)
      oldPackageName: com.example.demo
      newPackageName: com.example.openrewrite
      recursive: true
  1. Tipo, en este caso es un valor fijo.
  2. Nombre de nuestra receta custom o de sobrescritura, a la hora de lanzar rewrite debemos indicar este nombre.
  3. Listado de recetas que englobal la receta informada en el punto 2.
  4. Una receta con parámetros, en este caso la asociada al cambio de nombre en los paquetes. Enlace.

Para ejecutar dicha regla:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.activeRecipes=com.example.CambioDePaqueteria

Al ejecutarlo veremos en la salida lo siguiente por cada fichero:

File has been moved from src/main/java/com/example/demo/prices/infrastructure/controller/model/PriceListResponse.java to src/main/java/com/example/openrewrite/prices/infrastructure/controller/model/PriceListResponse.java by:
[WARNING]     com.example.CambioDePaqueteria
[WARNING]         org.openrewrite.java.ChangePackage: {oldPackageName=com.example.demo, newPackageName=com.example.openrewrite, recursive=true}

Si vemos el diff podemos ver el cambio de paquetería, aunque no lo ha hecho de la manera adecuada para git ya que no ejecuta un git mv, por lo que en el diff podemos ver los ficheros eliminados y los nuevos incluidos.

Ejecución mediante modificación de pom.xml

La otra opción es añadir openrewrite a nuestro fichero de build. Para ello debemos incluir en nuestro pom.xml el plugin de openrewrite:

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.34.1</version>
</plugin>

Y en la configuración del mismo activaremos las recetas que deseamos ejecutar

 ...
 <configuration>
    <activeRecipes>
      <recipe>org.openrewrite.java.OrderImports</recipe>
    </activeRecipes>
  </configuration>
  ...

Para ejecutarlo basta con lanzar el siguiente comando:

mvn rewrite:run

Además tenemos la opción de lanzar openrewrite pero no aplicar los cambios. Lo que se conoce como dryRun.

mvn rewrite:dryRun

Recetas externas a openrewrite

En caso que se quieran incluir recetas que no están incluidas en el plugin (no quiere decir que no sean implementadas por OpenRewrite) basta con incluir la librería donde se encuentran como dependencias del plugin.

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>${openrewrite.version}</version>
  <configuration>
    <exportDatatables>true</exportDatatables>
    <activeRecipes>
       ...
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
       <groupId>org.openrewrite.recipe</groupId>
       <artifactId>rewrite-java-dependencies</artifactId>
       <version>${rewrite-java-dependencies.version}</version>
    </dependency>
  </dependencies>
</plugin>

Generación de tablas de datos

OpenRewrite nos puede ayudar también a generar informes de los repositorios, para ello muchas recetas ofrecen la capacidad de exportar los resultados a tablas. Para ello basta con añadir el siguiente parámetro a la configuración:

<exportDatatables>true</exportDatatables>

El resultado aparece en el target de nuestro repositorio:

Captura de pantalla 2024-06-24 103112.png

Y si lo visualizamos( ha sido formateado tras generarlo) :

Captura de pantalla 2024-06-24 103631.png

Esto junto con la opción de dryRun nos puede permitir generar una herramienta de control sin necesidad de realizar modificaciones en el código.

Caso práctico: Migración de Spring Boot.

Vamos a poner en práctica las capacidades de OpenRewrite intentando migrar una aplicación de Java 8 y Spring Boot 1.5.1 a Java 17 y la última versión de Spring Boot.

Si este requerimiento tuviéramos que hacerlo a mano y para 50-60 aplicaciones, resoplaríamos, por lo que vamos a tratar de evaluar como OpenRewrite nos podría facilitar la vida.

Para ello nos vamos a descargar el siguiente proyecto de ejemplo de Spring Boot. Y nos vamos a situar en el TAG asociado a la versión de Spring Boot 1.5 que funcionaba con Java 8.

git clone https://github.com/mgvinuesa/spring-petclinic-openrewrite.git;
cd spring-petclinic-openrewrite;
git checkout -b migrate_with_open_rewrite tags/1.5.x

Dicho proyecto usa Java 8 y Spring Boot 1.5.4.RELEASE, compila perfectamente:

[INFO] [1m------------------------------------------------------------------------[m
[INFO] [1;32mBUILD SUCCESS[m
[INFO] [1m------------------------------------------------------------------------[m
[INFO] Total time:  01:18 min
[INFO] Finished at: 2024-06-24T10:54:01+02:00
[INFO] [1m------------------------------------------------------------------------

Levantamos el proyecto para validar que todo va correcto:

2024-06-24 11:09:14.070  INFO 17272 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2024-06-24 11:09:14.092  INFO 17272 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2024-06-24 11:09:14.383  INFO 17272 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2024-06-24 11:09:14.396  INFO 17272 --- [           main] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 15.413 seconds (JVM running for 16.422)

Y podemos acceder a su interfaz y navegar:

Captura de pantalla 2024-06-24 111051.png

Receta de migración a Spring Boot 3.3

Si exploramos la información de dicha receta:

https://docs.openrewrite.org/recipes/java/spring/boot3/upgradespringboot_3_3

Podemos observar que es una receta de tipo composite, es decir, engloba muchas más:

Captura de pantalla 2024-06-24 111257.png

Cada una de ellas, sigue el mismo formato, ya que se realiza una migración de forma incremental. De hecho, si seguimos explorando podemos ver como para migrar a Spring Boot 3.0 necesitaríamos ejecutar la migración a Java 17:

Captura de pantalla 2024-06-24 111415.png

Ahora basta con ejecutar la siguiente línea de comandos:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3 

IMPORTANTE: El comando debe lanzarse con la JDK que acepte el proyecto. En este caso la JDK8.

En los logs de ejecución veremos cosas tan interesantes como las siguientes donde se ve la ejecución incremental de la migración.

...
[INFO] Printing Available Datatables to: target\rewrite\datatables\2024-06-24_11-53-37-263
[WARNING] Changes have been made to pom.xml by:
[WARNING]     org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0
[WARNING]         org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7
[WARNING]             org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6
[WARNING]                 org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5
[WARNING]                     org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4
[WARNING]                         org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_3
[WARNING]                             org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_2
[WARNING]                                 org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_1
[WARNING]                                     org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_0
[WARNING]                                         org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=2.0.x}
[WARNING]                                         org.openrewrite.maven.RemoveProperty: {propertyName=thymeleaf.version}
[WARNING]                                         org.openrewrite.java.dependencies.ChangeDependency: {oldGroupId=org.webjars, oldArtifactId=webjars-locator, newGroupId=org.webjars, newArtifactId=webjars-locator-core, newVersion=^0.35}
[WARNING]                                         org.openrewrite.java.spring.boot2.MigrateHibernateConstraintsToJavax
...

Una vez finalizado podemos ver todos los cambios que ha realizado sobre el proyecto:

  1. Cambios en el pom.xml para indicar las versiones de Java y Spring más actuales:

    Captura de pantalla 2024-06-24 120903.png

  2. Nuevas dependencias y eliminación de otras

    Captura de pantalla 2024-06-24 121352.png

  3. Eliminación de plugins incompatibles con las nuevas versiones

    Captura de pantalla 2024-06-24 121439.png

  4. Cambios en la API de Java, de javax a jakarta

    Captura de pantalla 2024-06-24 121517.png

    Captura de pantalla 2024-06-24 121638.png

  5. Cambios en el uso de hibernate validator, usando los de jakarta

    Captura de pantalla 2024-06-24 121604.png

  6. Eliminación de la anotación @Autowired a nivel de constructor, ya que ya no es necesaria.

    Captura de pantalla 2024-06-24 121725.png

  7. Eliminación de nombres en las anotaciones que no son necesarios

    Captura de pantalla 2024-06-24 121813.png

  8. Actualización de Junit4 a Junit5

    Captura de pantalla 2024-06-24 122133.png

    Captura de pantalla 2024-06-24 122154.png

    Captura de pantalla 2024-06-24 122233.png

  9. Actualización de properties

    Captura de pantalla 2024-06-24 135135.png

Lógicamente en este proyecto de spring boot no se han aplicado muchas de las reglas debido a que no contenía mucha deuda técnica o las dependencias eran manejadas

En este punto, ya debemos compilar con JDK17 y levantar el proyecto para ver que todo ha ido correcto.

Problemas encontrados y soluciones

  1. Error en los tests debido al cambio de RFC
VetControllerTests.testShowResourcesVetList:67 Content type expected:<application/json;charset=UTF-8> but was:<application/json>

El código del test es el siguiente y debemos adaptarlo:

@Test
    public void testShowResourcesVetList() throws Exception {
        ResultActions actions = mockMvc.perform(get("/vets.json").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk());
        actions.andExpect(content().contentType("application/json;charset=UTF-8"))
            .andExpect(jsonPath("$.vetList[0].id").value(1));
    }

Esto se debe al cambio de RFCs (RFC 7159 has been obsoleted by RFC 8259) y debemos adaptar el test para que que la validación sea correcta. OpenRewrite ya tiene una receta para este caso particular, pero sólo si se hubiera usando el enumerado correctamente:

Además tiene la siguiente receta que puede ser interesante:

Por lo que vamos a ejecutarla y ver si arreglamos el problema:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.spring.http.ReplaceStringLiteralsWithMediaTypeConstants

En este caso, parece que no hace el cambio, o al menos el esperado, volvemos a abrir un issue para revisarlo.

[INFO] Using active recipe(s) [org.openrewrite.java.spring.http.ReplaceStringLiteralsWithMediaTypeConstants]
[INFO] Using active styles(s) []
[INFO] Validating active recipes...
[INFO] Project [petclinic] Resolving Poms...
[INFO] Project [petclinic] Parsing source files
[INFO] Running recipe(s)...
[INFO] Printing Available Datatables to: target\rewrite\datatables\2024-06-28_11-51-12-821

¿Tenemos más opciones? Sí, existe una receta más sencilla para hacer replace. En este caso ReplaceStringLiteralWithConstant es la receta base de la anterior y podemos configurarla de la siguiente manera:

type: specs.openrewrite.org/v1beta/recipe
name: com.example.ReplaceStringLiteralWithConstantMediaType
displayName: Replace String literal with constant example
recipeList:
  - org.openrewrite.java.ReplaceStringLiteralWithConstant:
      literalValue: application/json;charset=UTF-8
      fullyQualifiedConstantName: org.springframework.http.MediaType.APPLICATION_JSON_VALUE

Para ejecutar el plugin correctamente, dicha clase (MediaType) debe existir a nivel de dependencias por lo que es necesaria añadirla:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>6.1.0</version>
</dependency>

En este caso si que ha sido capaz de hacer el replace:

Captura de pantalla 2024-06-28 122948.png

  1. Error en los test debido a cambio de mensaje de constraint.
[ERROR] [1;31m  ValidatorTests.shouldNotValidateWhenFirstNameEmpty:42 
expected: "may not be empty"
 but was: "must not be empty"

El código del test es el siguiente y debemos adaptarlo:

@Test
    public void shouldNotValidateWhenFirstNameEmpty() {

        LocaleContextHolder.setLocale(Locale.ENGLISH);
        Person person = new Person();
        person.setFirstName("");
        person.setLastName("smith");

        Validator validator = createValidator();
        Set<ConstraintViolation<Person>> constraintViolations = validator.validate(person);

        assertThat(constraintViolations.size()).isEqualTo(1);
        ConstraintViolation<Person> violation = constraintViolations.iterator().next();
        assertThat(violation.getPropertyPath().toString()).isEqualTo("firstName");
        assertThat(violation.getMessage()).isEqualTo("may not be empty");
    }

En este caso ha cambiado un texto a nivel de constraint @NotNull relacionado con el uso de jakarta para las validaciones.

validator.png

De nuevo podemos usar una receta de replace:

  - org.openrewrite.java.migrate.ReplaceStringLiteralValue:
      oldLiteralValue: may not be empty
      newLiteralValue: must not be empty

Y ahora sí, el software compila, levanta y funciona perfectamente:

Captura de pantalla 2024-06-28 125457.png

Problemas previos

Este artículo se escribió durante el mes de Julio y será publicado en Septiembre. Aunque se ha visto la potencia de Openrewrite, no todo funcionó a la primera, a lo largo de la escritura del mismo se detectaron algunos problemas que fueron solventados por el equipo de Openrewrite. A continuación, os los listo con sus respectivos issues en github y cómo fueron solucionados:

  1. Incorrecta incorporación de jakarta.xml.bind-api. Se detectó una incorporación de dicha librería con una versión incorrecta para JDK 17

    Se ha abierto un bug con respecto a esto: https://github.com/openrewrite/rewrite-spring/pull/485

    El problema es que la receta específica org.openrewrite.java.dependencies.AddDependency Esta forzando dicha versión

    org.openrewrite.java.dependencies.AddDependency: {groupId=jakarta.xml.bind, artifactId=jakarta.xml.bind-api, version=2.3.x, onlyIfUsing=javax.xml.bind..*, acceptTransitive=true}
    
  2. Incorrecta versión de wro4j-maven-plugin para JDK17, la versión original no es compatible con JDK17 y da un error.

    Pero parece que en este caso existe la receta pero solo para JDK11

    Captura de pantalla 2024-06-24 125211.png

  3. Se elimina el plugin de cobertura, pero no la property que define su versión.

Esto no implica que nos quedemos bloqueados, ya que podemos añadir nuestras propias recetas parametrizadas para solventarlo. De todas formas hemos abierto este issue.

En este caso tres recetas que podrían ayudarnos a completar la migración desde nuestro lado son:

  1. org.openrewrite.maven.UpgradeDependencyVersion nos permite actualizar dependencias.
  2. org.openrewrite.maven.UpgradePluginVersion, donde podremos forzar la actualización del plugin a 2.1.1
  3. org.openrewrite.maven.RemoveProperty, para eliminar la property de cobertura que no vamos a usar. Esta es opcional, pero mejoraría nuestro código.

Si aplicamos dichas recetas:

    - org.openrewrite.maven.UpgradePluginVersion:
        groupId: ro.isdc.wro4j
        artifactId: wro4j-maven-plugin
        newVersion: 2.1.1
    - org.openrewrite.maven.RemoveProperty:
        propertyName: cobertura.version
    - org.openrewrite.maven.UpgradeDependencyVersion:
        groupId: jakarta.xml.bind
        artifactId: jakarta.xml.bind-api
        newVersion: latest.release

Estilos

Existe otra capacidad de OpenRewrite de aplicar estilos al código que estamos implementando. Esto no quiere decir que vaya a formatear todo el código, lo que quiere decir es que el código que va a introducir OpenRewrite en nuestro código siga un estilo u otro.

En este punto no voy a alargarme ya que no creo que sea algo excesivamente crucial, pero es importante que lo conozcáis por si os interesa aplicar un estilo u otro.

Si queréis más información, podéis encontrarla aquí.

Conclusiones

OpenRewrite es una herramienta muy interesante tanto para ejecutar migraciones desatendidas, automáticas y escalables como para gestionar deuda técnica incremental que puedan ir generando nuestros equipos de trabajo.

Aunque la prueba no ha salido perfecta tenemos que ver en perspectiva lo que se ha conseguido aplicando una serie de recetas encadenadas y la potencia que esta herramienta puede darnos en implementar procesos de migraciones.

También nos puede ayudar a generar reports muy interesantes para buscar información en nuestro código que sirva para auditar el código.

Además, no sólo aplica a proyectos Java, en las recetas podemos encontrar ejemplos para Terraform, Docker, Kubernetes... que nos pueden ayudar a aplicar buenas prácticas sobre dichos ficheros.

En resumen, el coste de añadirlo es mínimo y la ganancia para controlar la deuda técnica enorme, creo que es una herramienta que debe formar parte de muchos proyectos a escala para mantener el código actualizado, basado en buenas prácticas y con un estándar de programación de manera automática.

Bibliografía

Todos los ejemplos iniciales y algunos más podéis encontrarlos en el siguiente repositorio de github: https://github.com/mgvinuesa/ecommerce-example-openrewrite

El proyecto sobre el que se ha ejecutado la migración es: https://github.com/mgvinuesa/spring-petclinic-openrewrite, en la rama: migrate_with_open_rewrite

https://docs.openrewrite.org/
https://martinfowler.com/bliki/TechnicalDebt.html
https://rewisoft.com/blog/large-scale-software-development/
https://techquarter.io/breaking-boundaries-navigating-large-scale-enterprise-software-development/
https://www.scnsoft.com/software-development/large-scale
https://2024.springio.net/sessions/automated-software-refactoring-with-openrewrite-and-generative-ai/
https://www.baeldung.com/java-openrewrite