El impacto real: de veinte minutos a segundos
De dónde salen los segundos cuando un pipeline de CI de veinte minutos se resuelve casi al instante: solo se ejecuta la porción que de verdad cambió, y esa porción se sirve desde el trabajo que hizo cualquier otra máquina antes. La tasa de aciertos es la métrica que manda, y el reencuadre profundo es que el build deja de escalar con el tamaño del repo para escalar con el tamaño del cambio.
La promesa concreta del remote caching es asombrosa dicha en voz alta: un pipeline de CI que tardaba veinte minutos se resuelve en segundos cuando el noventa por ciento del repositorio no cambió. No es exageración de folleto, es aritmética, y sale de multiplicar dos efectos. Primero, solo se ejecuta la porción que de verdad cambió; el resto son aciertos. Segundo, esos aciertos vienen del trabajo que hizo cualquier máquina antes —un compañero esta mañana, la rama principal ayer, otro PR hace un rato—, no solo el tuyo. El resultado es un CI cuyo coste deja de depender del tamaño del repo y pasa a depender del tamaño del cambio.
- Descomponer de dónde salen los segundos: cómputo de lo afectado por aciertos compartidos.
- Reconocer la tasa de aciertos como la métrica que gobierna el beneficio.
- Ser honesto sobre cuándo la caché no ayuda o incluso hace daño.
- Reencuadrar el build como un coste que escala con el cambio, no con el tamaño.
De dónde salen los segundos
El colapso de veinte minutos a segundos es el producto de dos mecanismos que se multiplican. El primero viene del grafo de tareas: cuando cambias una línea en un paquete, solo ese paquete y los que dependen de él tienen entradas nuevas y, por tanto, hashes nuevos; todo lo demás conserva su hash. El segundo es el remote caching: para todos esos hashes que no cambiaron, la salida ya existe en la caché compartida, subida por alguna ejecución anterior, y se restaura en milisegundos en lugar de recomputarse.
Lo decisivo es que los dos mecanismos se multiplican, no se suman. El grafo reduce el trabajo a la fracción afectada; la caché compartida hace que esa fracción, además, se sirva desde el trabajo de cualquiera. Uno solo de los dos ya ayuda —el grafo sin caché remota ahorra en tu máquina, la caché remota sin grafo comparte builds completas—, pero juntos producen el salto de orden de magnitud, porque atacan dos redundancias distintas a la vez: la de recomputar lo intacto y la de no compartir lo ya computado.
Es útil ver los dos efectos como dos filtros en serie sobre el trabajo total. El primer filtro, el grafo, deja pasar solo las tareas afectadas por el cambio. El segundo filtro, la caché compartida, intercepta de entre las restantes las que ya computó cualquiera y las resuelve sin trabajo. Lo que sobrevive a ambos filtros —tareas afectadas y nunca antes computadas con estas entradas— es lo único que de verdad se ejecuta, y suele ser una porción diminuta del total.
La intuición aritmética es directa. Si de cien tareas noventa son aciertos que se restauran casi al instante y solo diez se ejecutan de verdad, el tiempo de pared no colapsa exactamente al diez por ciento —el camino crítico y la descarga de artefactos cuestan algo— pero sí a un orden de magnitud menor. La porción que cambió suele ser pequeña, y los aciertos son casi gratis, así que la suma tiende a segundos. Turborepo lo corona con un mensaje inequívoco cuando todo fue acierto.
Tasks: 42 successful, 42 total
Cached: 42 cached, 42 total
Time: 2.1s >>> FULL TURBO
Ese FULL TURBO es el estado en que ni una sola tarea se ejecutó: el pipeline entero se resolvió consultando la caché. Y lo decisivo es que no hace falta que tú hayas compilado antes. Como el hash direcciona por contenido, el acierto puede venir del trabajo de cualquiera que compartiera ese contenido. La primera máquina que compila una versión siembra la caché; todas las demás —personas y jobs— cosechan. El beneficio, por tanto, compone con el tamaño del equipo: cuanta más gente converge sobre la misma base, más alta es la probabilidad de que lo que necesitas ya esté hecho.
Un ejemplo con números redondos fija la magnitud. Supón un monorepo de cien paquetes cuya build en frío tarda veinte minutos. Un PR toca un paquete hoja del que dependen otros dos: cambian tres paquetes, noventa y siete conservan su hash. Esos noventa y siete se restauran en un par de segundos de descarga y descompresión; los tres afectados se compilan en el tiempo que les corresponda, quizá medio minuto. El pipeline cierra en torno al medio minuto en vez de veinte, no porque nada se hiciera, sino porque casi nada hacía falta que se hiciera.
El suelo real de ese colapso es el camino crítico: la cadena de dependencias más larga entre las tareas que sí cambiaron. Por muchos aciertos que tengas, no puedes ir más rápido que la secuencia de trabajo genuinamente nuevo que no se puede paralelizar. Por eso el diez por ciento que cambia no se traduce en el diez por ciento del tiempo, sino en algo cercano al coste del camino crítico del cambio más la descarga del resto; y por eso mantener los cambios pequeños y poco encadenados no solo es buena higiene de PR, también es lo que deja lucir a la caché.
Solo lo afectado
El grafo marca qué cambió; solo esos paquetes y sus dependientes tienen hash nuevo y se ejecutan. El resto conserva su hash.
Acierto compartido
Los aciertos vienen del trabajo de cualquiera: un compañero, la rama principal, otro PR. No hace falta que compilaras tú.
Restaurar es casi gratis
Un acierto descarga y descomprime en milisegundos frente a los minutos de compilar. Ese contraste es lo que colapsa el tiempo.
flowchart TB cambio[Un cambio toca 1 paquete] --> grafo[El grafo marca lo afectado] grafo --> afectado[10 por ciento hashes nuevos] grafo --> intacto[90 por ciento mismo hash] afectado --> corre[se ejecuta de verdad] intacto --> hit[acierto remoto en milisegundos] corre --> total[Tiempo total en segundos] hit --> total style intacto fill:#a6e3a1,color:#11111b style hit fill:#a6e3a1,color:#11111b style afectado fill:#f9e2af,color:#11111b style total fill:#89b4fa,color:#11111b
Conviene desmontar una confusión habitual: el remote caching no hace que la primera build sea rápida, hace que las siguientes lo sean. Alguien tiene que pagar el coste completo una vez para sembrar la caché con cada artefacto; a partir de ahí, todos los demás cosechan. Por eso el beneficio no se mide en una ejecución aislada, sino en el agregado del equipo: cuantas más personas y más jobs convergen sobre una misma base de código, más sube la probabilidad de que cualquier tarea concreta ya esté sembrada por alguien. Es un efecto de red clásico, la caché vale más cuanta más gente la usa, porque cada corrida de cualquiera la enriquece para todos.
Ese efecto de red tiene una consecuencia estratégica: el valor de la caché no es lineal en el tamaño del equipo, es superlineal. Cada persona que se suma no solo se beneficia de lo que ya hay, también aporta sus builds al pozo común del que beben los demás. Un equipo de cinco comparte cinco fuentes de siembra; uno de cincuenta, cincuenta. Por eso el remote caching, que en un proyecto individual es una comodidad agradable, en una organización grande se vuelve una infraestructura de la que es imposible prescindir sin volver a la edad de piedra de los veinte minutos.
La métrica que manda: la tasa de aciertos
Todo el valor del sistema se condensa en un número: la tasa de aciertos, la fracción de tareas que se resuelven desde la caché en vez de ejecutarse. Es la métrica que hay que vigilar por encima de cualquier otra, porque gobierna directamente el tiempo que ahorras. Una tasa alta con artefactos pequeños da el sueño de los segundos; una tasa baja significa que estás recomputando de todas formas, y una caché que casi nunca acierta consume ancho de banda y almacenamiento sin devolver tiempo.
| Escenario | Estado de la caché | Tiempo del pipeline |
|---|---|---|
| Primera build de una versión | siembra, sin aciertos | completo, veinte minutos |
| El 90% intacto, caché caliente | mayoría de aciertos remotos | segundos |
| Caché fría por hit rate bajo | pocos aciertos | casi completo, sin beneficio |
La tabla esconde una asimetría importante: la fila del medio, la del sueño de los segundos, no es un regalo, es el resultado de que alguien pagó la fila de arriba. La caché caliente existe porque hubo una siembra fría. Por eso la primera build tras un cambio grande en una dependencia compartida siempre duele —invalida muchos hashes y hay que reconstruir— y por eso conviene programar esas invalidaciones grandes, cuando se pueda, en momentos donde la resiembra no bloquee a todo el equipo a la vez.
Que la tasa de aciertos sea alta no es automático: es la recompensa de la higiene que aprendiste en los niveles previos. Depende de declarar bien las entradas —para que hashes que deberían coincidir coincidan de verdad—, de tener tareas deterministas —para que la misma entrada no genere artefactos distintos que nunca aciertan— y de no meter en el hash ruido que cambia sin motivo. La tasa de aciertos es, en el fondo, un termómetro de lo puras que son tus tareas: sube cuando el build se comporta como una función, y se desploma cuando no.
Pero el hit rate no cuenta la historia entera por sí solo, porque un acierto no es gratis: hay que descargar y descomprimir el artefacto. Por eso la segunda magnitud a vigilar es el tamaño de los artefactos. Una caché con hit rate altísimo pero artefactos de cientos de megabytes puede rendir peor que compilar cuando la red es el cuello de botella. La salud de la caché es, en rigor, el producto de dos factores: con qué frecuencia aciertas y cuánto cuesta cada acierto. Optimizar solo el primero y descuidar el segundo es una trampa que solo se ve midiendo el tiempo real, no la tasa.
Hay una forma barata de arruinar el hit rate sin darse cuenta: introducir una entrada que cambia en cada corrida. Una marca de tiempo en un archivo generado, un identificador de build incrustado, una variable volátil metida en env sin necesidad —cualquiera de ellas reescribe el hash constantemente y convierte cada tarea en un miss perpetuo—. Vigilar la tasa a lo largo del tiempo es, sobre todo, una alarma contra estas regresiones: una caída brusca del hit rate casi siempre delata que alguien, sin querer, ató una tarea a algo que cambia siempre.
Conviene fijar un objetivo, aunque sea aproximado. En un monorepo sano con buena higiene de entradas, la tasa de aciertos en CI para ramas que parten de una base ya construida suele vivir muy arriba: la norma es que casi todo acierte y solo corra lo tocado. Si tu tasa languidece cerca de la mitad, no tienes un problema de caché, tienes un problema de determinismo o de entradas mal declaradas, y el remedio no está en la caché sino en las tareas que la alimentan.
No trates la tasa de aciertos como una curiosidad de la salida de consola: mídela y publícala. Los resúmenes de ejecución te dicen cuántas tareas fueron acierto y cuántas corrieron; agrégalo a lo largo del tiempo y tendrás la salud real de tu caché. Una caída súbita del hit rate es una alarma —alguien introdujo no-determinismo, cambió una entrada global, rompió el aislamiento— tan valiosa como una alerta de tests en rojo, y mucho más fácil de ignorar si no la instrumentas.
Más allá de CI: el bucle de desarrollo
El titular del pipeline de CI es el ejemplo más vistoso, pero el impacto no se queda ahí. El mismo mecanismo acelera el bucle diario de cada desarrollador. Cuando alguien hace pull de la rama principal y ejecuta turbo run build o turbo run test, no recompila lo que el equipo ya construyó: obtiene aciertos remotos de todo lo que no ha cambiado desde su última sincronización, y solo ejecuta la diferencia. El arranque de la mañana deja de ser un café obligatorio esperando a que compile medio repositorio.
Ese bucle interno es, en horas acumuladas, tanto o más valioso que el ahorro de CI, porque ocurre muchas más veces al día y le pasa a cada persona. Cada cambio de rama, cada rebase, cada vuelta al proyecto después de una reunión dispara tareas que, sin caché compartida, se recomputan y, con ella, se restauran. La caché convierte “sincronizar con el equipo” de una operación cara en una casi gratis.
El efecto se nota especialmente en quien vuelve a un proyecto tras una ausencia. Quien retoma una rama parada durante días suele temer el pull porque augura una reconstrucción larga; con caché compartida, esa reconstrucción es casi toda aciertos del trabajo que el equipo hizo entretanto. Reincorporarse deja de tener un peaje de compilación, y esa fricción eliminada, aunque pequeña por vez, es de las que más desaniman cuando se acumulan.
Hay incluso un efecto de segundo orden en la cultura del equipo. Cuando reconstruir es barato, la gente sincroniza más a menudo con la rama principal, porque ya no teme el coste de hacerlo; y sincronizar más a menudo reduce la deriva entre ramas y los conflictos de fusión. Una caché rápida no solo ahorra tiempo de máquina: empuja sutilmente los hábitos hacia integraciones más frecuentes y ramas más cortas, que es justo lo que un monorepo saludable necesita.
El ahorro de CI es visible porque alguien mira el tiempo del pipeline; el del bucle de desarrollo es invisible porque se disuelve en la fluidez del día. Nadie celebra que un pull seguido de un test tarde cinco segundos en vez de tres minutos, pero esa diferencia, multiplicada por cada sincronización de cada persona a lo largo de un año, es probablemente el mayor retorno de toda la inversión en caché. Que no aparezca en ningún panel no lo hace menor; lo hace más fácil de dar por sentado.
Honestidad: cuándo la caché no ayuda o hace daño
Un ingeniero maduro conoce los límites de su herramienta. El remote caching tiene modos en los que no ayuda o incluso perjudica, y ocultarlos sería vender humo. El más grave ya lo viste: un acierto falso por entradas mal declaradas reparte un artefacto obsoleto como si fuera correcto; aquí la caché no te hace más lento, te hace incorrecto, que es infinitamente peor. La corrección va siempre antes que la velocidad. El segundo modo es el coste de red: si tus artefactos son enormes, descargarlos y subirlos por un enlace lento puede tardar más que recompilar; mantén las salidas magras y mide, porque una caché de artefactos gigantes puede añadir tiempo en vez de quitarlo. El tercero es el no-determinismo: una tarea que no es función pura de sus entradas casi nunca acierta, así que pagas el coste de consultar la caché sin cosechar nunca el beneficio.
Hay un cuarto modo, más de operación que de corrección: la caché debe degradar con gracia. Si el almacén remoto está caído o lento, el build tiene que caer de vuelta a compilar en local, no fallar; una caché que tumba el pipeline cuando se cae ha convertido una optimización en un punto único de fallo. Y para los escritores no confiables, los PRs de forks, vale el mismo blindaje del nivel anterior: solo lectura, para que nadie envenene el almacén compartido. Ninguno de estos cuatro modos es razón para renunciar a la caché; son las condiciones de contorno que separan una caché que ayuda de una que estorba, y todas se cumplen con la higiene que ya conoces.
Acierto falso
Entradas mal declaradas sirven un artefacto obsoleto como bueno. No te hace lento, te hace incorrecto. La corrección va primero.
Artefacto pesado
Salidas enormes por una red lenta cuestan más que recompilar. Mantén el outputs magro y mide el tiempo real.
No-determinismo
Una tarea que no es función pura casi nunca acierta. Pagas la consulta a la caché sin cosechar jamás el beneficio.
Ninguno de estos modos invalida la caché, la matizan. El ingeniero que los ignora vende la caché como magia y se lleva un susto en producción; el que los conoce la despliega con las guardas puestas —entradas completas, salidas magras y deterministas, escritores no confiables en solo lectura— y entonces sí cosecha los segundos sin pagar el precio oculto.
Hay un patrón común detrás de los cuatro modos: todos nacen de tratar la caché como una caja mágica en vez de como lo que es, una memoización con supuestos. El acierto falso viola la completitud de las entradas; el artefacto pesado ignora el coste de transporte; el no-determinismo rompe la pureza; la falta de degradación olvida que una optimización no puede ser una dependencia dura. Conocer los supuestos es, exactamente, conocer los modos de fallo, porque cada modo de fallo es un supuesto incumplido.
La trampa emocional del caching es que la velocidad es visible y la incorrección es silenciosa. Un pipeline en FULL TURBO se siente como una victoria aunque esté restaurando artefactos construidos con la variable de entorno equivocada. Por eso la disciplina se ordena así, sin excepción: primero garantizas que la caché es correcta —entradas completas, salidas deterministas, escritores no confiables en solo lectura— y solo después celebras que es rápida. Un build lento y correcto se despliega; uno veloz y envenenado se despliega también, y ahí empieza el desastre.
Aquí converge todo el track, y conviene decirlo en los términos más fuertes posibles. Durante años la industria persiguió compiladores más rápidos: de webpack en JavaScript a esbuild en Go a Rolldown y Oxc en Rust, cada salto redujo los tiempos de build en un orden de magnitud atacando la constante del problema. Pero hay un techo que ninguna reescritura en Rust rebasa, porque el build más rápido no es el más optimizado: es el que no ocurre. El remote caching no mejora la constante; cambia el exponente. Sin caché, el coste de CI escala con el tamaño del repositorio: cuanto más grande el monorepo, más tarda cada build, y ese acoplamiento es lo que hace que los monorepos se vuelvan insostenibles al crecer, porque el tiempo de pared castiga a todos por el código de todos. Con remote caching, el coste deja de escalar con el tamaño del repo y pasa a escalar con el tamaño del cambio —el delta, no el todo—, porque lo intacto no se recomputa, se consulta. Es, literalmente, un cambio de clase de complejidad: de proporcional al repositorio a proporcional a la modificación. Y ese cambio es exactamente lo que vuelve económicamente viable el monorepo a la escala de cientos de ingenieros: el CI ya no se degrada conforme el código crece, porque cada PR solo paga por lo que toca, no por lo que existe. Interiorizar esta jerarquía es lo que separa a quien “hace que compile” de quien diseña el build de un producto que escala con su gente en lugar de asfixiarse con ella: primero módulos rápidos, luego bundlers rápidos, y por encima de todo, no repetir trabajo. La reescritura en Rust hizo que compilar cueste diez veces menos; el remote caching hace que la mayoría de las veces no compiles en absoluto. La lección final de todo el camino de src a producción es la más simple y la más difícil de vivir: la optimización definitiva no es hacer el trabajo más rápido, es no hacerlo dos veces.
- Cronometra tu pipeline de CI con la caché remota vacía; esa es tu línea base sin caché.
- Haz un cambio mínimo en un paquete hoja y vuelve a ejecutarlo con la caché caliente; mide el tiempo y anota cuántas tareas fueron acierto.
- Calcula tu tasa de aciertos real en varios PRs seguidos y compárala con la fracción de paquetes que de verdad cambiaron.
- Provoca un acierto falso a propósito —lee una variable no declarada en
env— y comprueba que la caché sirve la salida equivocada; luego arréglalo declarándola. - Escribe en una frase por qué el remote caching cambia el exponente y no solo la constante del coste de tu build.