2.0

Ver en inglés

2. Introducción a la Parte 2: Programación de software

Esta parte se ocupa del oficio cotidiano de escribir software que muchas personas puedan leer, modificar y confiar durante toda su vida. La Parte 1 sentó las bases de cómo se organizan los equipos y toman sus decisiones. Aquí nos adentramos en el propio código: las convenciones que se siguen, la forma de dar forma a los diseños y las interfaces, la manera de probar y revisar el trabajo, el manejo del historial del código fuente y la redacción de la documentación. Estas son las prácticas que separan una base de código que acelera la entrega de una que se resiste a cada cambio.

En un equipo grande, el oficio no es cuestión de gusto personal. Es la forma en que nos coordinamos. Cuando cientos o miles de ingenieros, contratistas y sucesores intervienen en los mismos sistemas, son las convenciones compartidas y los contratos claros lo que permite a todos trabajar en paralelo sin choques constantes. No se olvide: el código se lee con mucha más frecuencia de la que se escribe, y gran parte de esa lectura ocurre años después, a manos de personas a quienes jamás conocerá.

En entornos empresariales y gubernamentales, la posta se eleva aún más. Los sistemas suelen sobrevivir a sus autores por más de una década. La regulación y la auditoría exigen documentación que sirva de evidencia de control. El conocimiento debe trasladarse a través de rotaciones de personal y de límites contractuales. Por eso, los capítulos que siguen tratan la calidad no como una hazaña heroica, sino como una propiedad de la ingeniería, en su mayor parte automatizada, que emerge de la manera en que todo el equipo trabaja.

Capítulos de esta parte

  • 2.1 Estándares y estilo de código: convenciones compartidas y ejecutadas automáticamente para el nombrado, el formato y las construcciones idiomáticas, de modo que muchos autores escriban como si un único autor meticuloso hubiera redactado el texto, para que quien revise el código dedique su atención al diseño y no al estilo.

  • 2.2 Principios de diseño de software: heurísticas como SOLID (los cinco principios de diseño orientado a objetos), DRY (no repita su trabajo), el acoplamiento y la cohesión, y el Diseño Orientado al Dominio (modelar el software en el lenguaje del dominio de negocio), tratados como herramientas con un ámbito de aplicación y fallos conocidos, no como leyes que se acatan.

  • 2.3 Diseño de interfaces y APIs: diseñar los contratos por los que se encuentran los sistemas y los equipos, de modo que equipos independientes puedan modificar sus interiores sin romper a sus consumidores ni imponer un despliegue sincronizado.

  • 2.4 Estrategia de pruebas: elecciones deliberadas sobre qué probar, en qué nivel y hasta qué grado de confianza, construyendo una red de seguridad rápida y fidedigna que permite a una organización grande desplegar con frecuencia y seguridad.

  • 2.5 Revisión de código y colaboración: examinar los cambios antes de integrarlos, para detectar defectos, difundir conocimiento, hacer cumplir los estándares y satisfacer los controles de cumplimiento, manteniendo la revisión ágil y constructiva, en lugar de ceremonial.

  • 2.6 Control de versiones y gestión del código fuente: el sistema de registro definitivo de cada cambio, y la disciplina de ramificación, repositorio y registro que mantiene la rama principal lista para publicar, el historial legible y la trazabilidad de auditoría intacta.

  • 2.7 Documentación: el conocimiento escrito, desde guías de inicio hasta manuales de operación (procedimientos operativos paso a paso) y registros de decisiones, que protege contra el riesgo de dependencia de una sola persona, acelera la incorporación de nuevos miembros y transmite comprensión a lo largo de los años y las fronteras contractuales.

  • 2.8 Requisitos de software: captar, especificar, validar y gestionar lo que el software debe hacer y con qué nivel de desempeño, con la trazabilidad que exige el trabajo regulado y gubernamental.

  • 2.9 Construcción de software: el oficio de edificar software funcional: minimizar la complejidad, construir para la verificación y el cambio, programar de forma defensiva y reutilizar de manera disciplinada.

  • 2.10 Gestión de configuración de software: identificar, controlar y auditar cada elemento de configuración (cualquier artefacto cuyas versiones deben rastrearse y controlarse) y cada cambio, para que los lanzamientos sean reproducibles y la trazabilidad de auditoría se conserve.

  • 2.11 Calidad del software: la calidad como propiedad gestionada, más amplia que la prueba: modelos de calidad, garantía frente a control, medición, gestión de defectos y el coste de la calidad.

  • 2.12 Modelos y métodos de software: cuándo y cómo modelar, abarcando modelos estructurales y de comportamiento, métodos formales (especificación y verificación basadas en matemáticas), prototipado y métodos ágiles, y cuándo el modelado es una pérdida de esfuerzo.

  • 2.13 Fundamentos de la computación, las matemáticas y la ingeniería: los cimientos perdurables sobre los que se asienta la práctica: algoritmos y estructuras de datos, lógica y probabilidad, y el método empírico de la ingeniería.

  • 2.14 Estructura de proyectos y repositorios: convenciones coherentes para organizar una solución y su repositorio, incluidas carpetas estándar, un README como punto de entrada y configuración compartida, de modo que cualquier ingeniero pueda orientarse en cualquier base de código.

  • 2.15 Depuración y resolución de incidencias: encontrar y corregir defectos como una práctica disciplinada y enseñable: reproducir, aislar mediante búsqueda binaria, formular y poner a prueba hipótesis, y capturar cada corrección como una prueba de regresión, en lugar de adivinar o aplicar cambios a ciegas.

  • 2.16 Ingeniería de rendimiento: lograr que el software sea lo bastante rápido por diseño, estableciendo presupuestos de rendimiento, midiendo y analizando antes de optimizar, comprendiendo el coste algorítmico y la latencia en la cola, y protegiendo contra regresiones, todo ello a nivel de código y de componente.

  • 2.17 Concurrencia y paralelismo: escribir código concurrente correcto por defecto, priorizando la inmutabilidad y la comunicación por mensajes, comprendiendo las carreras, los bloqueos mutuos y la visibilidad de la memoria, eligiendo el modelo de sincronización y abstracción de mayor nivel adecuado, y probando de forma deliberada el comportamiento no determinista.

  • 2.18 Gestión de dependencias y cadena de suministro: administrar el código de terceros que compone la mayor parte de un sistema moderno mediante disciplina de versiones y archivos de bloqueo, un ritmo constante de actualización, una huella de dependencias mínima y verificada, y una trazabilidad y lista de materiales de software (SBOM) que garanticen una cadena de suministro fiable.

  • 2.19 Refactorización y deuda técnica: mejorar el diseño interno de código que ya funciona, respaldado por una suite de pruebas fidedigna; reconocer olores de código y aplicar refactorizaciones pequeñas con nombres reconocibles; emplear la figueroa estranguladora para cambios de mayor envergadura; y gestionar la deuda técnica como un portfolio visible y financiado, no como un fracaso moral.

  • 2.20 Patrones de manejo de errores y resiliencia: decidir de forma deliberada cómo falla y se recupera el código, mediante contratos de error claros, elecciones entre fallar rápido y fallar de forma segura, reintento con retroceso y idempotencia, disyuntores y degradación elegante, y nunca tragar un error en silencio.

  • 2.21 Sistemas de tipos y análisis estático: detectar clases enteras de defectos antes de que el código se ejecute, mediante tipado estático e incremental que hace inrepresentables los estados ilegales, y linteres, verificadores de tipos y analizadores integrados en el editor y en la línea de integración.

Cómo se interrelacionan estos capítulos

El hilo conductor de la Parte 2 es la modificabilidad a escala. Toda práctica expuesta aquí existe para que muchas personas alteren, con confianza, un sistema compartido y de larga vida. Los estándares de código (2.1) y los principios de diseño (2.2) dan forma al código para que pueda comprenderse y modificarse. El diseño de interfaces (2.3) traza los límites que permiten a los equipos alterar sus interiores de forma independiente. La prueba (2.4) provee la red de seguridad que hace posible el cambio seguro. La revisión de código (2.5) es el punto donde el trabajo individual encuentra la propiedad colectiva, y donde los estándares se aplican de verdad. El control de versiones (2.6) es el cimiento sobre el que se asientan la revisión, la integración y la auditoría. Y la documentación (2.7) preserva la intención que subyace a todo ello para quienes vengan después.

Estos capítulos también alimentan el resto del manual. Las interfaces y los principios de diseño que aquí se presentan se convierten en los bloques de construcción de los sistemas de la Parte 3, especialmente los fundamentos de arquitectura (capítulo 3.1). La estrategia de pruebas (2.4) y el control de versiones (2.6) son la materia prima de las líneas de entrega automatizadas del capítulo 8.1. Las prácticas de documentación (2.7) se enlazan directamente con los manuales de operación y la observabilidad de las operaciones, como el capítulo 9.2. Y toda esta parte se apoya en los valores y los fundamentos de la toma de decisiones sentados en la Parte 1, convirtiendo los principios compartidos en un oficio cotidiano y concreto.