Cómo Google crea marcos web

Es de conocimiento público que Google usa un único repositorio para compartir código (las 2 mil millones de líneas) y que usa el paradigma de desarrollo basado en troncales.

Para muchos desarrolladores fuera de la empresa, esto es sorprendente y contrario a la intuición, pero funciona realmente bien. (El artículo vinculado anteriormente ofrece buenos ejemplos, por lo que no los repetiré aquí).

La base de código de Google es compartida por más de 25.000 desarrolladores de software de Google de decenas de oficinas en países de todo el mundo. En un día laboral típico, realizan 16.000 cambios en el código base. (fuente)

Este artículo trata sobre los aspectos específicos de la creación de un marco web de código abierto (AngularDart) en este contexto.

Solo una versión

Cuando empleas el desarrollo basado en troncales en un único repositorio enorme, solo tienes una versión de todo. Eso es algo obvio. Sin embargo, todavía es bueno señalarlo aquí, porque significa que, en Google, no puede tener la aplicación FooBar que use AngularDart 2.2.1 y otra aplicación BarFoo que esté en 2.3.0. Ambas aplicaciones deben tener la misma versión, la más reciente.

Es por eso que los empleados de Google a veces dicen que todo el software de Google vive a la vanguardia.

Si toda tu alma grita '¡peligroso!' ahora mismo, eso es comprensible. Dependiendo del tronco ('maestro' en terminología de git) de una biblioteca con su código de producción seguro que suena peligroso. Pero hay un giro en la trama por delante.

74 mil pruebas por compromiso

AngularDart define 1601 pruebas (aquí). Pero cuando realiza un cambio en el código de AngularDart en el repositorio de Google, también ejecuta pruebas para todos en Google que dependen del marco . Por el momento, son alrededor de 74 mil pruebas (dependiendo de qué tan grande sea su cambio, una heurística omite las pruebas que el sistema sabe que no está afectando).

Es bueno tener más pruebas.

Acabo de hacer un cambio que solo se manifiesta el 5% de las veces, simulando algo así como una condición de carrera en el algoritmo de verificación de reinserción de detección de cambios (agregué && random.nextDouble() >.05 a esta declaración if). No se manifestó en ninguna de las pruebas 1601 cuando las ejecuté (una vez). Pero rompió un montón de pruebas de clientes.

Sin embargo, el valor real aquí es que son pruebas de aplicaciones reales . No solo son numerosos, sino que también reflejan cómo los desarrolladores usan el marco (no solo los autores del marco). Esto es significativo: los propietarios de marcos no siempre estiman correctamente cómo se utiliza su marco.

También ayuda que esas aplicaciones estén en producción y que miles de millones de dólares fluyan a través de ellas cada mes. Existe una gran diferencia entre las aplicaciones de demostración que un autor de framework crea en su tiempo libre y las aplicaciones de producción reales con decenas o cientos de personas-año invertidas en ellas. Para que la web sea relevante en el futuro, debemos apoyar mejor el desarrollo de esta última.

Entonces, ¿qué sucede si el marco rompe algunas de las aplicaciones que se construyen en él?

Lo rompes, lo arreglas

Cuando los autores de AngularDart quieren introducir un cambio importante, tienen que ir y arreglarlo para sus usuarios . Dado que todo en Google se encuentra en un solo repositorio, es trivial averiguar a quién están rompiendo y pueden comenzar a reparar de inmediato.

Cualquier cambio importante en AngularDart también incluye todas las correcciones a ese cambio en todas las aplicaciones de Google que dependen de él. Por lo tanto, la rotura y la corrección ingresan en el repositorio simultáneamente y, por supuesto, después de la revisión adecuada del código por parte de todas las partes afectadas.

Demos un ejemplo concreto. Cuando alguien del equipo de AngularDart realiza un cambio que afecta el código en la aplicación de AdWords, va al código fuente de esa aplicación y lo corrige. Pueden ejecutar las pruebas existentes de AdWords en el proceso y pueden agregar otras nuevas. Luego, ponen todo eso en su lista de cambios y piden una revisión. Dado que su lista de cambios toca el código tanto en el repositorio de AngularDart como en el repositorio de AdWords, el sistema requiere automáticamente la aprobación de revisión de código de ambos equipos. Solo entonces se puede enviar el cambio.

Esto tiene el efecto obvio de prevenir el desarrollo del marco en el vacío. Los desarrolladores de marcos de AngularDart tienen acceso a millones de líneas de código que se crean con su plataforma, y ​​ellos mismos tocan regularmente ese código. No necesitan asumir cómo se usa su marco. (La salvedad obvia es que solo ven el código de Google y no el código de todos los Workivas, Wrikes y StableKernels del mundo que también usan AngularDart).

Tener que actualizar el código de sus usuarios también ralentiza el desarrollo. No tanto como puede pensar (mire el progreso de AngularDart desde octubre), pero aún así ralentiza las cosas. Eso es bueno y malo, dependiendo de lo que desee de un marco. Volveremos a eso.

De todas formas. La próxima vez que alguien en Google diga que una versión alfa de alguna biblioteca es estable y en producción, ahora sabrá por qué.

Cambios a gran escala

¿Qué pasa si AngularDart necesita hacer un cambio importante (digamos, pasar de 2.xa 3.0) y ese cambio rompe 74 mil pruebas? ¿Irá el equipo a arreglarlos todos? ¿Realizarán cambios en miles de archivos de origen, la mayoría de los cuales no son de su autoría?

Si.

Una de las cosas interesantes de tener un sistema de tipos de sonido es que sus herramientas pueden ser mucho más útiles. En Sound Dart, las herramientas pueden estar seguras de que una variable es de cierto tipo, por ejemplo. Para la refactorización, eso significa que muchos cambios pueden ser completamente automáticos, sin necesidad de confirmación por parte del desarrollador.

Cuando un método en la clase Foo cambia de bar()a baz(), puede crear una herramienta que recorra la totalidad del repositorio único de Google, encuentre todas las instancias de esa clase Foo y sus subclases, y cambie todas las menciones de bar()a baz(). Con el sistema de tipo de sonido de Dart, puede estar seguro de que no romperá nada. Sin tipos de sonido, incluso un cambio tan simple puede causarle problemas.

Otra cosa que ayuda con los cambios a gran escala es dart_style, el formateador predeterminado de Dart. Todo el código de Dart en Google se formatea con esta herramienta. Para cuando su código llega a los revisores, se ha formateado automáticamente con dart_style, por lo que no hay argumentos sobre si poner la nueva línea aquí o allá. Y eso también se aplica a los refactores a gran escala.

Métricas de rendimiento

Como dije anteriormente, AngularDart se beneficia de las pruebas de sus dependientes. Pero no son solo pruebas. Google es muy riguroso a la hora de medir el rendimiento de sus aplicaciones, por lo que la mayoría (¿todas?) De las aplicaciones de producción tienen suites de referencia.

Entonces, cuando el equipo de AngularDart introduce un cambio que hace que AdWords sea un 1% más lento de cargar, lo saben antes de realizar el cambio. Cuando el equipo dijo en octubre que las aplicaciones de AngularDart se volvieron un 40% más pequeñas y un 10% más rápidas desde agosto, no estaban hablando de algunas aplicaciones de ejemplo pequeñas sintéticas de TodoMVC. Hablaban de aplicaciones de producción de misión crítica de la vida real con millones de usuarios y megabytes de código de lógica empresarial.

Nota al margen: herramienta de construcción hermética

Quizás se esté preguntando: ¿cómo supo este tipo qué pruebas en el enorme repositorio interno ejecutar después de introducir el error escamoso en AngularDart? Seguramente no estaba eligiendo a mano las 74 mil pruebas, e igualmente seguro que no estaba ejecutando todas las pruebas en Google. La respuesta está en algo llamado Bazel.

A esta escala, no puede tener una serie de scripts de shell para construir cosas. Las cosas serían inestables y prohibitivamente lentas. Lo que necesita es una herramienta de construcción hermética.

“Hermético” en este contexto es muy similar a “puro” en el contexto de funciones. Sus pasos de compilación no pueden tener efectos secundarios (como archivos temporales, cambios en PATH, etc.) y deben ser deterministas (la misma entrada siempre conduce a la misma salida). Cuando ese sea el caso, puede ejecutar las compilaciones y las pruebas en cualquier máquina en cualquier momento y obtendrá resultados consistentes. No es necesario make clean. Por lo tanto, puede enviar sus compilaciones / pruebas para construir servidores y paralelizarlos.

Google ha pasado años desarrollando una herramienta de construcción de este tipo. Fue de código abierto el año pasado como Bazel.

Y gracias a esta pieza de infraestructura, las herramientas de prueba internas pueden determinar qué compilaciones / pruebas afecta cada cambio y ejecutarlas cuando sea apropiado.

Que significa todo esto?

El objetivo explícito de AngularDart es ser el mejor en su clase en productividad, rendimiento y confiabilidad para crear grandes aplicaciones web. Se espera que esta publicación cubra la última parte, la confiabilidad, y por qué es importante que las aplicaciones de Google de misión crítica como AdWords y AdSense estén usando el marco. No es solo el equipo que se jacta de sus usuarios; como se explicó anteriormente, tener grandes usuarios internos hace que AngularDart sea menos probable que introduzca cambios superficiales. Hace que el marco sea más confiable.

Si está buscando un marco que realice revisiones importantes e introduzca funciones importantes cada pocos meses, AngularDart definitivamente no es para usted. Incluso si el equipo quisiera construir el marco de esa manera, creo que está claro en este artículo que no pueden. Creemos sinceramente, sin embargo, que hay espacio para un marco menos moderno pero confiable.

En mi opinión, la mejor predicción del soporte a largo plazo de una pila tecnológica de código abierto es que es una gran parte del negocio del mantenedor principal. Tome Android, dagger, MySQL o git como ejemplos. Es por eso que me alegro de que Dart finalmente tenga un marco web preferido (AngularDart), una biblioteca de componentes preferida (Componentes AngularDart) y un marco móvil preferido (Flutter), todos los cuales se utilizan para crear aplicaciones de Google críticas para el negocio.

[Matan Lurey y Kathy Walrath contribuyeron a este artículo].

[Discutir en Reddit, HN, Twitter.]