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:

  1. Corrección: El software hace lo que se especificó.
  2. Fiabilidad: El software funciona sin fallar bajo las condiciones esperadas.
  3. 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
Durante el código
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):

  1. Requerimientos incompletos o cambiantes (31%)
  2. Falta de involucramiento del usuario (13%)
  3. Falta de recursos (12%)
  4. Expectativas poco realistas (9%)
  5. 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ó:

  1. 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.
  2. Sin ninguna prueba automática.
  3. Las credenciales de la BD hardcodeadas en el código.
  4. 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:

  1. Extraer calcular_total() en funciones pequeñas (SRP).
  2. Agregar pruebas para las funciones extraídas.
  3. Mover credenciales a variables de entorno.
  4. 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:

  1. Refinamiento: El equipo y el Product Owner discuten la historia de usuario, clarifican criterios de aceptación.
  2. Sprint Planning: La historia entra al sprint. El equipo la estima y planifica.
  3. Desarrollo: El desarrollador crea una rama (feature/agregar-pago-tarjeta), escribe el código y las pruebas.
  4. Code Review: Otro miembro del equipo revisa el código (GitHub Pull Request). Se comentan mejoras.
  5. Pruebas automáticas (CI): Al hacer push, el pipeline de CI corre todas las pruebas automáticamente.
  6. Merge: Si las pruebas pasan y el reviewer aprueba, se mergea a main.
  7. Despliegue (CD): El pipeline despliega automáticamente a producción (o a staging primero).
  8. 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.

✏️ 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

1.6 Para profundizar


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.