Entornos, versiones y ciclos de lanzamiento

Informe de metodología adaptado al monorepo UnoSportClub. Documento fuente: Entornos, versiones y ciclos de lanzamiento del software (rev 1.0.1, 04.11.2022), Neftalí Yagua, Ingeniero de Software.

Descargar PDF v1.0.1 · Copia en repositorio: docs/developer/resources/entornos-versiones-ciclos-lanzamiento-v1.0.1.pdf

La implementación técnica del pipeline (jobs, GHCR, comandos) se documenta en CI/CD y DevOps sin repetirse aquí.

Producción

El proceso de Producción del Software es un proceso dinámico, que debe entenderse como un movimiento o flujo de información continua; el embotellamiento en las diferentes etapas de despacho o despliegue produce retrasos importantes. Para evitar esto se han desarrollado a lo largo de los años estrategias de ingeniería que permiten agilizar y reaccionar rápidamente ante los problemas que surgen en el proceso, ya que de no poder observarlos estos se acumulan y obstruyen el proceso de producción.

El proceso de producción de Software en algunas organizaciones es más o menos complejo, dependiendo de los requerimientos que van surgiendo y la madurez del equipo. En el presente documento se explica un esquema básico y simple, que no es una norma general pero ayuda a estructurar los mecanismos de producción y agilizar los procesos.

La gráfica siguiente muestra el flujo de artefactos dentro del proceso de producción, de izquierda a derecha desde el momento en el que se escriben las líneas que resuelven parte del problema.

Flujo de artefactos del proceso de producción
Figure 1. Flujo de artefactos (informe v1.0.1, pág. 2–3)
Desarrollo Pruebas Ensayo Producción

Programación y propagación

Pruebas funcionales y unitarias

Control de calidad registra incidencias

Etiqueta y adición al ciclo de lanzamiento

Pruebas iniciales; cierre de incidencias

Empaquetado y despacho de aplicación

Comprobación de cumplimiento

Levantar información; registrar incidencias

Hay que tener claro que antes de todo lo descrito hay una serie de pasos previos que no son parte del proceso de planificación en sentido operativo, sino donde se describe la ingeniería y la arquitectura inicial, así como las tecnologías que se usarán en el proceso.

Para ello la gerencia de proyectos debería entregar a la dirección de desarrollo todos los detalles para que sean separados los problemas en pequeños fragmentos del problema general, siguiendo el principio Rubik que indica que «un problema complejo se resuelve más fácil fragmentando en problemas cada vez más pequeños y por consiguiente más simples». De manera que cada incidencia o tarea conlleva a una sola responsabilidad y a la vez es sencilla de explicar.

Cargar estas incidencias y la fragmentación del proyecto no debe ser responsabilidad del desarrollador; su responsabilidad es darle seguimiento, solucionarlo hasta cerrar cada incidencia. Una vez que el desarrollador ha cerrado una incidencia debe sumar los cambios a la rama principal (master, main, trunk) o la rama principal de desarrollo (dev-master, develop).

Entornos y actores en el flujo de implementación
Figure 2. Entornos y actores en el flujo (informe v1.0.1, pág. 6–7)
Actor Ubicación en el flujo Responsabilidad

Desarrolladores

Inicio → Desarrollo

Escriben código, pruebas iniciales, cierran incidencias

Dirección de desarrollo

Desarrollo → Pruebas

Canalización de pruebas, estándares, revisión técnica

Gerencia de proyectos

Pruebas → Ensayo

Cumplimiento de requerimientos; asigna versión a cada incidencia

Cliente

Ensayo → Producción

Aceptación cuando el producto ya superó pruebas exhaustivas

Usuarios finales

Tras Producción

Reciben la versión; deben conocer si es beta o estable

Versionado semántico 2.0

El versionado es parte esencial en el desarrollo de software; en realidad no es posible llevar un control de versión semántico como si cada lanzamiento tuviera el mismo peso. En su lugar, tratamos de usar el número de la versión como un indicador de la cantidad de dolor que causará la actualización.

El formato de número de versión utilizado es <mayor>.<menor>.<parche>(-calificador). Véase: semver.org.

Mejora la empatía

Es importante recordar que las actualizaciones no son una tarea agradable para la mayoría de los usuarios. Como equipo, siempre debemos brindar la mayor ayuda posible y tener empatía por los usuarios.

Siempre debemos considerar cuidadosamente las razones detrás de cualquier cambio que hagamos. A veces es mejor para nosotros asumir el dolor que de otro modo infligiríamos a los usuarios. Por ejemplo, a menudo elegimos admitir versiones anteriores de Java aunque nos gustaría usar funciones de lenguaje más nuevas nosotros mismos.

Lanzamientos de parches

Los lanzamientos de parches deben ser reemplazos directos compatibles con API para los usuarios. Solo deben contener correcciones de errores.

Versiones menores

Las versiones menores contienen nuevas características. En general, debería ser posible actualizar desde una versión secundaria anterior sin demasiado esfuerzo, siempre que la versión principal no tenga cambios y no se utilicen métodos obsoletos «Deprecated».

Versiones mayores (principales)

Las versiones principales están reservadas para grandes cambios de última hora. Se espera que los usuarios tengan que trabajar un poco al actualizar las versiones principales. Esto es porque la versión mayor se utiliza para hacer cambios que no son compatibles con el API.

Calificadores

Los calificadores son parámetros opcionales que definen la versión actual. Algunos ejemplos:

  • RELEASE

  • SNAPSHOT

  • tiger

  • alpha

  • BUILD20220719064923

Releases

Representa una versión estable, única e inmutable; esto quiere decir que este artefacto no va a cambiar.

Snapshots

Se refiere a una versión del proyecto que está en tiempo de desarrollo: una especie de WORK-IN-PROGRESS o «Under Development». Parecido al concepto de Build en contraposición al de Release. En Maven la convención es agregar el sufijo -SNAPSHOT. Todo lo que antecede a este prefijo realmente no le importa a Maven; puede tener letras, números, lo que sea (aunque también nos viene bien tener una convención). Ejemplos:

  • 1.0-SNAPSHOT: se lee como «la que va a ser la versión 1.0»

  • 1.0.1-SNAPSHOT: futura versión 1.0.1

  • 1.0-alpha01-SNAPSHOT: lo que va a ser la versión 1.0-alpha01, etc.

Se trata de una captura instantánea: a veces necesitamos crear una versión instantánea de las fuentes actuales considerando la fotografía instantánea para tener una idea. Una consecuencia de esto es que las versiones snapshot son mutables, es decir que van evolucionando y cambiando. Si bajamos un snapshot hoy, quizás no sea igual al de ayer o la semana pasada. Es ideal durante las etapas de desarrollo, pero no se utiliza para lanzamientos.

Builds

Este es un calificador mucho más sencillo de comprender: se trata de una marca de compilación que contiene el timestamp del momento de empacarse. Resulta muy útil para lanzamientos beta o inestables, ya que continuamente hay que reemplazarlos con su misma versión corregida. Por ejemplo 2.0.3-beta-build20220719064923 y 2.0.3-beta-build20220718083314: claramente se puede distinguir que, aunque se trata de la misma versión, ambos son paquetes diferentes.

Política de depreciación

Las clases y los métodos en desuso deben anotarse @Deprecated de manera que sea claro que en las próximas versiones ya no estará disponible.

Nuevas funciones frente a errores

Las nuevas funciones deben apuntar a la próxima versión menor y no deben incluirse en las versiones de parches. Los errores deben dirigirse a la versión compatible más antigua, a menos que sean particularmente riesgosos o difíciles de corregir en una rama más antigua. En algunos casos se puede usar la marca <mayor>-<menor> para dar seguimiento global a una versión, por ejemplo v2.0, v2.3.

Confirmar mensajes

Los mensajes de confirmación son una herramienta vital cuando se trata de depurar una regresión. Es natural ser descuidado al crear mensajes de confirmación; sin embargo, cuando se trabaja en equipo y siempre que sea posible conviene usar mensajes descriptivos. Lo recomendable es seguir estas 7 reglas:

  1. Separe el asunto del cuerpo con una línea en blanco

  2. Límite la línea de asunto a 72 caracteres (use más corta si es posible)

  3. Ponga en mayúscula la línea de asunto

  4. No termine la línea de asunto con un punto

  5. Use el estado de ánimo imperativo en la línea de asunto

  6. Envuelva el cuerpo en 72 caracteres

  7. Use el cuerpo para explicar qué y por qué versus cómo

Además, casi todos los mensajes de confirmación incluyen una referencia a un problema en el Seguimiento de incidencias o una solicitud de extracción (pull request). Referenciamos problemas usando «See», «Fixes» o «Closes» seguido de #NNNN. Por ejemplo:

Permitir resultados opcionales de ConfigDataLocationResolver

Actualice `ConfigData` para que señale si se considera opcional. Esta actualización
permite que `ConfigDataLocationResolvers` devuelva resultados que se comporten de la
misma manera que ubicaciones prefijadas con `opcional:` sin que el usuario necesite
prefijar la cadena de ubicación. Cierra #322

Vale la pena leer:

En el monorepo UnoSportClub se complementa con la convención type(scope): subject (ej. feat(accounting): close period with equity check).

Entornos de implementación

Queda sostenido que la planificación, el versionado y la comunicación continua permiten un manejo de soluciones más eficiente; por lo tanto el desarrollo se pondrá a disposición del equipo de Ensayo (Staging) de forma instantánea mediante herramientas de integración continua y distribución continua (CI/CD). El detalle técnico del pipeline en este proyecto está en CI/CD y DevOps.

Desarrollo (development)

Etapa durante la cual se desarrolla la aplicación. El entorno de desarrollo suele estar al alcance directo del desarrollador, posiblemente en su propio equipo o en un servidor en su oficina, ya que el desarrollador debe haber realizado las pruebas de primera mano antes de realizar un despliegue a testing. En algunos casos el desarrollador requiere certificados SSL o conexiones VPN para lograr simular un entorno de producción; en esta etapa podría trabajar con entornos de prueba y datos de relleno, pero estos deben servirse cerca y no desde los servidores de despliegue, o se comprometen los tiempos de desarrollo por asuntos de latencia, velocidad o problemas de disponibilidad de los datos. Lo natural es entregar especificaciones claras de las interfaces de datos que maneja el entorno de ensayo y producción, de manera que pueda crearse una caja de arena (Sandbox) mientras se desarrolla el código fuente.

Desarrolladores

Lo natural es que los desarrolladores no deberían preocuparse por asuntos de infraestructura; en su lugar debería haber un canal donde pudieran solicitar la infraestructura que requieran. Establecer canales de comunicación entre los desarrolladores es un elemento clave, en especial durante esta etapa, y estos canales no deben ser las herramientas tradicionales sino herramientas especializadas para el manejo de equipos.

Pruebas (testing)

La etapa de pruebas funcionales y unitarias no es un proceso de prueba física de la aplicación, sino de pruebas técnicas al código, que permita examinar si los parámetros de calidad en relación a cada función que debe hacer el Software se puedan comprobar de forma automática. Esto es porque en el proceso de desarrollo de Software los desarrolladores continuamente están compartiendo cambios y fusionando ramas, lo que requiere en primer lugar que cada solicitud de integración sea revisada exhaustivamente antes de unirla a la rama principal (o de desarrollo; en algunos casos se usan dos ramas principales). Este es un proceso prácticamente automático; por lo tanto no debe requerir ningún tipo de intervención, salvo los cambios escritos por el desarrollador al crear las pruebas funcionales y unitarias.

Es común confundir estas pruebas con pruebas integrales al software ejecutadas por un operador; ese tipo de pruebas corresponden a la etapa de ensayo, ya que las pruebas funcionales corresponden a la edición del código fuente y ejecución en el servidor.

El equipo de desarrollo —quien envía la fuente— y el resto del equipo son los encargados de monitorear esta etapa; no es responsabilidad solamente del desarrollador. Enviar código fuente para ser examinado en el servidor de testing debería ser un paso sencillo. Cabe destacar que es totalmente válido configurar el servidor para hacer el despliegue a la siguiente etapa (staging) o fusionar el código fuente.

Dirección de desarrollo

La dirección de desarrollo debería proveer a los desarrolladores la configuración de canalización de pruebas funcionales o, en su defecto, proveer un servidor para testing o, como mínimo, indicar las pautas que conciernen a esta etapa. Lo ideal es tener un servidor dedicado a las pruebas; sin embargo, los servicios de control de versiones suelen implementar herramientas para este tipo de pruebas (GitHub Actions, Bitbucket Pipelines, TeamCity, etc.).

Una configuración de pruebas adecuada podría desplegar automáticamente el Software al entorno de ensayo —que no es el entorno de producción ni el de desarrollo; es importante observar siempre esta diferenciación— para posteriormente ser probado por el equipo de calidad.

Es importante tener en cuenta que el equipo de Calidad es quien debe estar pendiente de recibir los despliegues que son despachados del entorno de pruebas a ensayo, ya que los desarrolladores no deberían tener que ocuparse de esto. Deben mantenerse enfocados en su entorno de desarrollo desplegando nuevos cambios documentados en el servicio de seguimiento de incidencias. No observar este hecho podría producir retrasos considerables en las etapas de desarrollo, ya que sobre el pipeline pesa la garantía de que todos los paquetes que llegan al entorno de ensayo han pasado la comprobación de pruebas funcionales y unitarias configuradas en el código fuente.

Ensayo (staging)

En el entorno de ensayo, el equipo de calidad puede realizar una revisión de las funcionalidades que están pendientes y que no están registradas en el control de incidencias, y añadir una a una al control de incidencias, que es la herramienta adecuada para garantizar que los objetivos se cumplan. Cabe destacar que el flujo de la aplicación a través de los entornos no incrementa la versión en ninguna manera excepto cuando la aplicación pasa de la etapa de ensayo a producción.

Gerencia de proyectos

Dentro de la gerencia de proyectos se examina con detenimiento que se cumplan todos y cada uno de los requerimientos del cliente, y son quienes inician las incidencias de tareas y especifican el ciclo o versión a la que pertenecen, de manera que los desarrolladores solo deben seguir la dirección establecida.

El cliente

Después de que el equipo de producción está satisfecho con el cumplimiento de los requerimientos es cuando procede a discutir con el cliente y hacer presentaciones. En este punto el Software ya debe haber superado todas las pruebas exhaustivas, de manera que el cliente tiene en la mano la garantía de un Software funcional y probado.

Producción (production)

Es el equipo de Calidad quien determina cuándo una versión está terminada y prosigue a iniciar el ciclo de lanzamiento de la versión actual o a continuarlo en caso de que ya esté iniciado previamente. Entendemos que una misma versión tiene varios lanzamientos, y no se crea la etiqueta tag hasta que la versión está terminada. Los tags no se modifican: determinan un estado constante del código fuente que los compone. En este sentido, los tags son para versiones cerradas de modificaciones y las ramas para versiones abiertas a modificaciones, conforme al principio Open/Closed que forma parte de los principios SOLID de Robert C. Martin.

Usuarios finales

Es importante establecer canales de comunicación con los usuarios finales y especificar con claridad el ciclo de lanzamiento del Software, de manera que si una versión es beta el usuario sabrá que se trata de una versión inestable donde se esperan problemas.

Seguimiento de incidencias

Es el equipo de calidad el encargado de garantizar que las pruebas operativas —no confundir con unitarias y funcionales, que esas sí las hacen los desarrolladores en el código— sean revisadas arduamente, y que una vez que se presentan al cliente o usuario final se considere que no hay errores conocidos sin registrar. El equipo de calidad debería crear un ticket de incidencia con cada falla que encuentre, de manera que en el desarrollo se pueda llevar un seguimiento y corrección.

Todos los errores encontrados en una versión cerrada se añaden en la cola para la siguiente versión de parches. Todas las modificaciones que incrementan las características del Software pero que continúan siendo compatibles con el API se añaden a la próxima versión menor. Todas las modificaciones o cambios que no son compatibles se añaden a la cola de incidencias de la siguiente versión mayor.

En UnoSportClub la herramienta de seguimiento es GitHub Issues (y pull requests vinculados). Los milestones o etiquetas de versión agrupan las colas de parches, menores y mayores de forma análoga a la planificación por versiones del informe original.

Los tests son el contrato del equipo (TDD): si la integración continua falla, el artefacto no debe avanzar de etapa. No modificar tests y código de producción en el mismo commit para «cuadrar» resultados.

Ciclo de vida del lanzamiento de software

Para un correcto desarrollo del Software es necesario respetar cabalmente los ciclos de lanzamiento, que es tan importante como los entornos de implementación observados anteriormente. Hay un espacio durante el desarrollo donde ni el cliente final ni el usuario final participan, porque en las etapas de depuración del código es absolutamente normal encontrarse con errores, y esto no debería conocerlo el cliente hasta ser corregidos.

Alfa. Etapa inicial

La etapa inicial de la versión debe tener todas las características del requerimiento del cliente; incluye todo lo que se necesita para funcionar, es lo que se espera. Sin embargo todavía debe someterse a una serie de pruebas exhaustivas que determinan cuáles cambios deben hacerse para alcanzar con exactitud el requerimiento del cliente, y en especial para resolver los problemas planteados en la primera exploración de las necesidades del proyecto.

Beta. Etapa de pruebas

Esta etapa de la versión procura hacer una exploración profunda de la aplicación para conocer posibles fallas, repararlas y corregir alguna que su uso pueda ocasionar en los datos. Es una aplicación que bien se puede usar como definitiva; será liberada de responsabilidad ya que encontrar errores en ella es lo que se espera. Etapa imprescindible para alcanzar el máximo nivel de calidad.

RC. Etapa candidata a definitiva (Release Candidate)

Esta etapa de la versión es prácticamente el producto terminado, listo para usarse; sin embargo se mantiene bajo estricta observación. Debe estar funcionando correctamente en todo sentido, atentos a recuperar todos los datos necesarios para mejorar la aplicación.

Final. Etapa definitiva

Esta etapa de la versión se alcanza cuando se entiende por terminada la aplicación, lista para usar y puesta en marcha definitiva al público en producción.

Estable

Una vez puesta en marcha la etapa anterior, y una vez que comienza a usarse sin que su implementación falle, se considerará una versión estable. Servirá para definir las características que se añadirán en la etapa alfa de la siguiente versión.

Ciclo de vida Alfa Beta RC Final y Estable
Figure 3. Ciclo de vida del lanzamiento (informe v1.0.1, pág. 17–18)

Conclusión

Los principios, normas y técnicas descritas en el presente documento no son una ley absoluta acerca de cómo se deben organizar los entornos, versiones y ciclos de lanzamientos. Históricamente se ha comprobado que son parte de las buenas prácticas que nos ayudan a organizar el desarrollo y el versionado, pero muy en especial el mantenimiento rápido de cambios.

Este documento es una primera versión que puede mejorarse aún más. En la medida en que los equipos de trabajo tienen principios claros sobre cómo se marcan los flujos de trabajo, es más sencillo hacer el mantenimiento de desarrollo de Software. Actualmente hay tecnologías mucho más avanzadas para esto, pero antes de ir allí hay que empujar al equipo a dominar principios como estos.

Aplicación en UnoSportClub v0.4.1

Esta sección no sustituye el informe metodológico anterior; solo traduce sus conceptos al monorepo actual sin acortar el significado de cada entorno.

Entorno (informe) Implementación v0.4.1 Disparador en .github/workflows/ci.yml

Desarrollo (development)

npm run dev:serve; sandbox local; datos de prueba

Trabajo local del desarrollador

Pruebas (testing)

Job test (Node 24, npm run test); environment GitHub testing

Push o PR hacia develop

Ensayo (staging)

Imagen ghcr.io/…​/unosportclub:staging; dominios QA

Push a rama de línea N.M (ej. 0.4) tras test y build

Producción (production)

Tag vX.Y.Z; imágenes :vX.Y.Z, :latest y actualización de :staging; environment production

Push de tag v*.. tras test y build

Ramas y artefactos:

  • SNAPSHOT conceptual: rama develop, integración continua sin publicar imagen de ensayo.

  • Línea abierta: rama N.M (ej. 0.4); acepta cambios hasta el cierre de versión.

  • RELEASE: tag Git vX.Y.Z inmutable; no se reescribe.

  • BUILD: artefacto CI dist-and-docs (retención 1 día); complementa pero no sustituye semver.

El job test-functional (Jest + Postgres) está previsto pero deshabilitado en v0.4: #391.

Procedimiento operativo UnoSportClub

Checklist que ejecuta el procedimiento anterior en este repositorio. Detalle ampliado en Publicación de releases.

  1. Incidencias priorizadas; integración continua en verde en develop

  2. Merge a rama de línea N.M → imagen :staging para QA

  3. Tras aprobación de calidad: commit chore(release): vX.Y.Z, actualizar package.json y RELEASE_NOTES.md en la rama de línea

  4. gh release create vX.Y.Z — el tag dispara los jobs build y release en GHCR

  5. Sincronizar develop con la rama de línea

  6. Documentación: entrada en docs/RELEASE_NOTES.md y npm run build:local