Resumen teórico
Imagina que construyes una casa en el árbol con tus amigos. Si es pequeña, tomas unas tablas y empiezas. Pero ahora imagina construir un rascacielos, con cientos de obreros y años de trabajo. Necesitarías planos, dibujos, pruebas, un presupuesto y una manera de que todos trabajen juntos. La ingeniería de software es la misma idea para los programas de computador: es cómo las personas planifican, construyen, comprueban y arreglan el software, sobre todo cuando el programa es grande y mucha gente trabaja en él.
¿Por qué importa? El software está en todas partes: en teléfonos, autos, hospitales, bancos y videojuegos. Muchos proyectos de software terminan tarde, cuestan demasiado o se rompen cuando la gente los usa. La ingeniería de software reúne las lecciones que las personas aprendieron por las malas, para no repetir los mismos errores.
En este resumen conocerás las ideas principales del área: cómo se organiza un proyecto en etapas (procesos), cómo descubrimos lo que quiere el cliente (requisitos), cómo dibujamos y diseñamos el sistema, cómo escribimos código limpio, cómo probamos, estimamos y gestionamos un proyecto, por qué añadir más gente a un proyecto atrasado puede atrasarlo más, cómo hacer software que no lastime a nadie y cómo trabajan los equipos de forma ágil. Lee las secciones en orden, o salta a la que necesites, y luego haz el quiz.
El panorama general y el vocabulario
Hacer un sándwich es fácil. Dirigir un restaurante que sirve mil sándwiches al día es difícil. De la misma manera, escribir un programa pequeño es fácil, pero construir un software que mucha gente usa durante muchos años es difícil. La ingeniería de software es la disciplina (un conjunto de métodos cuidadosos) que hace manejable el caso difícil.
Todo proyecto de software, grande o pequeño, necesita cuatro actividades básicas. Piensa en hornear un pastel de cumpleaños: decides qué pastel quiere el invitado, planificas la receta, lo horneas y lo pruebas, y después lo arreglas o lo cambias para la próxima fiesta.
| Actividad | Pregunta que responde | Ejemplo del pastel |
|---|---|---|
| Especificación | ¿Qué debe hacer el sistema? | El invitado quiere un pastel de chocolate sin nueces |
| Diseño e implementación | ¿Cómo lo construimos? | Elegir la receta y hornear el pastel |
| Validación | ¿Es de verdad lo que se quería? | Probarlo y revisar que no tenga nueces |
| Evolución | ¿Cómo lo cambiamos después? | Al año siguiente el invitado quiere un pastel de vainilla |
Un [[stakeholder|cualquier persona con interés en el sistema: usuarios, clientes, gerentes, probadores, vecinos que se ven afectados]] es cualquier persona a la que le importa el resultado. Un requisito es algo que el sistema debe hacer o un límite que debe respetar. Un proceso es el conjunto ordenado de actividades que se usa para construir software.
↑ Volver al índiceModelos de proceso: cascada, incremental, ágil
Un modelo de proceso es una receta para ordenar las cuatro actividades. No existe una receta mejor para todo. Un buen equipo elige la que encaja con el proyecto, como elegir entre bicicleta, auto o avión según el viaje.
Cascada: un paso después del otro
En el modelo en cascada (waterfall) las fases van en secuencia, como agua que cae por escalones. Cada fase debe terminarse y aprobarse antes de que empiece la siguiente. Es un proceso dirigido por planplanificas todo el trabajo por adelantado y luego sigues el plan.
Requisitos
|
v
Diseño
|
v
Implementación
|
v
Pruebas
|
v
MantenimientoLa cascada encaja cuando los requisitos son conocidos y estables, y cuando los errores son muy costosos. Por ejemplo, el software de control de un dispositivo médico construido junto con su hardware, bajo reglas de seguridad, necesita planificación cuidadosa y muchos documentos. Su debilidad es que cambiar de idea tarde es caro.
El modelo en V
El modelo en V es un proceso dirigido por plan dibujado como una V. Cada plan de pruebas se prepara a partir de una fase del lado izquierdo, para que la prueba correspondiente del lado derecho pueda comprobarla. Por ejemplo, el plan de pruebas de aceptación sale de la especificación de requisitos: las necesidades del cliente se comprueban contra el sistema final.
Requisitos ---------------> Prueba de aceptación
Diseño ---------------> Prueba de sistema
Código ---------------> Prueba de unidadDesarrollo incremental
En el desarrollo incremental construyes el sistema en piezas pequeñas llamadas incrementos, y entregas cada pieza al cliente para que la pruebe. Es como armar un castillo de LEGO una torre a la vez y mostrarle cada torre a tu amigo. El costo de acomodar cambios en los requisitos del cliente es menor, y el cliente recibe software útil pronto.
Ingeniería orientada a la reutilización
¿Por qué hornear el pan si puedes comprarlo? En la ingeniería de software orientada a la reutilización el sistema se construye sobre todo con componentes existentes, como un paquete comercial listo (COTS). La etapa que no tiene equivalente directo en otros procesos es modificar los requisitos para que coincidan con los componentes disponibles. Un riesgo es perder el control sobre cómo evoluciona el sistema, porque las nuevas versiones del paquete las decide el proveedor.
El modelo en espiral y el RUP
El modelo en espiral de Boehm avanza en vueltas. Lo que lo hace especial es que hace del reconocimiento explícito y la reducción del riesgo una parte de cada vuelta. Si el equipo no sabe si los usuarios aceptarán una nueva interfaz de voz, la espiral dedica esa vuelta a la evaluación y reducción de riesgos, por ejemplo construyendo una pequeña prueba.
El Rational Unified Process (RUP) tiene cuatro fases, en orden: inicio (inception), elaboración, construcción y transición. Al final de la elaboración el equipo debe entender el dominio del problema, tener una arquitectura y un plan, y haber identificado los riesgos principales. Sommerville describe el RUP con tres perspectivas: dinámica (las fases en el tiempo), estática (las actividades del proceso, como los flujos de modelado del negocio y de requisitos) y práctica (buenas prácticas).
| Modelo | Idea principal | Mejor cuando |
|---|---|---|
| Cascada | Fases en secuencia, aprobadas | Requisitos estables, seguridad, hardware involucrado |
| Incremental | Entregar en piezas pequeñas | Los requisitos cambiarán, el cliente quiere valor pronto |
| Orientado a la reutilización | Ensamblar componentes existentes | Existen buenos componentes |
| Espiral | Vueltas guiadas por el riesgo | Proyectos de alto riesgo |
| RUP | Cuatro fases, iteraciones dentro | Proyectos grandes con varias vistas |
Prototipos y manejo del cambio
Un prototipo es una versión inicial y tosca del sistema que sirve para probar ideas. Un prototipo desechable rápido no debe convertirse en el producto final, porque se hizo para ir rápido: ignora los requisitos no funcionales y está mal estructurado y es difícil de mantener. Sommerville separa dos formas de reducir el costo del retrabajo: evitar el cambio (por ejemplo, un prototipo que muestra el sistema al cliente pronto, antes del trabajo real) y tolerar el cambio (por ejemplo, la entrega incremental, donde un cambio afecta solo una pieza pequeña). Algunos clientes, como una agencia de gobierno cuyas reglas de compra exigen una especificación completa antes del contrato, no pueden usar fácilmente la entrega incremental, pues esta desarrolla la especificación junto con el software.
↑ Volver al índiceIngeniería de requisitos
Antes de construir una casa debes preguntarle a la familia qué quiere: ¿cuántas habitaciones?, ¿cuánto dinero?, ¿cerca de qué escuela? La ingeniería de requisitos es el trabajo de descubrir, escribir y comprobar lo que un sistema debe hacer y los límites que debe respetar.
Requisitos de usuario y de sistema
Los requisitos de usuario son enunciados de alto nivel, escritos para los clientes, sobre lo que el sistema debe hacer. Los requisitos de sistema describen lo mismo con más detalle, para los desarrolladores. Piensa en el menú (lo que puedes pedir) y la receta de la cocina (exactamente cómo se prepara).
Funcionales y no funcionales
| Tipo | Significado | Ejemplo |
|---|---|---|
| Funcional | Un servicio o comportamiento que el sistema ofrece | Un usuario puede buscar en el catálogo por título |
| No funcional | Una cualidad o restricción del sistema | La búsqueda en el catálogo devolverá resultados en menos de 2 segundos para el 95% de las consultas |
Los requisitos no funcionales suelen ser más críticos que los funcionales individuales, porque fallar en uno de ellos puede dejar todo el sistema inutilizable. Un avión rápido y hermoso que no es seguro no sirve. Sommerville los clasifica como requisitos de producto (cómo se comporta el producto: velocidad, fiabilidad), organizacionales (reglas de la empresa, como un proceso de desarrollo obligatorio) y externos (factores fuera del sistema y de su desarrollo, como una ley o la norma de un regulador).
Haz los objetivos comprobables
Un cliente que dice 'el sistema debe ser fácil de usar' da un objetivo vago. Una buena reescritura lo vuelve medible: 'Tras cuatro horas de capacitación, los empleados cometerán en promedio no más de dos errores por hora de uso.' Un requisito que no puedes comprobar es un requisito del que no puedes estar seguro de haber cumplido.
Descubrir requisitos: la elicitación
- Entrevistas con los stakeholders.
- [[Etnografía|un analista pasa tiempo en el lugar de trabajo y observa cómo trabaja la gente en realidad]]: excelente para la práctica real, pero débil para encontrar requisitos de funciones innovadoras que cambian la forma de trabajar.
- Escenarios e historias: una descripción de cómo un usuario hace una tarea. Un escenario de retiro de efectivo en un cajero incluye el flujo normal y también lo que puede salir mal, como un PIN incorrecto o saldo insuficiente, y cómo se maneja.
Cuando dos stakeholders quieren cosas opuestas (la jefa de enfermería quiere que cada cambio de medicación se confirme dos veces, los médicos quieren rapidez), el ingeniero hace la priorización y negociación de requisitos para resolver el conflicto.
Escribir y comprobar requisitos
Un documento de requisitos debe describir qué debe hacer el sistema, no cómo hacerlo. Usa el lenguaje de forma consistente: shall (deberá) marca un requisito obligatorio, should (convendría) uno deseable. El lenguaje natural es fácil de leer pero puede ser impreciso: 'recordar al paciente antes de cada cita' no dice cómo (SMS o correo) ni cuándo, así que indica el canal y el momento con precisión, idealmente de forma estructurada.
La validación comprueba que los requisitos son lo que el cliente realmente quiere. Comprobaciones útiles: la comprobación de realismo (¿se puede construir con la tecnología y el presupuesto disponibles? exigir renderizado 3D en tiempo real en un dispositivo diminuto no la pasa) y la generación de casos de prueba (si no puedes escribir una prueba para un requisito, probablemente es vago o ambiguo).
Los requisitos cambian. Un buen proceso de cambio tiene tres pasos en orden: análisis del problema y especificación del cambio, luego análisis del cambio y cálculo de costos, luego implementación del cambio. La trazabilidad registra, para cada requisito, quién lo pidió y qué otros requisitos y partes del diseño dependen de él, para evaluar el impacto de un cambio propuesto antes de decidir.
↑ Volver al índiceModelado de sistemas con UML
Antes de construir una ciudad de juguete, dibujas un mapa. Un modelo es una imagen simplificada de un sistema que muestra lo que importa y oculta el resto. El Unified Modeling Language (UML) es un conjunto estándar de diagramas para dibujar sistemas de software, de modo que todos lean las imágenes igual.
Sommerville describe cuatro perspectivas para modelar: externa (el contexto y el entorno), de interacción (cómo se comunican las cosas entre sí), estructural (la organización del sistema y de sus datos) y de comportamiento (lo que el sistema hace mientras se ejecuta).
Modelo de contexto
Un modelo de contexto se dibuja en las etapas iniciales para definir las fronteras del sistema: qué otros sistemas están en su entorno. Decide qué queda dentro de la caja y qué queda fuera.
Diagrama de casos de uso
Un diagrama de casos de uso muestra los actores (personas u otros sistemas) y las interacciones que tienen con el sistema, pero no su orden. Responde '¿quién hace qué con el sistema?'
Diagrama de secuencia
Un diagrama de secuencia muestra el orden de los mensajes entre objetos, con el tiempo corriendo hacia abajo. En este, el cajero le pide al Banco que verifique el PIN antes de preguntarle al cliente el monto.
Cliente Cajero Banco
| inserta tarj.| |
|------------->| verificar |
| | PIN |
| |------------>|
| |<------------|
| pide monto | PIN ok |
|<-------------| |Diagrama de clases
Un diagrama de clases muestra las clases (tipos de cosas) y las relaciones entre ellas. Los números sobre una línea son la multiplicidad. Leer Departamento 1 --- 1..* Empleado significa que cada departamento tiene al menos un empleado, y cada empleado pertenece a exactamente un departamento.
| Relación | Significado | Ejemplo |
|---|---|---|
| Generalización | 'es un tipo de' (herencia) | Un camión es un tipo de vehículo |
| Agregación | 'tiene partes' (un todo y sus partes) | Un auto tiene ruedas y un motor |
| Asociación | Dos clases están enlazadas | Un cliente hace pedidos |
Diagramas de estados y de actividades
Un diagrama de estados muestra los estados de una cosa y los eventos que la llevan de uno a otro. Una máquina expendedora en el estado Lista, cuando el stock es cero y alguien presiona el botón, se queda en Lista y muestra 'agotado': el diagrama dice qué pasa con cada evento en cada estado.
Un diagrama de actividades muestra el flujo de trabajo. Una barra gruesa con una entrada y varias salidas es un fork (bifurcación): los caminos corren en paralelo. Una barra gruesa con varias entradas y una salida es un join (unión): espera hasta que todos los caminos de entrada terminen. Así, 'Enviar pedido' después de una unión empieza solo cuando 'Cobrar tarjeta' y 'Empacar artículos' han terminado.
Ingeniería dirigida por modelos
La arquitectura dirigida por modelos (MDA) usa tres tipos de modelo: el modelo independiente de la computación (el dominio), el modelo independiente de la plataforma (PIM), que describe el sistema sin referirse a una plataforma, y el modelo específico de la plataforma (PSM). La promesa es generar código para varias plataformas a partir de los modelos. En la práctica es difícil de adoptar porque las abstracciones buenas para discutir un diseño no siempre sirven para la implementación, y las herramientas y el esfuerzo de mantener modelos y código sincronizados pueden costar más de lo que ahorran.
↑ Volver al índiceVisión general del diseño arquitectónico
Antes de construir una casa, el arquitecto decide cuántos pisos tiene y por dónde pasan las paredes y las tuberías. En software, el diseño arquitectónico se ocupa de identificar los componentes estructurales principales de un sistema y las relaciones entre ellos. Es el vínculo entre los requisitos y el diseño detallado.
Vistas: el modelo 4+1
Un solo dibujo no muestra todo sobre una casa. El modelo de vistas 4+1 usa varias vistas: la vista lógica (las abstracciones clave), la vista de procesos (cómo se ejecuta el sistema, qué partes son concurrentes), la vista de desarrollo (cómo se divide el software en piezas para los desarrolladores) y la vista física (el hardware y dónde se despliegan las cosas). El '+1' son los casos de uso, que amarran las vistas.
Patrones arquitectónicos
| Patrón | Idea | Bueno para |
|---|---|---|
| MVC (Model-View-Controller) | El modelo guarda los datos, la vista los muestra, el controlador recibe interacciones del usuario como pulsaciones de teclas y clics del ratón | Apps interactivas con varias vistas de los mismos datos |
| En capas | Cada capa usa solo los servicios de la capa inferior; una capa se puede reemplazar por otra con la misma interfaz | Sistemas construidos por niveles, reemplazo fácil |
| Repositorio | Todas las herramientas trabajan sobre un almacén central compartido | Un IDE con editor, verificador de tipos, herramienta de refactorización |
| Tubería y filtro | Los datos fluyen por filtros que los transforman | Procesos por lotes y flujos de datos |
+--------------------+
| Interfaz de usuario|
+--------------------+
| Lógica de negocio |
+--------------------+
| Almacenamiento |
+--------------------+Un diseño de tubería y filtro funciona bien para un proceso por lotes nocturno que lee pagos, los concilia y escribe informes, porque está hecho para flujos de datos. Encaja mal en los sistemas interactivos, ya que estos reaccionan a eventos impredecibles del usuario.
Arquitectura y requisitos no funcionales
Las decisiones de arquitectura determinan las cualidades del sistema. Si el requisito más importante es la mantenibilidad, usa componentes pequeños y autocontenidos que se cambian fácilmente, con los productores de datos separados de los consumidores. Si es el rendimiento, usa menos componentes, más grandes, para reducir la comunicación. Sommerville dice que tener ambos a la vez es difícil porque el rendimiento favorece pocos componentes grandes, mientras que la mantenibilidad favorece muchos pequeños.
Mira el video: Visualizando la arquitectura de software con el modelo C4 (Simon Brown)↑ Volver al índiceDiseño, implementación y código limpio
Escribir código es como escribir una carta que otras personas deben leer durante años. Incluso tú, un mes después, serás un 'extraño' para tu propio código. Esta sección trata de diseñar con ideas probadas y de escribir código fácil de leer.
Patrones de diseño
Un patrón de diseño (design pattern) es la descripción de un problema recurrente y de la esencia de su solución, lo bastante abstracta para reutilizarse en contextos distintos. El patrón Observer es un ejemplo: cuando cambia una celda de la hoja de cálculo, la tabla, el gráfico de barras y el gráfico circular se actualizan, porque son observadores a los que el sujeto avisa del cambio.
Reutilización y código abierto
La reutilización ocurre en varios niveles. Cuando un equipo no copia ningún código pero aplica el conocimiento de un diseño exitoso, ese es el nivel de abstracción. Las licencias de código abierto difieren. La GNU General Public Licence (GPL) es recíproca (copyleft): si distribuyes software que incluye código GPL, debes publicar tu software también bajo la GPL. Licencias como BSD y MIT son permisivas: una empresa puede incluir una biblioteca BSD en un producto de código cerrado y modificarla, manteniendo privado su propio código, si conserva los avisos.
Sommerville enumera cuatro actividades fundamentales de gestión de la configuración durante la implementación: gestión de versiones, construcción del sistema, gestión de cambios y seguimiento de problemas (problem tracking, también llamado issue tracking), entre ellas.
Código limpio: nombres
En Clean Code, un nombre debe revelar su intención. Una declaración como int d; // tiempo transcurrido en días debe renombrarse a elapsedTimeInDays, y eliminar el comentario. El nombre ahora dice lo que decía el comentario.
// Malo: ¿qué es d? ¿por qué 86400000?
let d = (end - start) / 86400000;
// Bueno: el nombre cuenta la historia
const msPerDay = 86_400_000;
const elapsedTimeInDays = (end - start) / msPerDay;Código limpio: funciones
- Una función debe hacer una sola cosa, en un solo nivel de abstracción.
- Un parámetro booleano es mala señal: muestra que la función hace dos cosas. Divide
exportReport(true)enexportReportAsPdfyexportReportAsCsv. - No devuelvas
nullen lugar de una lista; devuelve una lista vacía, para que quien llama pueda recorrerla sin una comprobación especial (una comprobación de null olvidada puede tumbar el programa). - Sigue la Ley de Demeter: un método debe hablar solo con sus colaboradores inmediatos, sin meterse profundo en los objetos (
a.getB().getC().doIt()).
Código limpio: comentarios, formato, clases, pruebas
La visión de Martin sobre los comentarios: un comentario suele compensar un código que no se expresa bien, así que prefiere código claro y reserva los comentarios para lo que el código no puede decir (la razón de una decisión, una advertencia). El formato sigue la metáfora del periódico: pon los conceptos de alto nivel arriba y los detalles más abajo, para que el lector pueda parar cuando ya tenga suficiente.
El Principio de Responsabilidad Única dice que una clase debe tener una sola razón para cambiar. Una clase que cambia cuando el equipo de nómina cambia las reglas de pago, cuando el equipo de base de datos cambia el esquema y cuando finanzas cambia los informes tiene varias razones para cambiar; divídela. Las pruebas limpias siguen la idea de F.I.R.S.T.: rápidas (fast), independientes, repetibles, autovalidables y oportunas (timely). Una prueba que falla al ejecutarse sola pero pasa en orden no es independiente.
Por último, las fronteras: si el código llama a una biblioteca HTTP de terceros desde 40 archivos, una nueva versión mayor obliga a 40 ediciones. Envuelve la biblioteca detrás de una pequeña interfaz propia y escribe pruebas de aprendizaje que comprueben cómo se comporta la biblioteca, para que una actualización cambie un solo lugar.
↑ Volver al índicePruebas, evolución y mantenimiento
Un puente se prueba antes de que pasen los autos. Con el software es igual. Probar es ejecutar un programa para buscar defectos (bugs) y mostrar que hace lo que debería. La famosa frase de Dijkstra, que Sommerville repite, lo dice todo: las pruebas pueden mostrar la presencia de errores, nunca su ausencia. Si 2.000 pruebas pasan, solo sabes que esas 2.000 situaciones funcionan. Otras entradas y caminos aún pueden esconder un bug. Por eso también usamos revisiones y otras verificaciones, y elegimos los casos de prueba con cuidado en lugar de acumular muchos.
Etapas de prueba
| Etapa | Qué se prueba | Quién |
|---|---|---|
| Prueba de unidad | Una función, método o clase, sola | Desarrolladores |
| Prueba de componente | Varias unidades trabajando juntas, por su interfaz (error típico: una suposición equivocada entre unidades) | Desarrolladores |
| Prueba de sistema | Los componentes unidos, y cómo interactúan | El equipo de desarrollo |
| Prueba de release | Una versión pensada para usarse fuera del equipo, para comprobar que hace lo especificado | Un equipo aparte, caja negra |
| Prueba de usuario | Alfa (los usuarios trabajan con los desarrolladores), beta (usuarios elegidos prueban en su propio entorno), aceptación (el cliente decide aceptar) | Usuarios y clientes |
Las tres primeras etapas forman la prueba de desarrollo: las hace el equipo que construye el sistema y cazan bugs (prueba de defectos). La prueba de release es distinta. Un equipo que no construyó el sistema lo prueba como caja negra (sin mirar el código), usando los requisitos, para mostrar que es lo bastante bueno para liberarse. Eso es prueba de validación. Solo la última etapa, la prueba de aceptación, la hace el cliente.
Elegir casos de prueba
No se pueden probar todas las entradas posibles. La partición en clases de equivalencia divide las entradas en grupos (particiones) que el programa debe tratar igual, y pruebas un valor de cada grupo. Los bugs se juntan en los bordes de los grupos, donde un < se confunde fácilmente con un <=. Así que prueba también los valores de frontera: el valor en cada borde y los valores justo fuera de él.
regla: la entrada es gratis solo para edades de 0 a 12
partición: debajo de 0 | 0 .. 12 | encima de 12
fronteras: -1 | 0 12 | 13
pruebas: -1 0 6 12 13function isAdultWorker(age: number): boolean {
return age >= 18 && age <= 65;
}
// pruebas de frontera: 17 -> false, 18 -> true, 65 -> true, 66 -> false¿Cuánto código visitaron las pruebas?
La cobertura dice cuánto del código ejecutan las pruebas. Hay varios criterios y no son iguales. La cobertura de sentencias exige que cada línea corra al menos una vez. La cobertura de ramas exige que cada decisión (un if) se vea yendo hacia los dos lados, verdadero y falso. La cobertura de caminos exige cada recorrido posible por la función, y solo ella ejecuta todas las combinaciones de decisiones.
function price(base: number, student: boolean, voucher: boolean): number {
let total = base;
if (student) total -= 5;
if (voucher) total -= 3;
return total;
}| Criterio | Qué exige | Pruebas necesarias aquí |
|---|---|---|
| Sentencias | Cada sentencia corre una vez | 1: (true, true) |
| Ramas | Cada decisión va a verdadero y a falso | 2: (true, true) y (false, false) |
| Caminos | Cada recorrido por la función | 4: todas las combinaciones de los dos flags |
Con n decisiones independientes seguidas hay 2^n caminos: 10 decisiones dan 1.024 caminos y 20 dan 1.048.576. Por eso la cobertura total de caminos rara vez es práctica. Y un 100% de cobertura de sentencias aún puede dejar pasar un bug: el caso (true, false) de arriba nunca corrió en la única prueba de sentencias.
Desarrollo guiado por pruebas y regresión
En el desarrollo guiado por pruebas (TDD) la prueba va antes que el código. Trabajas en rondas muy cortas, un pedacito de funcionalidad cada vez:
- Escribe una prueba automatizada para el siguiente pedacito.
- Ejecútala y mira cómo falla. Eso demuestra que la prueba sí puede detectar algo, y que el código todavía no existe.
- Escribe solo el código suficiente para que pase.
- Ejecuta todas las pruebas otra vez y limpia el código (refactoriza) mientras siguen en verde.
Una regresión es un bug en algo que antes funcionaba. La prueba de regresión vuelve a ejecutar las pruebas antiguas después de cada cambio, porque un cambio puede romper código que parece no tener relación. Hecha a mano es lenta, y la gente la omite. Con pruebas automatizadas toda la suite puede correr en cada cambio, y por eso al TDD y a la integración continua les encanta la automatización.
Evolución y mantenimiento
El software no termina cuando se entrega: sigue vivo y cambia. Esto es la evolución del software, también llamada mantenimiento. Sommerville nombra tres tipos. Fíjate en que arreglar bugs es la parte más pequeña. La mayor parte del mantenimiento es evolución, no corrección de bugs.
| Tipo | También llamado | Ejemplo |
|---|---|---|
| Reparación de fallos | Correctivo | Arreglar un total equivocado en una factura |
| Adaptación al entorno | Adaptativo | Un sistema de nómina que funciona bien debe cambiar porque se va a reemplazar el sistema operativo (nada está mal, cambió el entorno) |
| Adición de funcionalidad | Perfectivo | Los usuarios piden un informe nuevo |
- Reparación de fallos
- 17 %
- Adaptación al entorno
- 18 %
- Adición de funcionalidad
- 65 %
Lehman y Belady estudiaron cómo cambian los sistemas grandes con los años y escribieron leyes. Las dos más conocidas explican por qué gastamos tiempo limpiando:
- Cambio continuo: un sistema usado en el mundo real tiene que seguir cambiando, o se vuelve cada vez menos útil.
- Complejidad creciente: al cambiar, su estructura empeora y se enreda, a menos que alguien trabaje para mantenerla simple.
- Crecimiento continuo: tiene que ganar funcionalidades para mantener contentos a los usuarios.
- Conservación de la familiaridad: la cantidad de cambio en cada release se mantiene más o menos igual con el tiempo, porque la gente debe seguir entendiendo el sistema.
- Estabilidad organizacional: el ritmo de desarrollo se mantiene más o menos constante, sin importar cuánta gente agregues.
Por qué cambiar después de la entrega cuesta más
El mismo cambio cuesta más después de la entrega que durante el desarrollo. Sommerville da razones:
- Estabilidad del equipo: los desarrolladores se van, y los mantenedores deben reconstruir el conocimiento. El código no guarda por qué se diseñó así.
- Responsabilidad contractual: cuando una empresa construye y otra mantiene, a quien construye no le pagan por dejar el sistema fácil de cambiar. Saltarse ese trabajo le ahorra dinero a quien construyó, y la siguiente empresa paga la cuenta.
- Habilidad del personal: el mantenimiento suele ir a personas con menos experiencia.
- Edad y estructura: los programas viejos, cambiados muchas veces, son más difíciles de leer.
Sistemas antiguos: ¿mantener, arreglar o reemplazar?
Un sistema heredado (legacy) es un sistema antiguo que sigue en uso y es difícil de cambiar. Para decidir qué hacer, Sommerville mira dos cosas: su valor para el negocio (cuánto lo necesita la gente) y su calidad (cuánto cuesta cambiarlo y operarlo).
| Valor para el negocio | Calidad | Qué hacer |
|---|---|---|
| Bajo | Baja | Descartarlo |
| Alto | Baja | Reingeniería, o reemplazarlo por un sistema adecuado |
| Bajo | Alta | Mantenerlo con mantenimiento normal mientras sea barato, o descartarlo |
| Alto | Alta | Continuar con el mantenimiento normal |
Refactorización y reingeniería
Las dos mejoran la estructura del código sin cambiar lo que hace, pero en momentos distintos.
| Aspecto | Refactorización | Reingeniería |
|---|---|---|
| Idea | Prevención | Cura |
| Cuándo | Todo el tiempo, en pasos pequeños, durante el desarrollo y la evolución | Un proyecto aparte, tras años de mantenimiento, cuando los costos ya subieron |
| Qué | Renombrar, dividir un método largo, quitar código duplicado | Reestructurar, redocumentar, traducir el código fuente a un lenguaje más nuevo, muchas veces con herramientas |
| Seguridad | Protegida por pruebas automatizadas | Exige plan y presupuesto |
Gestión de proyectos y estimación
Planear un paseo escolar es elegir una fecha, un autobús, un presupuesto y quién va. La gestión de proyectos hace eso para el software: se asegura de que se entregue a tiempo, dentro del presupuesto y que cumpla las necesidades del cliente.
Gestión de riesgos
Un riesgo es algo que puede salir mal. Si la única desarrolladora que conoce la integración de pagos anuncia que se va, eso es un riesgo de proyecto, porque amenaza el cronograma y los recursos. El proceso de gestión de riesgos, en orden, es: identificación, análisis, planificación, monitoreo. En el análisis estimas dos cosas para cada riesgo: su probabilidad y la gravedad de sus efectos. Un plan de contingencia es lo que haces si el riesgo ocurre (por ejemplo, si la base de datos podría no soportar la carga máxima, preparas un plan para añadir capacidad o caché, sin cambiar el diseño ahora).
Personas y equipos
Los desarrolladores se motivan de maneras distintas: las personas orientadas a la tarea disfrutan el trabajo en sí, las orientadas a sí mismas se mueven por el éxito personal, el reconocimiento y el dinero, y las orientadas a la interacción disfrutan la compañía de los colegas. Un equipo muy cohesionado puede caer en el pensamiento de grupo (groupthink): sigue aprobando sus propias decisiones sin crítica seria. La comunicación tiene un costo: con n personas, el número de canales entre dos personas es n(n-1)/2.
- n = 2
- 1 canales
- n = 4
- 6 canales
- n = 8
- 28 canales
- n = 10
- 45 canales
- n = 20
- 190 canales
Planificación y cronograma
Un entregable (deliverable) es un producto de trabajo que se entrega al cliente. Un hito (milestone) es un punto de control donde se puede evaluar el avance (puede no entregarse a nadie). Las actividades tienen duraciones y dependencias. La ruta crítica es la cadena más larga de actividades dependientes; decide el menor tiempo en que el proyecto puede terminar.
| Actividad | Duración (días) | Depende de |
|---|---|---|
| A: Diseño | 3 | ninguna |
| B: Programar módulo 1 | 5 | A |
| C: Programar módulo 2 | 7 | A |
| D: Integrar | 2 | B, C |
En este plan, A dura 3 días. B y C empiezan después de A y corren en paralelo. D empieza solo cuando B y C terminan, es decir, después del día 3 + 7 = 10. D termina el día 12. La ruta crítica es A, C, D = 3 + 7 + 2 = 12 días. B tiene holgura, puede retrasarse 2 días sin efecto.
Estimar
En los equipos ágiles, la velocidad (velocity) es el número de story points completados por sprint. Si un equipo hizo 30, 34 y 26 puntos, el promedio es 30, así que 150 puntos restantes toman unos 5 sprints. Un modelo algorítmico de costos tiene la forma Esfuerzo = A × Tamaño^B × M. Si B es mayor que 1 (por ejemplo 1,1), el esfuerzo crece más rápido que el tamaño: duplicar el tamaño multiplica el esfuerzo por 2^1,1, unos 2,14, y no por 2. M es el producto de los multiplicadores de los factores de costo (como en COCOMO II). Los factores valorados lejos del nominal se multiplican entre sí. Por ejemplo, una estimación nominal de 100 personas-mes con multiplicadores 1,1, 1,05 y 0,98 da unas 113 personas-mes.
Cuando un cliente quiere presupuesto fijo y fecha de entrega fija con un proceso ágil, fija el plazo y el costo, entrega primero las funciones de mayor prioridad y negocia cuánto del alcance se entrega.
↑ Volver al índiceBrooks: The Mythical Man-Month y No Silver Bullet
Fred Brooks dirigió la construcción del OS/360 de IBM y escribió sobre ello en The Mythical Man-Month (1975). Sus lecciones siguen siendo ciertas hoy.
El hombre-mes mítico y la ley de Brooks
Un hombre-mes es el trabajo de una persona en un mes. El mito es creer que personas y meses se pueden intercambiar: que 12 personas-mes pueden ser 12 personas durante 1 mes. Esto solo funciona cuando la tarea se reparte entre trabajadores que no necesitan comunicarse, como recoger manzanas. Cuando las tareas dependen unas de otras, más gente significa más conversación. La ley de Brooks: añadir personas a un proyecto de software atrasado lo atrasa más. Los nuevos deben ser capacitados por quienes ya hacen el trabajo, el trabajo debe repartirse de nuevo, y cada persona extra añade caminos de comunicación.
La ley es una simplificación deliberada. Añadir personas tiene menos probabilidad de perjudicar cuando se incorporan temprano, la tarea se divide en partes independientes y ya conocen el dominio.
La regla de cronograma de Brooks
| Fase | Parte del cronograma |
|---|---|
| Planificación | un tercio |
| Programación | un sexto |
| Prueba de componentes y primeras pruebas de sistema | un cuarto |
| Prueba de sistema, con todos los componentes listos | un cuarto |
Fíjate en que programar es solo un sexto; la mayor parte del tiempo va a planificación y pruebas.
El equipo quirúrgico
Brooks describió, a partir de una propuesta de Harlan Mills, un equipo quirúrgico: un programador jefe diseña y programa, y los demás lo apoyan en funciones especializadas. La razón es que la productividad de los programadores varía unas diez veces, y pocas mentes dan un diseño más coherente.
Integridad conceptual y el arquitecto
La integridad conceptual es la consideración más importante en el diseño de sistemas: el sistema debe reflejar un conjunto coherente de ideas de diseño, aunque eso signifique dejar fuera algunas buenas funciones. Brooks separa al arquitecto de los implementadores. El arquitecto decide la especificación completa de lo que ve el usuario: los comandos, sus parámetros y su comportamiento. Cómo se construye por dentro pertenece a los implementadores.
El efecto del segundo sistema y 'planea tirar uno'
El efecto del segundo sistema: el segundo sistema de un diseñador tiende a estar sobrediseñado, cargado con las ideas que se guardó del primero. En 1975 Brooks aconsejó: 'planea tirar uno; lo harás de todos modos.' Más tarde juzgó ese consejo demasiado simple, porque suponía el modelo en cascada; pasó a favorecer el crecimiento incremental de un sistema.
No Silver Bullet (1986)
Una bala de plata mata monstruos; la gente deseaba una herramienta que acabara con la crisis del software. La afirmación central de Brooks: ninguna técnica o método de gestión por sí solo dará, dentro de una década, ni siquiera una mejora de diez veces en productividad, fiabilidad o simplicidad. Él separa las dificultades esenciales, inherentes a la estructura conceptual del software (complejidad, conformidad, mutabilidad, invisibilidad), de las dificultades accidentales, que vienen de nuestras herramientas (como manejar registros y direcciones de memoria a mano en ensamblador).
- Complejidad: el software tiene demasiados estados y partes.
- Conformidad: debe seguir muchas reglas arbitrarias del mundo exterior, como leyes de impuestos, acuerdos sindicales y formatos de bancos.
- Mutabilidad: se le pide cambiar constantemente.
- Invisibilidad: no tiene una forma natural que dibujar.
Ejemplo de cuenta: supón que el 60% del esfuerzo es accidental y el 40% esencial. Aunque elimines toda la parte accidental, queda el 40%, así que el tiempo baja a 0,4 y la mejora es 1 / 0,4 = 2,5 veces, no 10.
- Accidental 90%
- 10 x más rápido
- Accidental 60%
- 2.5 x más rápido
- Accidental 50%
- 2 x más rápido
Calidad y gestión de la configuración
Una fábrica de chocolate revisa cada lote y guarda un registro de cada versión de la receta. Los equipos de software hacen lo mismo con la gestión de calidad (comprobar que el software es bueno) y la gestión de la configuración (seguir cada versión y cada cambio).
Estándares, revisiones e inspecciones
Los estándares de producto se aplican al producto (por ejemplo, una convención de nombres). Los estándares de proceso definen cómo se hace el trabajo, como un procedimiento que toda solicitud de cambio debe seguir antes de ser aprobada.
Una inspección de código es una lectura del código en grupo para encontrar defectos. Su ventaja real sobre las pruebas es que puede encontrar muchos defectos en una sesión, porque un defecto no oculta a los otros (en una prueba, una caída puede ocultar los problemas siguientes). En una inspección formal, el moderador planifica la reunión y mantiene la discusión enfocada en encontrar defectos, no en arreglarlos.
Métricas
Una métrica es algo que medimos. Una métrica de control mide el proceso (por ejemplo, el tiempo medio para reparar un defecto reportado), mientras que una métrica de predicción mide el producto (por ejemplo, la complejidad ciclomática). Cuidado: la complejidad es un atributo interno; dice algo sobre un atributo externo como la mantenibilidad solo si la relación ha sido validada. La densidad de defectos es defectos por cada mil líneas de código (KLOC): 45 defectos en 15.000 líneas es 45 / 15 = 3 defectos por KLOC.
Gestión de la configuración
- Control de versiones: registra cada cambio en los archivos. Una baseline es una definición de una versión específica del sistema que nombra la versión exacta de cada componente.
- Control de versiones distribuido (como Git): cada desarrollador tiene una copia completa del repositorio, con todo su historial. En un sistema centralizado hay una copia en el servidor.
- Gestión de cambios: una solicitud de cambio se analiza por costo, impacto y valor estratégico, y un comité de control de cambios decide si la aprueba.
- Construcción del sistema: una herramienta de build como
makereconstruye un objetivo solo cuando una de sus entradas directas es más nueva, y los objetivos reconstruidos se propagan en cascada. Si cambiautil.c, se reconstruyeutil.o, y luego elappfinal que depende de él. - Gestión de releases: para reproducir un error en una versión antigua (como la 2.3 entregada hace dos años), necesitas las versiones exactas del compilador, las bibliotecas, los scripts de build y la configuración usados.
main.c --> main.o --\
+--> app
util.c --> util.o --/La integración continua significa integrar a menudo en la línea principal compartida. Antes de subir tu cambio, trae la última línea principal, construye el sistema con tu cambio y ejecuta las pruebas localmente, para que el build compartido siga sano.
↑ Volver al índiceConfiabilidad y seguridad (dependability y safety)
Cuando presionas el botón del ascensor, confías en él. La dependability (confianza en el sistema) es cuánto pueden confiar los usuarios en un sistema. Para algunos sistemas, como un controlador de trenes o una bomba médica, una falla puede lastimar a las personas.
Las propiedades
| Propiedad | Significado | Ejemplo |
|---|---|---|
| Disponibilidad | La probabilidad de que el sistema esté activo y pueda dar sus servicios cuando se piden | Un sitio web casi siempre accesible |
| Fiabilidad | La probabilidad de operar sin fallos durante un tiempo dado | Un programa que no se cae mientras lo usas |
| Seguridad (safety) | El sistema no causa daño a las personas ni al entorno | Una bomba nunca da una dosis mortal |
| Seguridad (security) | El sistema puede protegerse de ataques | Cerraduras y contraseñas |
| Resiliencia | El sistema sigue dando los servicios esenciales durante los problemas | Un sistema de hospital sigue funcionando con un respaldo |
Disponibilidad y fiabilidad son diferentes. Un servicio web que se cae una vez por hora pero se reinicia solo en menos de un segundo, sin que los usuarios lo noten, tiene alta disponibilidad pero baja fiabilidad.
La disponibilidad como tiempo caído: con 99,9% de disponibilidad en un año de 8.760 horas, el tiempo caído es el 0,1% de 8.760, unas 8,8 horas.
- 99%
- 87.6 horas
- 99,9%
- 8.76 horas
- 99,99%
- 0.876 horas
Términos de safety y corrección
Un peligro (hazard) es una condición que podría llevar a un accidente, por ejemplo la posibilidad de que una bomba de insulina dé una sobredosis. Un accidente es un evento no planeado que causa daño. El software correcto no siempre es seguro: si el software nunca se desvió de su especificación, pero la propia especificación puede estar incompleta o equivocada sobre los peligros, entonces el comportamiento correcto aún puede ser inseguro.
Cadena de eventos: una falta (fault, un defecto en el código) puede producir un error (un estado interno equivocado), que puede producir un fallo (failure, el usuario ve un comportamiento equivocado).
Redundancia y diversidad
La redundancia significa copias extra, como llevar una llanta de repuesto. La diversidad significa que las copias son distintas, para que no fallen por la misma razón: por ejemplo, dos sensores de velocidad de fabricantes distintos, construidos con principios distintos. En la programación de N versiones, tres equipos independientes implementan la misma especificación y un votador toma la salida de la mayoría. La debilidad: los equipos tienden a cometer errores parecidos, sobre todo donde la especificación compartida es incorrecta o difícil.
Security y safety pueden chocar
Un sistema de hospital que bloquea a un usuario durante 30 minutos tras tres inicios de sesión fallidos es seguro contra intrusos, pero en una emergencia un médico que se equivoca de contraseña no puede acceder a los datos del paciente. Una medida de seguridad puede reducir la disponibilidad y, a través de ella, la safety.
↑ Volver al índiceMétodos ágiles y lean startup
Imagina dibujar un cuadro con un amigo, mostrándoselo después de unas pocas líneas y cambiándolo según lo que le guste. Ese es el espíritu de los métodos ágiles: entregar pequeñas piezas funcionando, escuchar y adaptarse.
El Manifiesto Ágil (2001)
- Individuos e interacciones sobre procesos y herramientas
- Software funcionando sobre documentación exhaustiva
- Colaboración con el cliente sobre negociación contractual
- Responder al cambio sobre seguir un plan
'Sobre' no significa 'nunca'. El lado derecho sigue teniendo valor; solo queda en segundo lugar. Un equipo que dice 'somos ágiles, así que no escribimos ninguna documentación' interpreta mal el manifiesto.
Extreme Programming (XP)
Las prácticas de XP incluyen programación en parejas, desarrollo test-first, integración continua, releases pequeños y propiedad colectiva: cualquier pareja de desarrolladores puede cambiar cualquier parte del código, y nadie es dueño de un módulo. XP también advierte sobre el ritmo sostenible: las horas extra largas (como semanas de sesenta horas) bajan la calidad del código y la productividad, así que la ganancia neta es pequeña o negativa.
Scrum
| Elemento | Qué es |
|---|---|
| Product owner | Ordena el product backlog por valor |
| Scrum master | Ayuda al equipo a seguir Scrum y quita obstáculos |
| Equipo de desarrollo | Construye el incremento |
| Sprint | Un periodo corto y fijo (normalmente de 1 a 4 semanas) que termina con un incremento funcionando |
| Retrospectiva del sprint | El equipo se reúne a solas al final del sprint para discutir cómo trabaja y acordar mejoras |
Lo ágil es más difícil de aplicar, sin adaptación, a un sistema crítico para la seguridad construido por varios equipos distribuidos, donde un regulador debe aprobar una especificación y una documentación detalladas.
La Lean Startup
Eric Ries aplica el mismo espíritu de aprendizaje a las startups, que venden bajo incertidumbre extrema. El ciclo es construir, medir, aprender. Un producto mínimo viable (MVP) es la versión de un producto que permite al equipo completar un ciclo con el menor esfuerzo.
Ideas
/ \
Aprender Construir
\ /
MedirPara una startup, el progreso es el aprendizaje validado sobre los clientes. Entregar todas las funciones a tiempo sin evidencia de que los clientes las quieren no es progreso. Las métricas de vanidad, como el total de registros, pueden crecer sin que nada mejore. Usa el análisis de cohortes, que sigue a grupos de clientes que entraron en el mismo periodo. Si el 2% de cada cohorte semanal paga, en todas las cohortes, entonces los clientes de pago crecieron porque se registró más gente, no porque el producto mejorara.
La contabilidad de la innovación tiene tres pasos, en orden: establecer una línea base con un MVP, ajustar el motor hacia el ideal y luego decidir si se hace un pivote o se persevera. Un pivote es una corrección de rumbo estructurada. Un pivote de zoom-in convierte una sola función en todo el producto (los clientes usan solo la parte de control de tiempo de una suite grande de proyectos, así que construye solo esa). Ries nombra dos hipótesis de salto de fe: la hipótesis de valor (¿el producto entrega valor?) y la hipótesis de crecimiento (¿cómo descubrirán el producto los nuevos clientes?).
Mira el video: Agile is Dead (Dave Thomas, GOTO)↑ Volver al índiceSimplicidad y deuda técnica
Un cuarto desordenado es más difícil de ordenar cuanto más esperas. Con el código pasa igual. El libro Code Simplicity, de Max Kanat-Alexander, dice que todo software tiene un propósito, ayudar a las personas, y que diseñar es el arte de mantener las cosas simples para que el software siga ayudando.
Deuda técnica
La metáfora de Ward Cunningham compara un atajo rápido y desordenado con un préstamo. Obtienes algo ahora (velocidad) y pagas después. La metáfora tiene tres partes.
| En dinero | En código |
|---|---|
| El préstamo | El tiempo que ganas al entregar la solución rápida |
| El principal | El trabajo para cambiar el atajo por la solución correcta (se paga una vez, y la deuda termina) |
| Los intereses | El esfuerzo extra de cada cambio hecho encima del atajo, que se paga una y otra vez hasta pagar el principal |
La deuda no siempre es mala: asumirla para cumplir una fecha puede ser un buen trato, si planeas pagarla. Pero los intereses se cobran por cambio, así que dependen de cuántas veces se toca el código y de cuánto tiempo va a vivir. La deuda en código que se cambia cada semana es la que hay que pagar primero. La deuda en código que rara vez cambia, o que se tirará pronto, cuesta poco.
- Mantener el atajo para siempre
- 10 días
- Diseño correcto ahora
- 4 días
- Reemplazar el atajo de inmediato
- 6 días
- Reemplazarlo tras 3 cambios
- 9 días
Los números salen de un ejemplo pequeño, contados frente al plan que toma el atajo. El atajo ahorra 4 días ahora, cada cambio encima de él cuesta 1 día más, y reemplazarlo después cuesta 6 días. Tras 10 cambios, mantener el atajo cuesta 10 días de intereses, y hacerlo bien cuesta los 4 días que el atajo ahorró. Reemplazarlo de inmediato cuesta 6, y esperar 3 cambios cuesta 3 + 6 = 9. Pero si el módulo recibe solo 3 cambios y luego se retira, mantener el atajo cuesta apenas 3 y gana. Con 4 cambios, mantener y hacerlo bien empatan (4 y 4).
Martin Fowler agrega que la deuda puede ser elegida o no, y sensata o tonta:
| Intención | Prudente | Imprudente |
|---|---|---|
| Deliberada | Debemos entregar ya y trataremos las consecuencias después del release | No tenemos tiempo para diseñar |
| Inadvertida | Ahora sabemos cómo deberíamos haberlo hecho | ¿Qué es separar en capas? |
El costo del desorden
Robert C. Martin abre Clean Code con esta historia. Un equipo avanza rápido al principio haciendo desorden. Después cada cambio tarda más, y la productividad del equipo se acerca a cero. Todos prometen limpiar luego. Pero el luego trae nuevas fechas límite, así que luego es igual a nunca (ley de LeBlanc). La única forma de ir rápido es mantener el código limpio todo el tiempo.
La Regla del Boy Scout dice: deja el campamento más limpio de como lo encontraste. En el código, haz el check-in un poco más limpio de como lo encontraste: un nombre mejor, una función dividida en dos, una duplicación eliminada. Es la forma más barata de pagar la deuda, porque ya tienes el código abierto y lo entiendes. Pide pasos pequeños, no una reescritura en cada visita.
A veces la gerencia pide un gran rediseño en el cielo: los mejores desarrolladores construyen un sistema nuevo desde cero mientras los demás siguen ampliando el viejo. Es una carrera. El sistema nuevo debe hacer todo lo que hace el viejo, y el viejo sigue avanzando. Puede durar años, y un equipo con los mismos hábitos también desordenará el código nuevo.
sistema viejo: v1 -> v2 -> v3 -> v4 -> v5 ... (los clientes piden cambios)
sistema nuevo: inicio ...... todavía alcanzando ......
la meta no deja de alejarseLeyes de Code Simplicity
- Propósito: el propósito del software es ayudar a las personas. Un diseño es bueno cuando deja el software lo más útil posible, que siga siendo útil, y que sea fácil para los programadores crearlo y mantenerlo.
- Ley de la simplicidad: la facilidad de mantenimiento de cualquier software es proporcional a la simplicidad de sus partes individuales. Nadie guarda en la cabeza un sistema grande entero, así que lo que cuenta es si la parte que estás cambiando se puede entender por sí sola.
- Ley de la probabilidad de defectos: la probabilidad de introducir un bug es proporcional al tamaño del cambio.
El libro también trae la ecuación del diseño de software. La conveniencia de un cambio (D) crece con el valor y baja con el esfuerzo, y ambos se dividen en el tiempo:
Vn + Vf Vn = valor ahora Vf = valor futuro
D = -----------
Ei + Em Ei = esfuerzo de implementar
Em = esfuerzo de mantenerLa implementación se paga una vez, pero el mantenimiento se paga mientras el software viva, y el valor futuro también se sigue sumando. Así que, para un software que vive años, reducir el esfuerzo de mantenimiento importa más que reducir el esfuerzo de implementación. Un diseño simple hace justo eso. La ecuación es una forma de razonar sobre compromisos (trade-offs), no una calculadora que dé puntajes exactos.
Errores frente al cambio
El libro nombra tres formas de equivocarse, y dos vienen de intentar adivinar el futuro:
- Escribir código que no se necesita (una funcionalidad o un sistema de plugins que nadie pidió).
- No dejar el código fácil de cambiar.
- Ser demasiado genérico (construir para muchos casos cuando solo existe uno).
La cura es construir solo lo que sabes que se necesita, mantenerlo simple para que sea fácil de cambiar, y generalizar cuando aparezca un segundo caso real.
// Sobrediseñado: nadie ha pedido otros formatos todavía
class ReportExporter { /* plugins, un lenguaje de config, 3 capas... */ }
// Simple: resuelve la necesidad de hoy
const toCsv = (rows: string[][]): string => rows.map((r) => r.join(",")).join("\n");Los cambios pequeños son más seguros
Imagina dos formas de arreglar un bug: cambiar 10 líneas, o reescribir de paso un archivo entero de 600 líneas. Según la ley de la probabilidad de defectos, la reescritura tiene mucha más probabilidad de introducir un bug nuevo, porque un cambio 60 veces mayor tiene 60 veces más lugares donde fallar. El código nuevo no tiene los bugs viejos, pero tiene los suyos. Así que haz cambios pequeños y paso a paso, para que cada uno sea fácil de revisar. Una reescritura aún puede estar justificada, pero nunca por ser la opción segura.
↑ Volver al índiceJuntando todo
Todas las secciones anteriores son partes de una sola historia: descubres lo que la gente necesita (requisitos), lo dibujas y diseñas (UML, arquitectura), lo construyes de forma limpia, lo compruebas (pruebas, inspecciones), gestionas personas, tiempo y riesgo, y lo mantienes vivo (evolución), siempre simple y seguro.
| Idea | Quién | Resumen en una línea |
|---|---|---|
| Cascada vs incremental | Sommerville | Planificar todo antes vs entregar por piezas |
| Espiral | Boehm | Cada vuelta reduce el riesgo |
| Ley de Brooks | Brooks | Añadir gente a un proyecto atrasado lo atrasa más |
| No Silver Bullet | Brooks | Ninguna herramienta sola da una mejora de diez veces |
| Clean Code | Martin | Nombres, funciones pequeñas, responsabilidad única |
| Lean Startup | Ries | Construir, medir, aprender; aprendizaje validado |
| Deuda técnica | Cunningham | Los atajos acumulan intereses |
Errores comunes
- Pensar que ágil significa sin documentación o sin plan.
- Pensar que más gente siempre acelera un proyecto.
- Confundir disponibilidad con fiabilidad.
- Escribir requisitos que no se pueden probar.
- Refactorizar sin pruebas.
- Leer una métrica (como la complejidad ciclomática) como si fuera la calidad misma.