Ingeniería de software y operación
Ingeniería de software
Escribir código que funciona es la parte fácil. Lo difícil empieza después: control de versiones, pruebas, revisión, despliegue continuo, deuda técnica y operar un sistema vivo con gente usandolo.
Escribir código que funciona es, con frecuencia, la parte más fácil. Lo difícil empieza después, y dura mucho más: mantener ese código cuando cambian los requisitos, operarlo cuando ya tiene usuarios reales dependiendo de el, y hacerlo evolucionar sin que se desmorone. De eso trata la ingeniería de software: no de programar, sino de construir y mantener sistemas que sobrevivan al tiempo, al equipo y a su propio éxito.
Esta ficha recorre las prácticas que distinguen un sistema que se sostiene de uno que se pudre.
Control de versiones y por qué Git ganó
El control de versiones es la primera práctica que adopta cualquier equipo serio. Un repositorio guarda la historia completa de cada cambio: quien, que y cuando. Puedes volver a cualquier estado anterior, ramificar para probar algo sin romper lo establecido, y mezclar de vuelta cuando funciona. Permite que varias personas trabajen en paralelo sin pisarse.
Git ganó esta batalla, y no por ser sencillo. Es potente y a veces enrevesado, pero resuelve con elegancia el problema del trabajo distribuido y descentralizado, y encima se volvió el estándar de facto. Sobre Git se construyó el flujo de revisión moderna: abres una propuesta de cambio, alguien la revisa, se discute, se corrige y recién entonces se integra. Esa disciplina de la revisión por pares es una de las defensas más eficaces contra los errores.
Pruebas: lo que te da confianza
Un programa sin pruebas es una caja negra: cada cambio podría romper algo sin que nadie lo note, hasta que un usuario lo descubre, en el peor momento. Las pruebas automatizadas devuelven la confianza. Las unitarias verifican piezas aisladas; las de integración, que esas piezas funcionen juntas; las end-to-end, que el sistema completo, desde la interfaz hasta la base de datos, haga lo que debe.
Un buen conjunto de pruebas se mide no por su cantidad sino por su utilidad: una buena prueba atrapa regresiones, deja claro que se esperaba del código y da valor para refactorizar sin miedo. Hay prácticas que llevan la idea más lejos, como escribir las pruebas antes que el código, pero el principio es siempre el mismo: si no estas seguro de que algo sigue funcionando, no estas seguro de nada.
Integración y despliegue continuos
Antes, integrar el trabajo de varios programadores era un evento doloroso y eventual; hoy se hace de forma continua. Cada cambio propuesto pasa por un pipeline automático que compila, ejecuta las pruebas y verifica la calidad. Si algo falla, se sabe de inmediato, no semanas después. Esa integración continua es lo que permite a un equipo grande avanzar sin descarrilarse.
El siguiente paso es el despliegue continuo: si todo pasa las pruebas, el cambio llega a producción de forma automatizada, o casi. Esto exige confianza, y esa confianza se construye con pruebas sólidas, con la posibilidad de revertir un cambio con un clic (el rollback) y con técnicas como liberar primero a un pequeño grupo de usuarios para detectar problemas antes de que afecten a todos. Lo que antes era un evento temible, un despliegue manual de madrugada, se vuelve rutina.
Deuda técnica y arquitectura
Cada atajo, cada decisión rápida que dejas para arreglar después, es una deuda técnica: un préstamo contra el futuro que en algún momento cobrará intereses. Un poco de deuda es sano, incluso inevitable; el problema es cuando se acumula y el sistema se vuelve tan difícil de cambiar que cualquier modificación pequeña cuesta semanas. Refactorizar (mejorar la estructura del código sin cambiar lo que hace) es como se paga esa deuda.
La arquitectura del sistema decide cuanto cuesta evolucionar. No hay una respuesta universal al debate entre monolito y microservicios: un monolito bien organizado puede ser perfecto para empezar y para equipos chicos, mientras que los microservicios permiten que equipos distintos avancen de forma independiente, a cambio de mucha más complejidad operacional. La elección depende del tamaño, del equipo y del problema, no de la moda.
Operar lo que ya está vivo
Lanzar un sistema es solo el comienzo. Operarlo es otra disciplina. La observabilidad (poder entender que está pasando dentro del sistema a partir de sus registros, sus métricas y sus trazas) es lo que permite detectar problemas antes de que los usuarios los reporten. Definir niveles de servicio y recibir alertas cuando se incumplen transforma la operación reactiva en proactiva.
Y cuando algo falla, y algo siempre falla, lo importante es como se reacciona. La práctica del postmortem, analizar el incidente sin buscar culpables sino causas, es lo que permite aprender y evitar que se repita. La ingeniería de software no termina cuando el sistema funciona: termina, en todo caso, cuando el sistema es reemplazado por el siguiente. Todo lo que está en medio es operación, mantenimiento y evolución, y ahí es donde se decide si el sistema perdura o se derrumba.
Lo que separa el código del software
Programar es producir código que resuelve un problema hoy. Hacer ingeniería de software es producir un sistema que siga resolviendolo mañana, con un equipo cambiante, bajo requisitos distintos, con usuarios reales y fallas inevitables. La diferencia la marcan las prácticas: control de versiones para no perder historia, pruebas para tener confianza, integración y despliegue continuos para avanzar seguros, disciplina contra la deuda técnica para no pudrir el sistema, observabilidad para operarlo y una cultura de aprendizaje cuando las cosas fallan. Esas prácticas, y no el código en si, son lo que permite que un sistema viva mucho más que quienes lo crearon.
Los términos de este tema
32 entradas del glosario que aparecen acá. Cada una abre en su definición del índice.
- ciclo de vida del software
- cobertura de código
- commit
- control de versiones
- desarrollo guiado por pruebas
- despliegue continuo
- deuda técnica
- feature-flag
- Git
- ingeniería de software
- integración continua
- log (registro de eventos)
- merge
- metodología ágil
- métrica
- microservicios
- monolito
- observabilidad
- pipeline
- postmortem
- principios SOLID
- prueba de extremo a extremo
- prueba de integración
- prueba unitaria
- pull request
- rama
- refactorización
- repositorio
- revisión de código
- rollback
- Scrum
- traza