Introducción a la ingeniería de software
"El software siempre va a ser entregado tarde. La pregunta es cuánto tarde, con cuántos bugs, y si alguien va a morir por eso." — parafraseo oscuro pero honesto.
Qué vas a aprender en este capítulo
La ingeniería de software es una disciplina joven — tiene menos de 60 años. Nació de una crisis: en la década de 1960, los proyectos de software fallaban sistematicamente, con costos desbordados y software que simplemente no funcionaba. Este capítulo explica por qué existe la disciplina, cuáles son los principios fundamentales, y por qué construir software bien (no solo rápido) vale la pena económicamente.
1.1 ¿Qué es la ingeniería de software?
💡 Intuición
La ingeniería civil aplica principios científicos para construir puentes, carreteras y edificios de forma segura, dentro del presupuesto y con calidad predecible.
La ingeniería de software hace lo mismo para el software: aplicar principios sistemáticos, disciplinados y cuantificables para desarrollar, operar y mantener software.
La diferencia entre "programar" y "ingeniería de software":
- Un programador resuelve un problema escribiendo código.
- Un ingeniero de software resuelve el problema correcto, con el equipo correcto, en el tiempo correcto, con calidad medible, y crea código que otro programador puede mantener 2 años después.
📐 Fundamento
Definición (IEEE): La ingeniería de software es la aplicación de un enfoque sistemático, disciplinado y cuantificable al desarrollo, operación y mantenimiento del software.
Las tres dimensiones del software de calidad:
- Corrección: El software hace lo que se especificó.
- Fiabilidad: El software funciona sin fallar bajo las condiciones esperadas.
- Mantenibilidad: El software puede ser modificado eficientemente para corregir defectos, mejorar el rendimiento o adaptarse a nuevos requisitos.
Por qué el software es diferente al hardware:
- No se desgasta físicamente — pero sí se "pudre" (rot) por deuda técnica acumulada.
- Los cambios parecen baratos (solo editar texto) pero pueden tener efectos en cascada inesperados.
- Es difícil de medir — no podés pesar ni medir con regla la calidad del código.
- Es invisible para el cliente — que ve solo la interfaz.
El costo de los bugs según cuándo se detectan:
| Fase de detección | Costo relativo |
|---|---|
| Durante el diseño | 1× |
| Durante el código | 6× |
| Durante las pruebas | 15× |
| En producción | 100× |
Un bug encontrado en producción cuesta 100 veces más que uno encontrado durante el diseño. Esta estadística (IBM, Boehm) justifica todo el esfuerzo de análisis, diseño y pruebas.
1.2 La crisis del software (1968)
💡 Intuición
En 1968, la OTAN convocó una conferencia en Garmisch, Alemania, para discutir una crisis: los proyectos de software siempre fallaban. Costos desbordados, entrega tardía, software que no hacía lo que se pedía.
El término "crisis del software" fue acuñado ahí. Y aunque han pasado casi 60 años, el Chaos Report del Standish Group sigue mostrando que ~66% de los proyectos de software tienen problemas graves. La crisis no terminó — aprendimos a gestionarla mejor.
Grandes fallos históricos:
- Therac-25 (1985-1987): Máquina de radioterapia con bugs en el software de control. Mató a 6 pacientes por sobredosis de radiación. Causa: condiciones de carrera en código concurrente, sin manejo de errores adecuado.
- Ariane 5 (1996): Cohete de la ESA explotó 37 segundos después del lanzamiento. Causa: desbordamiento de entero al convertir un float de 64 bits a un entero de 16 bits. Pérdida: $500 millones.
- Knight Capital (2012): Una actualización de software mal desplegada causó que el sistema comprara y vendiera acciones erróneamente durante 45 minutos. Pérdida: $440 millones. La empresa quebró.
- Boeing 737 MAX (2018-2019): El sistema MCAS tenía un bug de diseño que basaba toda su lógica en un solo sensor. 346 muertos.
📐 Fundamento
Causas comunes de fracaso de proyectos de software (Chaos Report):
- Requerimientos incompletos o cambiantes (31%)
- Falta de involucramiento del usuario (13%)
- Falta de recursos (12%)
- Expectativas poco realistas (9%)
- Falta de soporte ejecutivo (8%)
Los síntomas de software de mala calidad:
- Rigidez: Un solo cambio requiere modificar docenas de archivos.
- Fragilidad: Cambiar A rompe B, C y D sin relación aparente.
- Inmovilidad: Es imposible reutilizar el código en otro proyecto.
- Viscosidad: Es más fácil hacer un hack que hacer las cosas bien.
- Complejidad innecesaria: Diseño sobrediseñado para casos hipotéticos que nunca ocurrirán.
- Repetición innecesaria: Misma lógica copiada en múltiples lugares (viola DRY — Don't Repeat Yourself).
- Opacidad: El código es difícil de leer y entender.
(Robert C. Martin — "Uncle Bob")
1.3 Deuda técnica
💡 Intuición
Ward Cunningham acuñó el término deuda técnica en 1992: cuando tomás un atajo en el código (hacer algo "rápido y sucio" en lugar de bien), estás tomando un préstamo de tu futuro. Ese préstamo genera intereses — el código futuro se vuelve más difícil de modificar, los bugs aumentan, y eventualmente el sistema se vuelve tan frágil que cualquier cambio cuesta semanas.
Como una tarjeta de crédito: está bien usarla para emergencias, pero si solo pagás el mínimo, los intereses te ahogan.
Señales de deuda técnica:
- Cada cambio pequeño tarda días en implementarse.
- Los nuevos miembros del equipo tardan meses en ser productivos.
- Hay secciones del código que "nadie toca" porque "si lo tocás, se rompe todo".
- Los bugs que se corrigen generan nuevos bugs.
- El equipo tiene miedo de hacer cambios.
📐 Fundamento
Tipos de deuda técnica:
| Tipo | Descripción | Ejemplo |
|---|---|---|
| Deliberada/prudente | Atajo consciente con plan de pago | "Lo haremos bien después del lanzamiento" |
| Deliberada/imprudente | Atajo consciente sin plan | "No tenemos tiempo para pruebas" |
| Inadvertida/prudente | Sin saber que era deuda | Código escrito antes de aprender mejores patrones |
| Inadvertida/imprudente | Sin saberlo, sin plan | Código mal estructurado por desconocimiento |
(Clasificación de Martin Fowler)
Métricas de deuda técnica:
- Tiempo de ciclo: Cuánto tarda una historia de usuario desde "empezada" hasta "en producción".
- Defect rate: Bugs por línea de código o por sprint.
- Code coverage: Porcentaje del código cubierto por pruebas automáticas.
- Complejidad ciclomática: Número de caminos de ejecución posibles en una función (alto = difícil de testear).
- Technical Debt Ratio (TDR): Tiempo para pagar la deuda dividido por el costo de construirlo. > 20% es señal de alerta.
Herramientas para medir deuda: SonarQube, Code Climate, Pylint, ESLint.
🛠️ En la práctica
Deuda técnica en La Esquina:
El programador del sistema original de La Esquina tenía prisa. Dejó:
- La función
calcular_total()de 300 líneas que hace todo: calcula, guarda en la BD, imprime el ticket y envía un email. - Sin ninguna prueba automática.
- Las credenciales de la BD hardcodeadas en el código.
- La lógica de negocio mezclada con la presentación HTML.
Cada vez que el dueño pide un cambio, el programador tarda horas en entender el código y siempre rompe algo. Eso es deuda técnica en acción.
El plan de pago:
- Extraer
calcular_total()en funciones pequeñas (SRP). - Agregar pruebas para las funciones extraídas.
- Mover credenciales a variables de entorno.
- Separar la lógica en capas (MVC).
No se puede pagar todo de golpe — se prioriza según el costo/beneficio de cada ítem.
1.4 El rol del ingeniero de software
📐 Fundamento
Ciclo de vida de un feature en un equipo profesional:
- Refinamiento: El equipo y el Product Owner discuten la historia de usuario, clarifican criterios de aceptación.
- Sprint Planning: La historia entra al sprint. El equipo la estima y planifica.
- Desarrollo: El desarrollador crea una rama (
feature/agregar-pago-tarjeta), escribe el código y las pruebas. - Code Review: Otro miembro del equipo revisa el código (GitHub Pull Request). Se comentan mejoras.
- Pruebas automáticas (CI): Al hacer push, el pipeline de CI corre todas las pruebas automáticamente.
- Merge: Si las pruebas pasan y el reviewer aprueba, se mergea a
main. - Despliegue (CD): El pipeline despliega automáticamente a producción (o a staging primero).
- Monitoreo: Se observa el comportamiento en producción (logs, alertas, métricas).
Habilidades del ingeniero de software:
Técnicas:
- Dominar al menos un lenguaje de programación profundamente.
- Git y flujos de trabajo (Git flow, trunk-based development).
- Pruebas automáticas (unitarias, de integración, e2e).
- Sistemas operativos y redes (bases del ciclo 5).
- Bases de datos (SQL y NoSQL básico).
- CI/CD y conceptos de DevOps.
No técnicas (a veces llamadas "blandas"):
- Comunicación clara con clientes no técnicos.
- Estimación y planificación realista.
- Dar y recibir feedback en code reviews.
- Documentación clara y concisa.
- Gestión del tiempo en un entorno de cambios frecuentes.
1.5 Ejercicios
✏️ Ejercicio 1.1 — Análisis de fallos
Investigá brevemente uno de los siguientes fallos de software (hay información pública disponible) y respondé:
a. ¿Cuál fue el fallo técnico concreto? b. ¿En qué fase del desarrollo se originó el problema? c. ¿Qué práctica de ingeniería de software podría haberlo prevenido?
Opciones: Therac-25, Ariane 5, Knight Capital, healthcare.gov (lanzamiento 2013), Boeing 737 MAX.
Solución (ejemplo — Ariane 5)
a. Fallo técnico: Conversión de un número de punto flotante de 64 bits (velocidad horizontal) a un entero de 16 bits causó un desbordamiento aritmético. El sistema de navegación de respaldo (que era el mismo código del Ariane 4) no manejó la excepción y se apagó. El cohete interpretó el error como una desviación extrema y activó los propulsores al máximo, causando la desintegración estructural.
b. Fase de origen: Diseño. El código fue reutilizado del Ariane 4 sin verificar si los supuestos del Ariane 4 (velocidad máxima más baja) eran válidos para el Ariane 5.
c. Práctica preventiva: Pruebas de integración con los parámetros reales del Ariane 5 (no del 4), análisis de valores límite (qué pasa con los valores máximos de cada variable), y manejo robusto de excepciones que no simplemente apague el sistema sino que active un modo seguro.
✏️ Ejercicio 1.2 — Identificar deuda técnica
Lee el siguiente fragmento de código (Python) e identifica al menos 4 tipos de problemas/deuda técnica. Para cada uno, indica el problema y cómo lo corregirías.
def f(x, y, z):
# conectar a la base de datos
import mysql.connector
conn = mysql.connector.connect(host='localhost', user='root', password='1234', database='tienda')
cur = conn.cursor()
# calcular
total = x * y
iva = total * 0.13
total_con_iva = total + iva
# guardar en BD
cur.execute("INSERT INTO ventas VALUES (" + str(x) + ", " + str(y) + ", " + str(total_con_iva) + ")")
conn.commit()
# imprimir ticket
print("=== TICKET ===")
print("Cantidad: " + str(x))
print("Precio: $" + str(y))
print("Total con IVA: $" + str(total_con_iva))
return total_con_iva
Solución
-
Nombre de función y parámetros no descriptivos.
f(x, y, z)no dice nada. Debería sercalcular_venta(cantidad, precio_unitario, id_producto)o similar. -
Credenciales hardcodeadas.
password='1234'en el código fuente es un gravísimo problema de seguridad. Usar variables de entorno:os.environ.get('DB_PASSWORD'). -
Violación de SRP. La función calcula, guarda en BD, e imprime. Tres responsabilidades distintas. Separar en
calcular_total(cantidad, precio),guardar_venta(venta),imprimir_ticket(venta). -
Inyección SQL. La concatenación de strings en el
INSERTpermite inyección SQL. Usar parámetros:cur.execute("INSERT INTO ventas VALUES (%s, %s, %s)", (x, y, total_con_iva)). -
Magia numérica.
0.13es el IVA pero no está documentado. Usar una constante:IVA = 0.13oIVA_EL_SALVADOR = Decimal('0.13'). -
importdentro de función. Las importaciones deben ir al inicio del módulo. -
Parámetro
zno se usa. ¿Para qué está ahí?
1.6 Para profundizar
- Pressman, Ingeniería del Software: Un Enfoque Práctico, cap. 1-3.
- Martin, Clean Code — el libro de cabecera para escribir código mantenible.
- Fowler, Refactoring: Improving the Design of Existing Code.
- Siguiente: Ágil y Scrum — cómo gestionar un proyecto de software en la práctica.
Definiciones nuevas: ingeniería de software, crisis del software, deuda técnica, corrección, fiabilidad, mantenibilidad, rigidez, fragilidad, inmovilidad, viscosidad, DRY, complejidad ciclomática, Technical Debt Ratio.