Bueno, bueno… está bien… no nos pasemos… Yeoman y Cookiecutter no han muerto, solo es que quería un título con gancho. 😁

Lo que quiero decir es que de repente les ha salido un nuevo competidor que tiene muchas ventajas, y va a darles mucha guerra…

Si estás leyendo esto, supongo que ya conocerás estas herramientas, pero por si acaso:

¿Qué son Yeoman y Cookiecutter?

Cookiecutter es un generador. De hecho, es, hasta hoy, el principal referente en Python, y uno de los principales en cualquier lenguaje.

Yeoman es otra aplicación similar escrita en NodeJS, y que, esta vez sí, es el principal generador que tenemos disponible, a nivel mundial… hasta hoy, al menos.

Evidentemente no son los únicos generadores existentes, pero ya sabes cómo funcionan las cosas… Podemos hacer como si lo fueran. 😉

¿Y qué es un generador?

Básicamente, son programas que, basándose en una plantilla (también conocidas como “scaffoldings”), hacen una serie de preguntas al usuario y generan un “subproyecto”.

¿Y por qué no me servían Yeoman ni Cookiecutter?

Ambos cumplen bien su trabajo, pero tienen estos problemas fundamentales:

El problema del día 2

Ambos se centran en un caso de uso: crear un subproyecto desde cero.

Esto está muy bien, pero el software raramente es un producto terminado. Normalmente son proyectos que van evolucionando. Y si tu plantilla va evolucionando, ¿cómo aplicas las actualizaciones a los subproyectos que generaste con ella?

Yeoman tiene un soporte no muy bueno para esto, y Cookiecutter ninguno.

Para mí, poder evolucionar el proyecto es imprescindible.

Un problema especialmente difícil es el borrado de archivos. La mayoría de generadores están enfocados en la creación de archivos, pero ¿y si de repente pasas a almacenar tu configuración de isort en un archivo pyproject.toml en lugar del .isort.cfg? ¿Por qué no debería borrarse el .isort.cfg en la próxima actualización?

El problema de las múltiples plantillas

Nuevamente, las herramientas existentes suponen que vas a generar un subproyecto enfocado a una determinada cosa. Pero ¿y si tú añades tus propias cosas a tus subproyectos?

Un ejemplo: supongamos que tienes unas plantillas maestras para configurar tu CI/CD. Son plantillas privadas, muy emparejadas con tu infraestructura particular. Si usas un generador cualquiera para empezar un nuevo proyecto, ¿por qué no ibas a poder aplicarlas también?

Ningún otro generador soporta esto hasta donde sé.

¿Y por qué no usar solo un repositorio Git?

En 2017 tuve que empezar a mantener una plantilla bastante compleja, y debido a los problemas que tenían los generadores en aquel momento (los mismos que siguen teniendo hoy), opté por la plantilla tonta Git.

Aunque esto hizo las actualizaciones relativamente sencillas, supuso todos estos problemas:

  1. Problemas con el historial. Cada vez que se actualiza un subproyecto, descarga y fusiona todo el nuevo historial git del scaffolding.

  2. Problemas para documentar. Cualquier documentación (README o lo que sea) se clonaría a todos los subproyectos, así que tienes que mantenerla en algún lugar separado.

  3. Problemas para testear. Los tests se clonarían en cada subproyecto, pero ahí los tests ya no tendrían sentido.

  4. Problemas para actualizar. Por ejemplo, supongamos que tu proyecto usa Postgres, y cuando lo creaste, Postgres 10 era la versión más moderna. Ahora resulta que está la versión 12.

    Si lo cambias, significará que los subproyectos que se actualicen cambiarán la versión de Postgres. Y esto tal vez no tenga sentido porque podría hacer que los datos anteriores ya no sean compatibles.

    Así que, aunque soportes versiones más modernas, las que se clonan de la plantilla son anticuadas y no puedes actualizarlas con seguridad nunca.

  5. Problemas para introducir cambios no retrocompatibles. Lo normal para este tipo de cambios sería confiar en un simple versionado SemVer que todo el mundo entienda, pero con lo explicado en el punto anterior, simplemente no podemos versionar. Además, si pudieras, esas versiones acabarían integrándose en los subproyectos.

  6. Problemas para introducir nuevas características. Mientras no fueran 100% retrocompatibles, significarían rupturas casi garantizadas al actualizar los subproyectos.

  7. Problemas para tener una plantilla privada. Si quieres publicar una plantilla pero aún mantener algunas configuraciones privadas y combinar ambas en el mismo subproyecto, tienes que fusionar 2 plantillas en una. Esto agrava todos los problemas arriba mencionados.

Demasiados problemas. Años después, ya algunos se han vuelto bloqueantes.

Llegó la hora de dar el salto a un generador.

Por qué no bastaba con mejorar los generadores existentes

Sí, hubiera parecido más sencillo añadir esa característica a uno de esos proyectos tan conocidos, pero es que ambos también tienen problemas sustanciales de diseño:

Los problemas con Yeoman

Realmente Yeoman no es un generador, sino un framework para generadores.

Con Yeoman, realmente no fabricas un scaffolding, sino todo un generador asociado a un scaffolding.

Al final, acabas produciendo una aplicación NodeJS que debes publicar en NPM para que pueda ser usada.

Claro, puede que para un desarrollador de ECMAScript esto sea maravilloso, pero para el resto de mortales se convierte en una complicada curva de aprendizaje que no nos interesa para nada, cuando lo único que queremos es una simple plantilla para un proyecto que probablemente ni siquiera tiene nada que ver con ECMAScript.

Además de todo, ¡implica mantener a mano ficheros JSON!, lo cual se debería considerar un delito.

Y, por supuesto, si quieres programar alguna lógica en la plantilla, implica usar ECMAScript para ello, algo que no me gusta particularmente.

Los problemas con Cookiecutter

En el caso de Cookiecutter, no tenemos que publicar nada a PyPI, pero:

  1. El proyecto carece de financiación.

  2. Desde 2016 se empezó a dialogar sobre soportar actualizaciones, y aún no han hecho nada.

  3. Lo mismo pasa con el problema de tener que escribir JSON a mano (aunque desde 2014 esta vez).

Han surgido otros proyectos que usan Cookiecutter como biblioteca para añadir por encima el soporte para actualizaciones. No obstante, esto reducía la comunidad alrededor del proyecto, y no tenía sentido si se estaba ya hablando de añadir ese soporte en Cookiecutter directamente.

De modo que 3 años después la situación seguía idéntica, así que era hora de ponerse manos a la obra…

Copier entra en escena

Por otro lado, Copier ya existía (yo solo he ayudado a darle nueva vida para su versión 3), pero:

  1. Tenía una base de código más moderna,
  2. un acercamiento más natural a la gestión de plantillas,
  3. sin necesidad de publicar paquetes de software,
  4. y una sintaxis más agradable en YAML.

Bueno, si había que invertir horas (muchas), mejor en algo bien diseñado.

Ventajas de Copier 3

Copier te permite empezar a trabajar de forma supersencilla, con una bajísima curva de aprendizaje:

  • No estás obligado a usar lógica en tus plantillas.
  • No estás obligado a configurar la plantilla.
  • No estás obligado a usar git.
  • No estás obligado a soportar actualizaciones de las plantillas.
  • No estás obligado a programar.
  • No necesitas publicar tu plantilla en ningún registro de paquetes.

Pero, si necesitas cualquiera de esas características:

  • Tienes disponible un motor de plantillas sencillo de aprender para cualquiera, independientemente de qué lenguaje vengas: Jinja 2.

    Si te interesa, aquí está la documentación para escribir plantillas.

  • La plantilla es configurable con un sencillo (o complejo, según necesites) archivo copier.yml.

  • Si publicas tu plantilla en un repositorio Git, tan solo tienes que indicarle a Copier que la descargue desde ahí.

  • Si configuras la plantilla para soportar actualizaciones:

    • Copier usará las etiquetas Git para actualizar tu subproyecto en base a su plantilla original.
    • Intentará respetar el histórico que se haya generado desde la última actualización.
    • Ejecutará las migraciones que haya programado el diseñador de la plantilla (si las hay).
  • Puedes programar tus tareas post-copia y pre/post-migración:

    • Directamente como comandos en tu copier.yml.
    • O puedes usar un script en la plantilla fuente.
    • O uno en el subproyecto de destino.
    • En el lenguaje que quieras. Cualquier ejecutable sirve.

Evidentemente no es perfecto, pero hemos hecho un gran trabajo con él, y ha nacido expresamente para soportar actualizaciones, así que nada de parches sucios o bibliotecas externas para conseguir eso.

Resumen

Si quieres un gestor de plantillas sencillo pero potente, que te permita evolucionar sin tener que tomar decisiones inmodificables, y que no te obligue a escribir JSON, dale a Copier una oportunidad. ¡No te arrepentirás!