Ingeniería de software en Google - Titus Winters - E-Book

Ingeniería de software en Google E-Book

Titus Winters

0,0

Beschreibung

Hoy en día, los ingenieros de software necesitan saber no solo cómo programar eficazmente, sino también cómo desarrollar prácticas de ingeniería para que la base de código sea sostenible y funcione bien. Este libro hace hincapié en esta diferencia, entre la programación y la ingeniería de software. ¿Cómo pueden gestionar los ingenieros de software una base de código viva que evoluciona y responde a requisitos y demandas cambiantes a lo largo de su vida? A partir de su experiencia en Google, los ingenieros de software Titus Winters y Hyrum Wright, junto con el escritor técnico Tom Manshreck, presentan una mirada sincera y perspicaz sobre cómo construyen y mantienen el software algunos de los principales profesionales del mundo. Este libro trata de la cultura, los procesos y las herramientas de ingeniería exclusivas de Google, y de cómo estos aspectos contribuyen a la eficacia de una organización de ingeniería de software. Explorará tres principios fundamentales que las organizaciones de software deben tener en cuenta a la hora de diseñar, establecer la arquitectura, escribir y mantener el código: - Cómo afecta el tiempo a la sostenibilidad del software y cómo hacer que su código resista el paso del tiempo. - Cómo afecta la escala a la viabilidad de las prácticas de software dentro de una organización de ingeniería de software. - Qué contrapartidas debe tener en cuenta el ingeniero de software al evaluar las decisiones de diseño y los desarrollos.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 1261

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Ingeniería de softwareen Google

Lecciones sobre programación aprendidasa lo largo del tiempo

Organizadas por Titus Winters,Tom Manshreck y Hyrum Wright

Ingeniería de software en Google

Lecciones sobre programación aprendidasa lo largo del tiempo

Organizadas por Titus Winters,Tom Manshreck y Hyrum Wright

Edición original publicada en inglés por O’Reilly con el título Software Engineering at Google, ISBN 978-1-492-08279-8 © 2020 Google, LLC.

This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same.

Título de la edición en español:

Ingeniería de software en Google

Primera edición en español, 2022

© 2022 MARCOMBO, S.L.

www.marcombo.com

Diseño de portada: Karen Montgomery

Ilustración: Rebecca Demarest

Traducción: Francisco Martínez Carreño

Corrección: Mónica Muñoz Marinero

Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. La presente publicación contiene la opinión del autor y tiene el objetivo de informar de forma precisa y concisa. La elaboración del contenido, aunque se ha trabajado de forma escrupulosa, no puede comportar una responsabilidad específica para el autor ni el editor de los posibles errores o imprecisiones que pudiera contener la presente obra.

ISBN: 978-84-267-3487-7

Producción del ePub: booqlab

Contenidos

Prólogo

Prefacio

Parte I. Tesis

1. ¿Qué es la ingeniería de software?

Tiempo y cambio

Ley de Hyrum

Ejemplo: ordenación hash

¿Por qué no aspirar a que «nada cambie»?

Escala y eficiencia

Políticas que no escalan

Políticas que escalan adecuadamente

Ejemplo: actualización del compilador

Desplazamiento hacia la izquierda

Contrapartidas y costes

Ejemplo: rotuladores

Aportaciones a la toma de decisiones

Ejemplo: compilaciones distribuidas

Ejemplo: decidir entre tiempo y escala

Revisar decisiones, cometer errores

Ingeniería de software frente a programación

Conclusión

Resumen

Parte II. Cultura

2. Cómo trabajar bien en equipo

Ayúdeme a ocultar mi código

El mito del genio

La ocultación se considera perjudicial

Detección temprana

El factor autobús

Ritmo del progreso

En resumen, no se esconda

Todo es cuestión de equipo

Los tres pilares de la interacción social

¿Por qué importan estos pilares?

Humildad, respeto y confianza en la práctica

Cultura post mortem sin sentimiento de culpa

Ser Googley

Conclusión

Resumen

3. Compartir conocimientos

Desafíos para el aprendizaje

Filosofía

Preparación del escenario: seguridad psicológica

Tutoría

Seguridad psicológica en grupos grandes

Aumente sus conocimientos

Haga preguntas

Comprenda el contexto

Escalado de las preguntas: pregunte a la comunidad

Chats de grupo

Listas de correo electrónico

YAQS: plataforma de preguntas y respuestas

Escalado del conocimiento: siempre hay algo que enseñar

Horas de oficina

Charlas y clases de tecnología

Documentación

Código

Escalado de los conocimientos de la organización

Cultivar la cultura de compartir el conocimiento

Establecimiento de fuentes canónicas de información

Manténgase al día

Legibilidad: tutorías estandarizadas a través de la revisión del código

¿Qué es el proceso de legibilidad?

¿Por qué someterse a este proceso?

Conclusión

Resumen

4. Ingeniería para la equidad

Los prejuicios son la norma

Comprensión de la necesidad de la diversidad

Desarrollo de capacidades multiculturales

Hacer que se pueda procesar la diversidad

Rechazo de enfoques singulares

Desafío a los procesos establecidos

Valores frente a resultados

Mantener la curiosidad, seguir adelante

Conclusión

Resumen

5. Cómo liderar un equipo

Gerentes y líderes en tecnología (y ambos)

El gerente de ingeniería

El líder en tecnología

El gerente líder de tecnología

Pasar de la función de colaborador individual a la función de liderazgo

Lo único que hay que temer es…, bueno, todo

Liderazgo de servicio

El gerente de ingeniería

«Gerente» es una palabra de cuatro letras

El gerente de ingeniería en la actualidad

Antipatrones

Antipatrón: contratar a personas fáciles de manejar

Antipatrón: ignorar a las personas de bajo rendimiento

Antipatrón: ignorar los problemas de carácter personal

Antipatrón: ser amigo de todos

Antipatrón: comprometer el listón de contratación

Antipatrón: tratar al equipo como si fueran niños

Patrones positivos

Perder el ego

Ser un maestro zen

Ser catalizador

Eliminar obstáculos

Ser maestro y mentor

Establecer metas claras

Ser honesto

Rastrear la satisfacción

La pregunta inesperada

Otros consejos y trucos

Las personas somos como las plantas

Motivación intrínseca frente a motivación extrínseca

Conclusión

Resumen

6. Liderazgo a escala

Siempre hay que decidir

La parábola del aeroplano

Identificación de las orejeras

Señalar las contrapartidas clave

Decidir y, después, repetir

Siempre hay que dejar solo al equipo

Su misión: formar a un equipo «autónomo»

División del espacio del problema

Siempre hay que mantenerse escalando

El ciclo del éxito

Lo importante frente a lo urgente

Aprender a dejar caer pelotas al suelo

Proteja su energía

Conclusión

Resumen

7. Medición de la productividad de la ingeniería

¿Por qué debemos medir la productividad de la ingeniería?

Triaje: ¿vale la pena medirlo?

Selección de métricas significativas con objetivos y señales

Objetivos

Señales

Métricas

Uso de datos para validar métricas

Actuar y realizar un seguimiento de los resultados

Conclusión

Resumen

Parte III. Procesos

8. Guías de estilo y normas

¿Por qué tenemos normas?

Creación de normas

Principios rectores

Guía de estilo

Cambio de las normas

El proceso

Árbitros de estilo

Excepciones

Orientación

Aplicación de las normas

Comprobadores de errores

Formateadores de código

Conclusión

Resumen

9. Revisión del código

Flujo de revisión del código

Cómo funciona la revisión de código en Google

Beneficios de la revisión de código

Corrección de código

Comprensión de código

Coherencia del código

Beneficios psicológicos y culturales

Compartir conocimientos

Mejores prácticas de la revisión de código

Sea cortés y profesional

Escriba cambios pequeños

Escriba descripciones de los cambios que tengan calidad

Mantenga al mínimo el número de revisores

Automatizar donde sea posible

Tipos de revisiones de código

Revisiones del código greenfield

Cambios de comportamiento, mejoras y optimizaciones

Corrección de errores y reversiones

Refactorizaciones y cambios a gran escala

Conclusión

Resumen

10. Documentación

¿Qué calificar como «documentación»?

¿Por qué es necesaria la documentación?

La documentación es como el código

Conozca a su audiencia

Tipos de audiencias

Tipos de documentación

Documentación de referencia

Documentos de diseño

Tutoriales

Documentación conceptual

Páginas de destino

Revisiones de la documentación

Filosofía de la documentación

QUIÉN, QUÉ, CUÁNDO, DÓNDE y POR QUÉ

El principio, la parte central y el final

Parámetros de la documentación de calidad

Documentación obsoleta

¿Cuándo necesita a escritores técnicos?

Conclusión

Resumen

11. Descripción general de las pruebas

¿Por qué escribimos pruebas?

La historia de Google Web Server

Pruebas al ritmo del desarrollo moderno

Escribir, ejecutar, reaccionar

Ventajas de probar el código

Diseño de un conjunto de pruebas

Tamaño de las pruebas

Alcance de las pruebas

Regla de Beyoncé

Nota sobre la cobertura del código

Pruebas a escala de Google

Dificultades de un gran conjunto de pruebas

Historial de pruebas en Google

Clases de orientación

Pruebas certificadas

Pruebas en los aseos

La cultura de pruebas actualmente

Límites de las pruebas automatizadas

Conclusión

Resumen

12. Pruebas unitarias

Importancia del mantenimiento

Prevención de pruebas frágiles

Esfuércese en lograr pruebas que no cambien

Pruebas a través de API públicas

Comprobar el estado, no las interacciones

Escriba pruebas claras

Haga que las pruebas sean completas y concisas

Pruebe los comportamientos, no los métodos

No ponga la lógica en las pruebas

Escriba mensajes de error claros

Pruebas y uso compartido de código: DAMP, no DRY

Valores compartidos

Configuración compartida

Helpers compartidos y validación

Definición de infraestructura de pruebas

Conclusión

Resumen

13. Dobles de pruebas

El impacto de los dobles de pruebas en el desarrollo del software

Dobles de pruebas en Google

Conceptos básicos

Un ejemplo de dobles de pruebas

Costuras

Marcos de trabajo de simulación

Técnicas para utilizar los dobles de pruebas

Simulación

Stubbing

Pruebas de interacción

Implementaciones reales

Preferencia de las implementaciones reales a las aisladas

Cómo decidir cuándo utilizar una implementación real

Simulación

¿Por qué son importantes las simulaciones?

¿Cuándo deben escribirse las simulaciones?

Fidelidad de las simulaciones

Las simulaciones se deben probar

¿Qué hacer si no hay una simulación disponible?

Stubbing

Los peligros de abusar del stubbing

¿Cuándo es adecuado utilizar stubbing?

Pruebas de interacción

Preferencia de las pruebas de estado a las pruebas de interacción

¿Cuándo son apropiadas las pruebas de interacción?

Mejores prácticas para las pruebas de interacción

Conclusión

Resumen

14. Pruebas más grandes

¿Qué son las «pruebas más grandes»?

Fidelidad

Brechas frecuentes en las pruebas unitarias

¿Por qué no realizar pruebas más grandes?

Pruebas de gran tamaño en Google

Pruebas más grandes y el tiempo

Pruebas más grandes a escala de Google

Estructura de una prueba grande

El sistema bajo prueba

Datos para la prueba

Verificación

Tipos de pruebas más grandes

Prueba funcional de uno o más binarios interactivos

Pruebas de los navegadores y dispositivos

Pruebas de rendimiento, carga y estrés

Pruebas de la configuración de implementación

Pruebas exploratorias

Pruebas de regresión de diferencias A/B

UAT

Sistemas de sondeo y análisis canario

Recuperación en caso de catástrofe e ingeniería del caos

Evaluación al usuario

Grandes pruebas y el flujo de trabajo del desarrollador

Creación de pruebas grandes

Ejecución de pruebas grandes

Propiedad de las pruebas grandes

Conclusión

Resumen

15. Depreciación

¿Por qué hacer depreciación?

¿Por qué es tan difícil la depreciación?

Depreciación durante el diseño

Tipos de depreciación

Depreciación recomendada

Depreciación obligatoria

Advertencias de depreciación

Gestión del proceso de depreciación

Propietarios de los procesos

Hitos

Depreciación de las herramientas

Conclusión

Resumen

Parte IV. Herramientas

16. Control de versiones y gestión de ramas

¿Qué es el «control de versiones»?

¿Por qué es importante el control de versiones?

VCS centralizado frente a VCS distribuido

Fuente de verdad

Control de versiones frente a gestión de dependencias

Gestión de ramas

El trabajo en curso es similar a una rama

Ramas de desarrollo

Ramas de versión

Control de versiones en Google

One-Version

Escenario: varias versiones disponibles

La norma «One-Version»

Casi sin ramas longevas

¿Qué pasa con las ramas de versión?

Monorepos

Futuro del control de versiones

Conclusión

Resumen

17. Code Search

La interfaz de usuario de Code Search

¿Cómo utilizan los Googlers Code Search?

¿Dónde?

¿Qué?

¿Cómo?

¿Por qué?

¿Quién y cuándo?

¿Por qué una herramienta web independiente?

Escala

Vista global del código sin necesidad de configuración

Especialización

Integración con otras herramientas para desarrolladores

Presentacición de las API

Impacto de la escala en el diseño

Latencia de la consulta de búsqueda

Latencia del índice

Implementación de Google

Índice de búsqueda

Clasificación

Selección de contrapartidas

Completitud: repositorio en head

Completitud: todos los resultados frente a los más relevantes

Completitud: head versus ramas versus toda la historia versus espacios de trabajo

Expresividad: token frente a subcadena frente a expresión regular

Conclusión

Resumen

18. Sistemas de compilación y filosofía de la compilación

Propósito de un sistema de compilación

¿Qué sucede si no existe un sistema de compilación?

Pero todo lo que necesito ¡es un compilador!

¿Scripts de shell al rescate?

Sistemas de compilación modernos

Todo se trata de dependencias

Sistemas de compilación basados en tareas

Sistemas de compilación basados en artefactos

Compilaciones distribuidas

Tiempo, escala, contrapartidas

Tratamiento de los módulos y las dependencias

La utilización de módulos detallados y la regla 1:1:1

Minimización de la visibilidad de los módulos

Gestión de dependencias

Conclusión

Resumen

19. Critique, herramienta de revisión del código de Google

Principios de las herramientas de revisión del código

Flujo de revisión de código

Notificaciones

Nivel 1: realización de un cambio

Diferenciaciones

Resultados del análisis

Integración estricta de herramientas

Nivel 2: revisión de la solicitud

Niveles 3 y 4: comprensión y comentarios del cambio

Comentarios

Comprensión del estado de un cambio

Nivel 5: aprobación del cambio (puntuación del cambio)

Nivel 6: confirmación del cambio

Después de la confirmación: seguimiento del historial

Conclusión

Resumen

20. Análisis estático

Características del análisis estático eficaz

Escalabilidad

Usabilidad

Lecciones clave para hacer que el análisis estático funcione

Céntrese en la satisfacción de los desarrolladores

Haga que el análisis estático forme parte del flujo principal de trabajo del desarrollador

Permita la contribución de los usuarios

Tricorder: plataforma de análisis estático de Google

Herramientas integradas

Canales de retroalimentación integrados

Sugerencias de correcciones

Personalización por proyectos

Presubmits

Integración en el compilador

Análisis durante la edición y navegación por el código

Conclusión

Resumen

21. Gestión de dependencias

¿Por qué resulta tan difícil la gestión de dependencias?

Requisitos conflictivos y dependencias de diamante

Importación de dependencias

Promesas de compatibilidad

Consideraciones al hacer la importación

Cómo gestiona Google la importación de dependencias

Gestión de dependencias, en teoría

Nada cambia (también conocido como el «modelo de dependencias estáticas»)

Versionado semántico

Modelos de distribución por paquetes

Live at Head

Limitaciones de SemVer

SemVer puede que asegure una compatibilidad superior a la real

SemVer podría exagerar

Motivaciones

Minimum Version Selection

Entonces, ¿funciona SemVer?

Gestión de dependencias con recursos infinitos

Exportación de dependencias

Conclusión

Resumen

22. Cambios a gran escala

¿Qué es un «cambio a gran escala»?

¿Quién negocia con las LSC?

Obstáculos a los cambios atómicos

Limitaciones técnicas

Fusión de conflictos

Sin cementerios encantados

Heterogeneidad

Pruebas

Revisión de código

Infraestructura LSC

Políticas y cultura

Comprensión de la base de código

Gestión de cambios

Pruebas

Soporte del lenguaje

El proceso LSC

Autorización

Creación de cambios

Fragmentación y envío

Limpieza

Conclusión

Resumen

23. Integración continua

Conceptos de IC

Bucles de retroalimentación rápida

Automatización

Prueba continua

Desafíos de la IC

Pruebas herméticas

Integración continua en Google

Caso de estudio de IC: Google Takeout

Pero no puedo permitirme IC

Conclusión

Resumen

24. Entrega continua

Modismos de entrega continua en Google

La rapidez es un deporte de equipo: cómo dividir una implementación en partes manejables

Evaluación de cambios en el aislamiento: funciones de protección de banderas

La lucha por conseguir agilidad: configuración de un tren de lanzamiento

Ningún binario es perfecto

Cumpla con su fecha límite de lanzamiento

Calidad y enfoque en el usuario: envíe solo lo que se utilice

Desplazamiento a la izquierda: tomar antes decisiones basadas en los datos

Cambio de la cultura del equipo: creación de disciplina en el despliegue

Conclusión

Resumen

25. La computación como servicio

Dominio del entorno informático

Automatización del trabajo

Contenerización y tenencia múltiple

Resumen

Escritura de software para la computación gestionada

Arquitectura para el fracaso

Trabajos por lotes frente a trabajos de servicio

Gestión del estado

Conexión a un servicio

Código único

CaaS con el tiempo y la escala

Los contenedores como abstracción

Un servicio para gobernarlos a todos

Configuración enviada

Elección de un servicio informático

Centralización frente a personalización

Nivel de abstracción: sin servidores

Público versus privado

Conclusión

Resumen

Parte V. Conclusión

Epílogo

Índice

Prólogo

Siempre me han fascinado infinitamente los detalles de cómo hace Google las cosas.

He preguntado a mis amigos de Google para conseguir información sobre cómo funcionan realmente las cosas dentro de la empresa.

¿Cómo gestionan el depósito del código monolítico y masivo sin caerse?

¿Cómo colaboran con éxito decenas de miles de ingenieros en miles de proyectos?

¿Cómo mantienen la calidad de sus sistemas?

Trabajar con antiguos empleados de Google solo ha aumentado mi curiosidad. Si alguna vez ha trabajado con un exingeniero de Google (o «Xoogler», como se lo llama a veces), sin duda habrá escuchado la frase «en Google nosotros…». Salir de Google a otras empresas parece ser una experiencia impactante, al menos desde el lado de la ingeniería. Por lo que este forastero puede decir, los sistemas y procesos para escribir código en Google deben estar entre los mejores del mundo, dada la escala de la empresa y la frecuencia con la que la gente canta sus alabanzas.

En la ingeniería de software en Google, un conjunto de Googlers (y algunos Xooglers) nos brindan un plan extenso para muchas de las prácticas, herramientas e incluso elementos culturales que subyacen en la ingeniería de software en Google. Es fácil centrarse en las increíbles herramientas que Google ha creado para ayudar a escribir código, y este libro ofrece muchos detalles sobre esas herramientas. Pero también va más allá de la simple descripción de las herramientas, para ofrecernos la filosofía y los procesos que siguen los equipos de Google. Estos se pueden adaptar a una variedad de circunstancias, tenga o no la escala y las herramientas. Para mi deleite, hay varios capítulos en los que se profundiza en varios aspectos de las pruebas automatizadas, un tema que continúa encontrando demasiada resistencia en nuestra industria.

Lo mejor de la tecnología es que nunca hay una sola forma de hacer algo. En cambio, existen una serie de contrapartidas que todos debemos tener en cuenta, dependiendo de las circunstancias de nuestro equipo y la situación. ¿Qué podemos obtener de forma económica del código abierto? ¿Qué puede construir nuestro equipo? ¿Qué tiene sentido apoyar para nuestra escala? Cuando interrogaba a mis amigos Googlers, quería que me hablaran del mundo en el extremo de la escala: rico en recursos, tanto en talento como en dinero, con grandes exigencias en el software que se crea.

Esta información anecdótica me dio ideas sobre algunas opciones que, de otro modo, no habría considerado.

Con este libro, hemos escrito esas opciones para que todos las lean. Por supuesto, Google es una empresa única y sería una tontería suponer que la forma correcta de administrar su organización de ingeniería de software es copiar con precisión su fórmula. Aplicado de manera práctica, este libro le dará ideas sobre cómo se pueden hacer las cosas y mucha información que puede utilizar para reforzar sus argumentos para adoptar las mejores prácticas como las pruebas, la compartición de conocimientos y la creación de equipos colaborativos.

Es posible que nunca tenga usted que construir algo parecido a Google, y que ni siquiera desee recurrir en su organización a las mismas técnicas que ellos aplican. Pero, si no está familiarizado con las prácticas que ha desarrollado Google, se está perdiendo una perspectiva sobre la ingeniería de software que proviene de decenas de miles de ingenieros que han trabajado en colaboración en el software durante más de dos décadas. Ese conocimiento es demasiado valioso para ignorarlo.

—Camille Fournier

Autora, The Manager’s Path

Prefacio

Este libro se titula Ingeniería de software en Google. ¿Qué entendemos exactamente por «ingeniería de software»? ¿Qué distingue a la «ingeniería de software» de la «programación» o la «informática»? ¿Y por qué Google tendría una perspectiva única que añadir al corpus de bibliografía previo sobre ingeniería de software escrita durante los últimos cincuenta años?

Las expresiones «programación» e «ingeniería de software» se han utilizado de manera intercambiable durante bastante tiempo en nuestra industria, aunque cada término tiene un énfasis diferente y diversas implicaciones. Los estudiantes universitarios tienden a estudiar Ciencias de la computación y consiguen trabajo escribiendo código como «programadores».

La «ingeniería de software», sin embargo, suena más seria, como si implicara la aplicación de algunos conocimientos teóricos para construir algo real y preciso. Los ingenieros mecánicos, los ingenieros civiles, los ingenieros aeronáuticos y los de otras disciplinas de la ingeniería practican la ingeniería. Todos trabajan en el mundo real y utilizan la aplicación de sus conocimientos teóricos para crear algo real. Los ingenieros de software también crean «algo real», aunque es menos tangible que las cosas que crean otros ingenieros.

A diferencia de las profesiones de ingeniería más establecidas, la teoría o práctica actual de la ingeniería de software no es tan rigurosa. Los ingenieros aeronáuticos deben seguir pautas y prácticas rígidas, porque los errores en sus cálculos pueden causar daños reales; la programación, en general, no ha seguido tradicionalmente prácticas tan rigurosas. Pero, a medida que el software se integra más en nuestras vidas, debemos adoptar y confiar en métodos de ingeniería más rigurosos. Esperamos que este libro ayude a otros a ver un camino hacia prácticas de software más confiables.

Programación a lo largo del tiempo

Proponemos que la «ingeniería de software» abarque no solo el acto de escribir código, sino todas las herramientas y procesos que utiliza una organización para construir y mantener ese código a lo largo del tiempo. ¿Qué prácticas puede introducir una organización de software que mejor mantengan su valioso código a largo plazo? ¿Cómo pueden los ingenieros hacer más sostenible la base de código y más rigurosa la propia disciplina de la ingeniería del software? No tenemos respuestas esenciales a estas preguntas, pero esperamos que la experiencia colectiva de Google durante las últimas dos décadas ilumine posibles caminos para encontrar esas respuestas.

Una idea clave que compartimos en este libro es que la ingeniería de software se puede considerar como una «programación integrada a lo largo del tiempo». ¿Qué prácticas podemos introducir en nuestro código para hacerlo sostenible, capaz de reaccionar ante los cambios necesarios, a lo largo de su ciclo de vida, desde su concepción hasta su introducción, pasando por su mantenimiento y su eliminación?

En el libro, se hace hincapié en tres principios fundamentales que, en nuestra opinión, las organizaciones de software deberían tener en cuenta a la hora de diseñar, crear la arquitectura y escribir su código:

Tiempo y cambio

Cómo deberá adaptarse el código a lo largo de su vida.

Escala y crecimiento

Cómo deberá adaptarse una organización a medida que evoluciona.

Contrapartidas y costes

Cómo una organización toma decisiones, basándose en las lecciones del tiempo y el cambio, la escala y el crecimiento.

A lo largo de los capítulos, hemos tratado de enlazar con estos temas y señalar las formas en que tales principios afectan a las prácticas de ingeniería y permiten que sean sostenibles (véase el capítulo 1 para un análisis más completo).

Perspectiva de Google

Google tiene una perspectiva única sobre el crecimiento y la evolución de un ecosistema de software sostenible, derivado de nuestra escala y longevidad. Esperamos que las lecciones que hemos aprendido sean útiles a medida que su organización evolucione y adopte prácticas más sostenibles.

Dividimos los temas de este libro en tres aspectos principales del panorama de la ingeniería de software de Google:

• Cultura

• Procesos

• Herramientas

La cultura de Google es única, pero las lecciones que hemos aprendido en el desarrollo de nuestra cultura de ingeniería son de amplia aplicación. En los capítulos sobre la cultura (parte II), se destaca la naturaleza colectiva de una empresa de desarrollo de software, que el desarrollo de software es un esfuerzo de equipo y que los principios culturales adecuados son esenciales para que una organización crezca y se mantenga en buen funcionamiento.

Las técnicas descritas en los capítulos de «Procesos» (parte II) resultan familiares para la mayoría de los ingenieros de software, pero la base de código de gran tamaño y larga duración de Google proporciona una prueba de esfuerzo más completa para desarrollar las mejores prácticas. En esos capítulos, hemos intentado enfatizar lo que hemos descubierto que funciona a lo largo del tiempo y a escala, así como identificar áreas en las que aún no tenemos respuestas satisfactorias.

Por último, los capítulos dedicados a las herramientas (parte IV) ilustran el modo en que aprovechamos nuestras inversiones en la infraestructura de herramientas para proporcionar beneficios a la base de código, a medida que crece y envejece. En algunos casos, estas herramientas son específicas de Google, aunque indicamos las alternativas de código abierto o de terceros cuando corresponde. Esperamos que estos conocimientos básicos se apliquen a la mayoría de las organizaciones de ingeniería.

La cultura, los procesos y las herramientas que se describen en este libro describen las lecciones que, con suerte, un ingeniero de software típico aprende en el trabajo. Ciertamente, Google no tiene el monopolio de los buenos consejos, y nuestras experiencias, presentadas aquí, no pretenden dictar lo que su organización debe hacer. Este libro es nuestra perspectiva, pero esperamos que le resulte útil, ya sea adoptando estas lecciones directamente o utilizándolas como punto de partida a la hora de considerar sus propias prácticas, especializadas para su propio ámbito de problemas.

Esta obra tampoco pretende ser un sermón. El propio Google todavía aplica de forma imperfecta muchos de los conceptos de estas páginas. Las lecciones que hemos aprendido las hemos asimilado a través de nuestros fracasos: todavía cometemos errores, implementamos soluciones imperfectas y necesitamos repetir para mejorar. Sin embargo, el gran tamaño de la organización de ingeniería de Google garantiza que exista una diversidad de soluciones para cada problema. Esperamos que este libro contenga lo mejor de ese grupo.

Lo que no es este libro

Este libro no pretende contener el diseño de software, una disciplina que requiere su propio libro (y para el que ya existe mucho contenido). Aunque en la obra hay algo de código con fines ilustrativos, los principios son neutrales en cuanto al lenguaje y hay pocos consejos de «programación» en estos capítulos. Como resultado, este texto no trata muchos temas importantes en el desarrollo de software: administración de proyectos, diseño de application programming interfaces (API), fortalecimiento de la seguridad, internacionalización, marcos de interfaz de usuario u otras inquietudes específicas del lenguaje. Su omisión en este libro no implica su falta de importancia. En cambio, optamos por no incluirlos aquí sabiendo que no podríamos brindarles el tratamiento que merecen. Hemos intentado que las discusiones en este libro sean más sobre ingeniería y menos sobre programación.

Comentarios de despedida

Este texto ha sido un trabajo hecho con amor por parte de todos quienes han contribuido, y esperamos que lo reciba tal como se entrega: como una ventana que permite ver cómo una gran organización de ingeniería de software crea sus productos. También esperamos que sea una de las muchas voces que ayuden a que nuestra industria adopte prácticas más progresistas y sostenibles. Más importante aún, esperamos que disfrute de su lectura y pueda adaptar algunas de sus lecciones a sus propias inquietudes.

‒ Tom Manshreck

Convenciones utilizadas en el libro

Este elemento significa una nota general.

Aprendizaje online

Durante muchos años, O’Reilly Media y Marcombo han proporcionado la máxima formación en tecnología y negocios, conocimientos y perspectivas para ayudar a las empresas a tener éxito.

Nuestra red única de expertos y personal innovador comparte su conocimiento y experiencia a través de libros, artículos y distintas plataformas de aprendizaje en línea. Por ejemplo, la plataforma de aprendizaje en línea de O’Reilly le brinda acceso bajo demanda a cursos de capacitación en vivo, rutas de aprendizaje en profundidad, entornos de codificación interactivos y una amplia colección de texto y vídeos de O’Reilly y más de doscientos editores. Para más información, por favor, visite http://oreilly.com.

Tenemos una página web para este libro, donde enumeramos erratas, ejemplos y cualquier información adicional. Puede acceder a esta página en https://oreil.ly/software-engineering-at-google.

Reconocimientos

Un libro como este no sería posible sin el trabajo de muchas personas. Todo el conocimiento contenido en este libro nos ha llegado a todos a través de la experiencia de tantos otros en Google a lo largo de nuestras carreras. Somos los mensajeros; otros llegaron antes que nosotros, a Google y a otros lugares, y nos enseñaron lo que ahora les presentamos. No podemos enumerarlos a todos aquí, pero deseamos expresar nuestros reconocimientos.

También nos gustaría agradecerle a Melody Meckfessel su apoyo a este proyecto en sus inicios, así como a Daniel Jasper y Danny Berlin, por apoyarlo hasta su finalización.

Este libro no habría sido posible sin el enorme esfuerzo de colaboración de los organizadores, autores y editores. Aunque a los autores y editores se los reconoce específicamente en cada capítulo o rótulo, nos gustaría dedicar tiempo para reconocer a aquellos que contribuyeron a cada capítulo proporcionando comentarios, discusiones y reseñas para la reflexión.

•¿Qué es la ingeniería de software?: Sanjay Ghemawat, Andrew Hyatt

•Trabajar bien en equipo: Sibley Bacon, Joshua Morton

•Compartir conocimientos: Dimitri Glazkov, Kyle Lemons, John Reese, David Symonds, Andrew Trenk, James Tucker, David Kohlbrenner, Rodrigo Damazio Bovendorp

•Ingeniería para la equidad: Kamau Bobb, Bruce Lee

•Cómo liderar un equipo: Jon Wiley, Laurent Le Brun

•Liderazgo a escala: Bryan O’Sullivan, Bharat Mediratta, Daniel Jasper, Shaindel Schwartz

•Medición de la productividad de la ingeniería: Andrea Knight, Collin Green, Caitlin Sadowski, Max-Kanat Alexander, Yilei Yang

•Guía de estilo y normas: Max Kanat-Alexander, Titus Winters, Matt Austern, James Dennett

•Revisión del código: Max Kanat-Alexander, Brian Ledger, Mark Barolak

•Documentación: Jonas Wagner, Smit Hinsu, Geoffrey Romer

•Descripción general de las pruebas: Erik Kuefler, Andrew Trenk, Dillon Bly, Joseph Graves, Neal Norwitz, Jay Corbett, Mark Striebeck, Brad Green, Miško Hevery, Antoine Picard, Sarah Storck

•Examen de la unidad: Andrew Trenk, Adam Bender, Dillon Bly, Joseph Graves, Titus Winters, Hyrum Wright, Augie Fackler

•Dobles de pruebas: Joseph Graves, Gennadiy Civil

•Pruebas más grandes: Adam Bender, Andrew Trenk, Erik Kuefler, Matthew Beaumont-Gay

•Depreciación: Greg Miller, Andy Shulman

•Control de versiones y gestión de ramas: Rachel Potvin, Victoria Clarke

•Búsqueda de código: Jenny Wang

•Sistemas de compilación y filosofía de la compilación: Hyrum Wright, Titus Winters, Adam Bender, Jeff Cox, Jacques Pienaar

•Critique, herramienta de revisión del código de Google: Mikołaj Dądela, Hermann Loose, Eva May, Alice Kober-Sotzek, Edwin Kempin, Patrick Hiesel, Ole Rehmsen, Jan Macek

•Análisis estático: Jeffrey van Gogh, Ciera Jaspan, Emma Söderberg, Edward Aftandilian, Collin Winter, Eric Haugh

•Gestión de las dependencias: Russ Cox, Nicholas Dunn

•Cambios a gran escala: Matthew Fowles Kulukundis, Adam Zarek

•Integración continua: Jeff Listfield, John Penix, Kaushik Sridharan, Sanjeev Dhanda

•Entrega continua: Dave Owens, Sheri Shipe, Bobbi Jones, Matt Duftler, Brian Szuter

•La computación como servicio: Tim Hockin, Collin Winter, Jarek Kuśmierek

Además, nos gustaría expresar nuestro agradecimiento a Betsy Beyer, por compartir su conocimiento y experiencia al publicar el libro original Site Reliability Engineering, que hizo que nuestra experiencia fuera mucho más fluida. Christopher Guzikowski y Alicia Young de O’Reilly hicieron un trabajo increíble, al lanzar y dirigir este proyecto hasta su publicación.

Los organizadores también quieren mostrar particularmente su agradecimiento a las siguientes personas:

Tom Manshreck: a mi madre y a mi padre, por hacerme creer en mí mismo y por trabajar conmigo en la mesa de la cocina para hacer los deberes.

Titus Winters: a papá, por mi camino; a mamá, por mi voz; a Victoria, por mi corazón; a Raf, por darme la espalda. También al señor Snyder, Ranwa, Z, Mike, Zach, Tom (y todos los Paynes), mec, Toby, cgd y Melody, por las lecciones, la tutoría y la confianza.

Hyrum Wright: a mamá y papá, por sus ánimos; a Bryan y los habitantes de Bakerland, por mi primera incursión en el software; a Dewayne, por continuar ese viaje; a Hannah, Jonathan, Charlotte, Spencer y Ben, por su amor e interés; a Heather, por estar ahí, a pesar de todo.

CAPÍTULO 1

¿Qué es la ingeniería de software?

Autor: Titus WintersEditor: Tom Manshreck

Nada está construido en piedra; todo está construido sobre arena, pero debemos construir como si la arena fuera piedra.

—Jorge Luis Borges

Podemos distinguir tres diferencias críticas entre la programación y la ingeniería de software: el tiempo, la escala y las contrapartidas que entran en juego. En un proyecto de ingeniería de software, lo que más debe preocupar a los ingenieros es el transcurso del tiempo y la eventual necesidad de cambios. En una organización de ingeniería de software, nos deben preocupar, en mayor medida, la escala y la eficiencia, tanto en lo que se refiere al software que creamos como a la organización encargada de crearlo. Por último, como ingenieros de software, se nos pide que adoptemos decisiones cada vez más complejas y cuyos resultados son progresivamente de mayor trascendencia, frecuentemente en función de estimaciones imprecisas en lo que respecta al tiempo y el crecimiento.

En Google, decimos a veces que «la ingeniería de software es la programación integrada que se alcanza con el transcurso del tiempo». La programación es, sin duda, una parte importante de la ingeniería de software: después de todo, la programación es, antes que nada, la forma de generar nuevo software. Si aceptamos esta distinción, también queda claro que es posible que necesitemos delimitar entre tareas de programación (desarrollo) y tareas de ingeniería de software (desarrollo, modificación y mantenimiento). La incorporación del factor tiempo añade una importante y nueva dimensión a la programación. Los cubos no son cuadrados; la distancia no es la velocidad. La ingeniería de software no es programación.

Una forma de apreciar el impacto del tiempo en un programa es pensar en la pregunta: «¿Cuál es el tiempo de vida útil esperado1 de nuestro código?». Las respuestas razonables a esta pregunta pueden variar aproximadamente en un factor de 100 000. Es tan razonable pensar en un código que debe durar unos minutos como imaginar un código cuyo tiempo de vida durará décadas. Generalmente, el código que se sitúa a corto plazo de ese espectro no se ve afectado por el factor tiempo. Es poco probable que necesitemos adaptarnos a una nueva versión de las bibliotecas subyacentes, del sistema operativo (SO), del hardware, o a la versión del lenguaje, en el caso de un programa cuya utilidad dura solo una hora. Estos sistemas de corta duración son, efectivamente, un problema «solo» de programación, de la misma manera que un cubo al que se comprime lo suficiente para reducir una de sus dimensiones a cero se convierte en un cuadrado. A medida que ampliamos ese tiempo para permitir una vida útil más prolongada, el cambio adquiere una mayor importancia. En un lapso de una década o más, la mayoría de las dependencias de los programas, ya sean implícitas o explícitas, probablemente cambiarán. Este reconocimiento está en la raíz de la distinción que hacemos entre ingeniería de software y programación.

Esta diferenciación está en el centro de lo que llamamos sostenibilidad del software. El proyecto es sostenible si, durante el tiempo de vida útil previsto del software, somos capaces de reaccionar ante cualquier cambio importante que se produzca, ya sea por motivos técnicos o empresariales. Es importante destacar que solo buscamos tener la capacidad de actuar, ya que se puede optar por no realizar una determinada actualización, bien por falta de valor o debido a otras prioridades2. Cuando somos absolutamente incapaces de reaccionar ante un cambio en la tecnología subyacente o en la orientación del producto, estamos haciendo una apuesta de alto riesgo con la esperanza de que dicho cambio nunca llegue a ser crítico. Para proyectos a corto plazo, esa podría ser una apuesta segura. Para los que duran varias décadas, posiblemente no3.

Otra forma de ver la ingeniería de software es teniendo en cuenta la escala. ¿Cuántas personas están involucradas? ¿Qué papel desempeñan en el desarrollo y mantenimiento a lo largo del tiempo? Una tarea de programación es, a menudo, un acto de creación individual, pero una tarea de ingeniería de software supone un esfuerzo de equipo. Un primer intento de definir la ingeniería de software dio lugar a una buena definición desde este punto de vista: «El desarrollo de programas de múltiples versiones en los que intervienen varias personas4». Esto sugiere que la diferencia entre la ingeniería de software y la programación es una cuestión tanto de tiempo como de personas. La colaboración en equipo suscita nuevos problemas, pero también proporciona más potencial que cualquier programador individual para crear programas eficaces.

La organización del equipo, la composición del proyecto y las políticas y prácticas de un proyecto de software dominan este aspecto de la complejidad de la ingeniería de software. Dichos problemas son inherentes a la escala: a medida que la organización crece y sus proyectos se amplían, ¿se vuelve más eficiente en la producción de software? El flujo de trabajo de desarrollo, ¿es más eficiente a medida que crecemos, o nuestras políticas de control de versiones y estrategias de pruebas son proporcionalmente más costosas? Los problemas de escala sobre la comunicación y el aumento de los recursos humanos se han discutido desde el inicio de la ingeniería de software, a partir de la aparición del libro Mythical Man Month5. Estos problemas de escala son, a menudo, asuntos concernientes a las políticas y se refieren, fundamentalmente, a la cuestión de la sostenibilidad del software: ¿cuánto costará hacer las cosas que tenemos que hacer de forma repetida?

También podemos decir que la ingeniería de software es diferente de la programación en términos de la complejidad de las decisiones que deben adoptarse y de lo que está en juego. En ingeniería de software nos vemos obligados, frecuentemente, a evaluar las contrapartidas entre varios caminos que seguir, a veces con mucho en juego y, a menudo, con métricas de valor imperfectas. El trabajo de un ingeniero de software, o un responsable de ingeniería de software, consiste en aspirar a la sostenibilidad y a la gestión de los costes de escalado para la organización del producto y del flujo de trabajo de desarrollo. Con esas aportaciones en mente, evaluamos las contrapartidas y adoptamos decisiones racionales. A veces, podemos aplazar los cambios relativos al mantenimiento o incluso adoptar políticas que no se adaptan bien, sabiendo que tendremos que revisar esas decisiones. Esas opciones deben ser explícitas y estar claras en lo que se refiere a los costes diferidos.

En ingeniería de software, rara vez existe una solución única para todos, y el mismo principio se aplica a este libro. Dado un factor de 100 000 para respuestas razonables sobre «¿cuánto tiempo de vida tendrá este software?», un rango de quizá un factor de 10 000 para «¿cuántos ingenieros hay en la organización?» y quién sabe qué rango para «¿cuántos recursos de cálculo hay disponibles para el proyecto?», la experiencia de Google probablemente no coincidirá con la suya. Nuestro objetivo, en este libro, es presentar lo que hemos descubierto que funciona en la creación y el mantenimiento de un software que esperamos dure décadas, con decenas de miles de ingenieros y recursos informáticos que se encuentran repartidos por todo el mundo. La mayoría de las prácticas que consideramos necesarias a esa escala también funcionarán adecuadamente para proyectos más pequeños: lo podemos considerar como una referencia sobre un ecosistema de ingeniería que creemos que podría ser bueno a medida que escalamos. En algunos lugares, la escala muy grande tiene sus propios costes y nos complacería no tener que pagar gastos generales adicionales. Los citamos como advertencia. Con suerte, si su organización crece lo suficiente como para preocuparse por esos costes, podrá encontrar una respuesta mejor.

Antes de abordar los detalles sobre el trabajo en equipo, la cultura, las políticas y las herramientas, analicemos primero los temas fundamentales del tiempo, la escala y las contrapartidas.

Tiempo y cambio

Cuando un principiante está aprendiendo a programar, el tiempo de vida útil del código resultante se mide, generalmente, en horas o días. Las tareas de programación y los ejercicios tienden a escribirse una vez, con poca o ninguna refactorización y, ciertamente, sin la necesidad de mantenimiento a largo plazo. Estos programas, a menudo, no se reconstruyen ni se ejecutan después de su elaboración inicial. Este hecho no es sorprendente en un entorno pedagógico. Quizá en la educación secundaria o posterior, podemos encontrar algún curso sobre proyectos en equipo o una tesis práctica. De ser así, es probable que estos proyectos sean el único momento en el que el tiempo de vida del código de los estudiantes sea de aproximadamente un mes o más. Es posible que esos desarrolladores necesiten refactorizar algún código, tal vez como respuesta a cambios en los requisitos, pero es poco probable que se les pida que se ocupen de cambios más extensos en su entorno.

También encontramos desarrolladores de código de corta duración en configuraciones industriales habituales. Las aplicaciones móviles, a menudo, tienen un tiempo de vida útil bastante corto6 y, para bien o para mal, es relativamente frecuente la reescritura de todo. Los ingenieros que se encuentran en su etapa inicial pueden optar, con razón, por centrarse en los objetivos inmediatos, en lugar de hacerlo en las inversiones a largo plazo: es posible que la empresa no viva lo suficiente para cosechar los beneficios de una inversión en infraestructura que se amortiza lentamente. Es razonable pensar que un desarrollador de startups en serie pueda tener diez años de experiencia en desarrollo y poca o ninguna experiencia en el mantenimiento de cualquier software que se espera que exista durante más de uno o dos años.

En el otro extremo del espectro, algunos proyectos de éxito tienen un tiempo ilimitado de vida útil: no podemos pronosticar, de forma razonable, un punto final para Google Search, el kernel de Linux o el proyecto del servidor HTTP Apache. Para la mayoría de los proyectos de Google, debemos asumir que tendrán un tiempo de vida indefinido; no podemos pronosticar cuándo dejaremos de tener la necesidad de actualizar nuestras dependencias o versiones del lenguaje. A medida que aumentan sus tiempos de vida, estos proyectos de larga duración acaban teniendo un aire diferente al de las tareas de programación o al de desarrollo de nuevas empresas.

Consideremos la figura 1.1, en la que se muestran dos proyectos de software situados en los extremos opuestos de este espectro de «tiempo de vida útil esperado». Para un programador que trabaja en una tarea con un tiempo de vida útil previsto de horas, ¿qué tipo de mantenimiento es razonable esperar? Es decir, si aparece una nueva versión del sistema operativo mientras está trabajando en una secuencia de comandos de Python que se ejecutará una vez, ¿debería dejar lo que está haciendo y actualizarlo? Por supuesto que no: la actualización no es crítica. Pero, en el extremo opuesto del espectro, que Google Search esté atascada en una versión de nuestro sistema operativo de los años noventa sería claramente un problema.

Figura 1.1.Tiempo de vida útil e importancia de las actualizaciones

Los puntos bajo y alto en el espectro del tiempo de vida esperado sugieren que hay una transición en algún lugar. En un sitio determinado, a lo largo de la línea entre un programa único y un proyecto que dura décadas, ocurre una transición: un proyecto debe comenzar a reaccionar a factores externos cambiantes7. Para cualquier proyecto para el que no hemos planeado actualizaciones desde el principio, esa transición es, probablemente, muy penosa por tres razones, cada una de las cuales agrava a las demás:

• Llevamos a cabo una tarea que aún no se ha realizado para este proyecto; se han incorporado más supuestos que estaban ocultos.

• Es poco probable que los ingenieros que intentan realizar la actualización tengan experiencia en este tipo de tareas.

• El tamaño de la actualización suele ser mayor de lo habitual, y se realizan de una sola vez actualizaciones correspondientes a varios años, en lugar de llevar a cabo una actualización más gradual.

Y, por lo tanto, después de pasar una vez por una actualización de este tipo (o abandonar a medio camino), es bastante razonable sobrestimar el coste de hacer una actualización posterior y, en consecuencia, decidir no repetir «nunca más». Las empresas que llegan a esta conclusión, o bien terminan comprometiéndose a simplemente tirar cosas y reescribir el código, o bien deciden no volver a actualizar. En lugar de adoptar un enfoque natural de evitar una tarea dolorosa, a veces la respuesta más responsable es invertir en hacerla menos penosa. Todo depende del coste de la actualización, el valor que proporciona y el tiempo de vida útil esperado del proyecto en cuestión.

Superar no solo esa primera gran actualización, sino llegar al punto en el que el proyecto pueda mantenerse actualizado de manera confiable en el futuro, es su esencia de la sostenibilidad a largo plazo. La sostenibilidad requiere planificar y gestionar el impacto de los cambios necesarios. Para muchos proyectos en Google, creemos que hemos logrado este tipo de sostenibilidad, en gran parte a través del método de prueba y error.

Entonces, concretamente, ¿en qué se diferencia la programación a corto plazo de la generación de código con un tiempo de vida útil esperado mucho más largo? Con el paso del tiempo, debemos ser mucho más conscientes de la diferencia entre «funciona» y «se puede mantener». No existe una solución perfecta para identificar estos problemas. Esto es lamentable, porque mantener el software a largo plazo es una batalla constante.

Ley de Hyrum

Si estamos manteniendo un proyecto que utilizan otros ingenieros, la lección más importante sobre «funciona» versus «se puede mantener» es lo que hemos venido en llamar la ley de Hyrum: «Con un número suficiente de usuarios de una API, no importa lo que prometa en el contrato: todos los comportamientos observables de su sistema dependerán de alguien».

Según nuestra experiencia, este axioma es un factor dominante en cualquier debate sobre el cambio de software a lo largo del tiempo. Es conceptualmente similar a la entropía: en los debates sobre el cambio y el mantenimiento en el tiempo, se debe tener en cuenta la ley de Hyrum8, al igual que, en los debates sobre la eficiencia o la termodinámica, se debe tener en cuenta la entropía. El hecho de que la entropía nunca disminuya no significa que no debamos intentar ser eficientes. El hecho de que la ley de Hyrum se aplique al mantenimiento del software no significa que no podamos planificarlo o intentar comprenderlo mejor. Podemos mitigarla, pero sabemos que nunca se podrá erradicar.

La ley de Hyrum representa el conocimiento práctico de que, incluso con las mejores intenciones, los mejores ingenieros y concienzudas prácticas para la revisión de código, no podemos asumir la adhesión perfecta a los contratos publicados o a las mejores prácticas. Como propietario de una API, obtendrá algo de flexibilidad y libertad, al ser claro sobre las promesas de la interfaz, pero, en la práctica, la complejidad y dificultad de un determinado cambio también depende de lo útil que encuentren los usuarios algún comportamiento observable de su API. Si los usuarios no pueden confiar en tales cosas, su API será fácil de cambiar. Con suficiente tiempo y los suficientes usuarios, incluso el cambio más inocuo romperá algo9. El análisis que usted haga del valor de ese cambio debe incorporar la dificultad de investigar, identificar y resolver esas roturas.

Ejemplo: ordenación hash

Consideremos el ejemplo de la ordenación de la iteración de hash. Si insertamos cinco elementos en un conjunto basado en hash, ¿en qué orden los sacamos?

La mayoría de los programadores saben que las tablas hash no están ordenadas de forma obvia. Pocos conocen los detalles de si la tabla hash concreta que están usando tiene la intención de proporcionar ese orden en particular para siempre. Esto puede parecer normal, pero, durante la última década o dos, la experiencia de la industria de la computación en el uso de estos tipos ha evolucionado:

• Los ataques por inundaciones de hash10 proporcionan un mayor incentivo para la iteración de hash no determinista.

• Los beneficios en cuanto a la eficiencia derivados de la investigación de algoritmos hash mejorados o contenedores hash requieren cambios en el orden de iteración hash.

• Según la ley de Hyrum, los programadores escribirán programas que dependan del orden en que se recorre una tabla hash, si tienen la posibilidad de hacerlo.

Como resultado, si le pregunta a cualquier experto «¿puedo asumir una secuencia de salida concreta para mi contenedor de hash?», ese experto presumiblemente responderá: «No». En general, eso es correcto, pero quizá simplista. Una respuesta más matizada es: «Si tu código es de corta duración, sin cambios en el hardware, ni en el tiempo de ejecución del lenguaje o en la elección de la estructura de datos, esa suposición está bien. Si no sabe cuánto tiempo vivirá su código, o no puede prometer que nada de lo que dependa de usted cambiará, tal suposición es incorrecta». Además, incluso si su propia implementación no depende del orden del contenedor hash, la podría utilizar otro código que implícitamente crea tal dependencia; por ejemplo, si la biblioteca pone en serie valores como respuesta a una llamada a procedimiento remoto (RPC), el programa que llama a RPC podría terminar dependiendo del orden de esos valores.

Este es un ejemplo muy básico de la diferencia entre «funciona» y «es correcto». Para un programa de corta duración, dependiendo del orden de iteración sobre sus contenedores, no causará ningún problema técnico. Para un proyecto de ingeniería de software, sin embargo, esa dependencia de un orden definido es un riesgo; si se da el tiempo suficiente, algo hará que sea beneficioso cambiar ese orden de iteración. Ese beneficio puede manifestarse de varias maneras, ya sea en lo que se refiere a la eficiencia, la seguridad o, simplemente, la protección de la estructura de datos para permitir futuros cambios. Cuando ese beneficio esté claro, deberá sopesar las contrapartidas entre ese beneficio y el pesar de romper con sus desarrolladores o clientes.

Algunos lenguajes aleatorizan específicamente el ordenamiento del hash entre versiones de bibliotecas o incluso entre ejecuciones del mismo programa en un intento de evitar dependencias. Pero, incluso así, todavía puede haber algunas sorpresas relacionadas con la ley de Hyrum: hay un código en el que se usa el orden de iteración hash como un generador ineficiente de números aleatorios. Eliminar tal aleatoriedad ahora invalidaría a esos usuarios. Así como la entropía aumenta en todos los sistemas termodinámicos, la ley de Hyrum se aplica a todos los comportamientos observables.

Pensando en las diferencias entre el código escrito con una mentalidad de «funciona ahora» y la otra de «funciona indefinidamente», podemos extraer algunas relaciones claras. Si consideramos el código como un artefacto con el requisito de un tiempo de vida útil (muy) variable, podemos comenzar a categorizar los estilos de programación: el código que depende de características frágiles e inéditas de sus dependencias probablemente se describa como «sucio» o «inteligente», mientras que el código que sigue las mejores prácticas y se ha planificado para el futuro es más probable que se describa como «limpio» y «susceptible de mantenimiento». Ambos tienen sus propósitos, pero el que seleccionemos depende, fundamentalmente, del tiempo de vida útil esperado del código en cuestión. Se dice que es «programación si “inteligente” es un cumplido, pero es ingeniería de software si “inteligente” es una acusación».

¿Por qué no aspirar a que «nada cambie»?

Implícita en toda esta discusión sobre el tiempo y la necesidad de reaccionar al cambio está la suposición de que el cambio podría ser necesario. ¿Lo es?

Como ocurre con todo lo demás en este libro, depende. Nos comprometemos fácilmente con «la mayoría de los proyectos, durante un periodo de tiempo suficientemente largo, pero es posible que sea necesario cambiar todo lo que hay debajo». Si tenemos un proyecto escrito en C puro, sin dependencias externas (o solo dependencias externas que prometen una gran estabilidad a largo plazo, como POSIX), es posible que podamos evitar cualquier forma de refactorización o actualización difícil. C hace un gran trabajo, al proporcionar estabilidad; en muchos aspectos, ese es su propósito fundamental.

La mayoría de los proyectos están expuestos, sobre todo, a cambios en la tecnología subyacente. La mayor parte de lenguajes de programación, así como los tiempos de ejecución, cambian mucho más de lo que lo hace C. Incluso las bibliotecas implementadas en C puro pueden cambiar para admitir nuevas funciones, lo que puede afectar a los usuarios intermedios. Los problemas de seguridad se revelan en todo tipo de tecnologías, desde procesadores hasta bibliotecas de redes y códigos de aplicaciones. Cada elemento de tecnología de la que depende su proyecto tiene algún riesgo (con suerte pequeño) de contener errores críticos y vulnerabilidades de seguridad que podrían salir a la luz solo después de que haya empezado a confiar en ella. Si no puede implementar un parche para Heartbleed (http://heartbleed.com) o atenuar problemas especulativos de ejecución, como Meltdown y Spectre (https://meltdownattack.com), porque ha asumido (o prometido) que nada cambiará nunca, esa es una apuesta considerable.

Las mejoras en la eficiencia complican aún más el panorama. Queremos equipar nuestros centros de datos con equipos informáticos rentables, especialmente para mejorar la eficiencia de las unidades centrales de procesamiento (CPU). Sin embargo, los algoritmos y las estructuras de datos de los primeros tiempos de Google son, sencillamente, menos eficientes en los equipos modernos: una lista enlazada o un árbol de búsqueda binario seguirán funcionando bien, pero la brecha cada vez mayor entre los ciclos de CPU y la latencia de la memoria impacta en la imagen de lo que sería el código «eficiente». Con el tiempo, el beneficio de la actualización a un hardware nuevo puede disminuir si no se producen cambios de diseño en el software. La compatibilidad con versiones anteriores asegura que los sistemas antiguos sigan funcionando, pero eso no garantiza que las antiguas optimizaciones sigan siendo útiles. Si no queremos o no podemos aprovechar tales oportunidades, corremos el riesgo de incurrir en importantes costes. Problemas de eficiencia como este son particularmente sutiles: el diseño original podría haber sido perfectamente lógico y seguir las mejores y razonables prácticas. Solo después de una evolución de cambios compatibles con versiones anteriores, una opción nueva y más eficiente se convierte en una necesidad importante. No se cometieron errores, pero fue el paso del tiempo lo que hizo que el cambio fuera beneficioso.

Preocupaciones como las que acabamos de mencionar explican por qué existen grandes riesgos para los proyectos a largo plazo en los que no se ha invertido en sostenibilidad. Debemos ser capaces de responder a este tipo de problemas y aprovechar estas oportunidades, independientemente de si nos afectan directamente o se manifiestan solo en el cierre por transición de la tecnología que utilizamos como base. El cambio no es inherentemente bueno. No deberíamos cambiar solo por cambiar. Pero necesitamos ser capaces de cambiar. Si admitimos esa eventual necesidad, también deberíamos considerar la posibilidad de invertir para hacer que esa capacidad sea barata. Como todo administrador de sistemas sabe, una cosa es saber, en la teoría, qué se puede recuperar de la cinta y otra es saber, en la práctica, exactamente cómo hacerlo y cuánto costará cuando sea necesario. La práctica y la experiencia son grandes impulsores de la eficiencia y la fiabilidad.

Escala y eficiencia

Como se indica en el libro Site Reliability Engineering (SRE)11, el sistema de producción de Google en su conjunto se encuentra entre las máquinas más complejas creadas por la humanidad. La complejidad inherente a la construcción de una máquina de este tipo, así como a la capacidad de mantenerla funcionando sin problemas, ha requerido innumerables horas de pensamiento, discusión y rediseño por parte de expertos en toda nuestra organización y en todo el mundo; así que ya hemos escrito un libro sobre la complejidad de mantener esa máquina funcionando a esa escala.

Gran parte de este libro se centra en la complejidad de la escala de la organización que produce dicha máquina y los procesos que usamos para mantenerla en funcionamiento a lo largo del tiempo. Consideremos nuevamente el concepto de sostenibilidad de la base de código: «La base de código de su organización es sostenible cuando puede cambiar todas las cosas que debe cambiar, de forma segura, y puede hacerlo durante todo el tiempo de vida útil de la base de código». En el debate sobre la capacidad se esconde uno de los costes: si cambiar algo supone un coste excesivo, es probable que se posponga. Si los costes crecen con el tiempo por encima de una función lineal, es evidente que la operación no es escalable12. A la larga, el tiempo se impondrá y surgirá algo inesperado que será necesario cambiar. Cuando su proyecto duplique su alcance y necesite realizar esa tarea de nuevo, ¿supondrá el doble de mano de obra? ¿Tendrá los recursos humanos necesarios para abordar el problema?

Los costes en recursos humanos no son el único medio limitado que necesita escalar. Así como el software en sí necesita escalar adecuadamente con recursos tradicionales como computación, memoria, almacenamiento y ancho de banda, el desarrollo de ese software también debe escalar, tanto en términos de participación de tiempo de los recursos humanos como de los recursos de computación, que impulsan el flujo de trabajo de desarrollo. Si el coste de cómputo para su cluster de prueba crece por encima de una función lineal, consumiendo más recursos de cómputo por persona cada trimestre, está en un camino insostenible y necesita hacer cambios rápidamente.

Por último, el activo más valioso de una organización de software, la base de código en sí, también debe escalar. Si su sistema de compilación o un sistema de control de versiones escala con el tiempo por encima de una función lineal, tal vez como resultado del crecimiento y de un historial de registros de cambios cada vez mayor, podría llegar a un punto en el que, sencillamente, no pueda continuar. Preguntas como «¿cuánto tiempo se tarda en hacer una compilación completa?», «¿cuánto tiempo se tarda en obtener una copia nueva del repositorio?» o «¿cuánto costará actualizar a una nueva versión de lenguaje?» no se supervisan de forma activa y cambian a un ritmo lento. Puede ocurrir como en la metáfora de la rana hervida (https://oreil.ly/clqZN). Es demasiado fácil que los problemas empeoren lentamente y nunca se manifiesten como un momento singular de crisis. Solo con la concienciación y el compromiso de toda la organización en cuanto al escalado es probable que se mantenga al tanto de estas cuestiones.

Todo aquello en lo que su organización confía para crear y mantener el código debe ser escalable en términos de coste global y consumo de recursos. En particular, todo lo que su organización debe hacer repetidamente ha de ser escalable en términos de esfuerzo humano. Muchas políticas comunes no parecen ser escalables en este sentido.

Políticas que no escalan

Con un poco de práctica, resulta más fácil detectar las políticas con malas propiedades de escalado. Por lo general, estas se pueden identificar considerando el trabajo encomendado a un solo ingeniero e imaginando que la organización aumentará entre diez y cien veces su tamaño. Cuando seamos 10 veces más grandes, ¿añadiremos 10 veces más trabajo con el que nuestro ingeniero de muestra tenga que seguir el ritmo? ¿Aumenta en función del tamaño de la organización la cantidad de trabajo que debe realizar nuestro ingeniero? ¿Se incrementa el trabajo con el tamaño de la base de código? Si alguna de las respuestas es afirmativa, ¿disponemos de algún mecanismo para automatizar u optimizar ese trabajo? Si no es así, tenemos problemas de escala.

Considere un enfoque tradicional de la depreciación. Discutiremos sobre la depreciación a fondo en el capítulo 15, pero el enfoque común de la depreciación sirve como un gran ejemplo de problemas de escala. Se ha desarrollado un nuevo widget. Se adopta la decisión de que todos deben usar el nuevo y dejar de utilizar el anterior. Para motivar esta decisión, los líderes del proyecto dicen: «Eliminaremos el widget anterior el 15 de agosto; asegúrese de utilizar el nuevo widget».

Este tipo de enfoque puede funcionar en una configuración de software pequeña, pero falla pronto, a medida que aumenta la profundidad y la amplitud del gráfico de dependencias. Los equipos dependen de un número cada vez mayor de widgets, y una sola interrupción de la compilación puede afectar a un porcentaje creciente de los equipos de la empresa. Resolver estos problemas de manera escalable significa cambiar la forma en la que tratamos la depreciación: en lugar de trasladar el trabajo de migración a los usuarios, los equipos lo pueden hacer internamente ellos mismos, con las economías de escala que eso conlleva.

En 2012, intentamos poner fin al problema de la depreciación con reglas que reducen la rotación: los equipos de infraestructura deben hacer el trabajo para que sus usuarios internos se muevan a las nuevas versiones por sí mismos o realizar la actualización in situ, de manera compatible con las versiones anteriores. Esta política, a la que hemos llamado «regla de la rotación», escala mejor: ya no hay que hacer un esfuerzo cada vez mayor para mantener los proyectos dependientes. También hemos aprendido que tener un grupo dedicado de expertos permite ejecutar las escalas de cambio mejor que pedir más esfuerzo de mantenimiento a cada usuario: los expertos dedican algún tiempo a aprender todo el problema en profundidad y, luego, aplican esa experiencia a cada parte del problema. Obligar a los usuarios a responder a la rotación significa que cada equipo afectado hace un peor trabajo de puesta en marcha, resuelven el problema inmediato y luego desechan ese conocimiento que ya les resulta inútil. La experiencia escala mejor.

El uso tradicional de divisiones de desarrollo es otro ejemplo de políticas que lleva incorporados problemas de escala. Una organización podría identificar que la fusión de grandes características en el tronco ha desestabilizado el producto y concluir: «Necesitamos controles más estrictos sobre cuándo se fusionan las cosas. Deberíamos hacer las fusiones con menos frecuencia». Esto conduce, inmediatamente, a que cada equipo o cada función tengan divisiones de desarrollo separadas. Siempre que se decide que una división está «completa», se prueba y se fusiona con el tronco, lo que genera un trabajo potencialmente costoso para otros ingenieros que aún trabajan en su división de desarrollo, manifestándose en forma de resincronización y pruebas. Este tipo de gestión de ramas puede funcionar para una pequeña organización que tenga entre cinco y diez ramas de esta clase. Como el tamaño de una organización (y el número de divisiones) aumenta, rápidamente se hace evidente que estamos incurriendo en una cantidad cada vez mayor de gastos generales para realizar la misma tarea. Necesitaremos un enfoque diferente a medida que escalamos; lo discutimos en el capítulo 16.

Políticas que escalan adecuadamente

¿Qué tipo de políticas mejoran los costes a medida que crece la organización? O, mejor aún, ¿qué tipo de políticas podemos implementar que proporcionen valor por encima de una función lineal a medida que la organización crece?