Noventa por ciento, y la frase que lo vendió
El setup de Spotify para Claude Code es una entrada de blog de Dimitri Mazmanov, product manager en Spotify, con su código en GitHub: afirma que la configuración que usa su equipo recortó su consumo de tokens en Claude Code un 90%. Su primera línea contiene todo el argumento: la mayor parte de lo que hace un agente de programación con IA no es pensar, es E/S. Leer cinco archivos para responder una pregunta sobre un solo método, o escribir el vigesimoprimer archivo de tests que copia los veinte de al lado, quema miles de tokens con casi nada de razonamiento.
Un tuit llevó la entrada a millón y medio de vistas con una sola frase: las reglas escritas son una sugerencia, un bloqueo no. Hacker News la puso en portada, 271 puntos y 173 comentarios, y la mitad de los comentarios hacían la misma pregunta: ¿el 90% de qué? El matiz que pone el propio Spotify es "lectura masiva". Este artículo reconstruye el setup dentro de Claude Code a secas y después lo mide, para que sepas exactamente qué te compra ese matiz.
Qué es Portal en realidad (y por qué no puedes ejecutarlo)
Portal no es un router. Es el portal interno de desarrollo de Spotify, construido sobre Backstage, la plataforma de desarrollo que Spotify liberó como código abierto. La función relevante dentro de él se llama Modes: según la definición de Spotify, un mode es un agente declarativo que se ejecuta en un runtime efímero, algo así como AWS Lambda para agentes. Escribes las instrucciones, eliges un modelo, fijas una temperatura, adjuntas herramientas. Mazmanov construyó dos, un lector masivo y un escritor de código, ambos sobre Gemini Flash con temperatura 0,2, así que los dos son baratos y aburridos a propósito.
El enrutado vive en un plugin de Claude Code llamado Shunt. Es público en GitHub y se instala con dos comandos. El paso dos, sin embargo, autentica la línea de comandos de Portal contra tu instancia de Portal, y tú no tienes una. El plugin es público; aquello a lo que delega no lo es.
Así que la jugada útil es olvidar el plugin y quedarse con el patrón. Tiene tres capas, en sus propias palabras: hooks, scripts, skills. Cada una tiene un equivalente en Claude Code a secas, y eso es lo que el resto de este artículo construye y mide.
Capa uno: el hook que bloquea en vez de preguntar
La versión 1 del setup era un bloque de reglas de enrutado en el archivo de instrucciones del proyecto. En palabras de Mazmanov, "más o menos funcionaba": las reglas eran orientativas, no obligatorias, Claude podía ignorarlas y cada proyecto necesitaba su propia copia. La versión 2 saca la decisión del prompt y la lleva a la capa de herramientas con dos hooks, ambos disparados antes de una llamada a herramienta. Uno vigila cada lectura de archivo, el otro vigila la shell.
El hook de lectura son 33 líneas de bash. Lee un umbral del entorno, 350 líneas por defecto, y después deja pasar tres cosas:
- Una lectura con offset o límite, porque Claude ya sabe lo que necesita.
- Un archivo que no existe.
- Un archivo en el umbral o por debajo, porque delegar algo pequeño cuesta más que leerlo.
Todo lo demás se bloquea, con un mensaje que Claude lee en lugar del archivo: este archivo tiene tantas líneas, usa el skill de lectura masiva, y si necesitas el contenido exacto para una edición, relee solo esa sección. El hook de shell atrapa cat, head, tail, less y more sobre un archivo grande. Un comando con tubería pasa, porque encadenar hacia grep es una lectura dirigida.
El punto de Mazmanov sobre las capas es el importante: aunque Claude nunca lea la descripción del skill, el hook sigue bloqueando la lectura cara. El skill hace la redirección más suave; el bloqueo la hace real. Un detalle importa más adelante: el script responde con una decisión de nivel superior llamada "block". Guarda esa palabra.
Capas dos y tres: los workers y sus números
Los workers son dos prompts. El lector: "eres un analista de código preciso, devuelve solo viñetas estructuradas, sin saludos, sin prosa, empieza cada viñeta con el nombre exacto, el tipo o el número de línea". El escritor: "respeta exactamente los patrones, la nomenclatura y el estilo existentes; devuelve solo el código, sin bloques de código, sin explicaciones". Sin esa última línea el modelo lo envuelve todo en Markdown que Claude después tiene que parsear.
Dos scripts los envuelven. Bulk-read recibe una pregunta y rutas de archivos y las envía. Code-write recibe una especificación y un archivo de referencia y escribe el resultado directamente en disco, así que Claude nunca ve el código generado. Cada delegación es de un solo disparo: una pregunta de seguimiento vuelve a enviar los archivos. Eso sale gratis donde importa, porque el corpus va al worker y nunca entra en el contexto de Claude.
La capa tres es un archivo de skill que le dice a Claude cuándo delegar: archivos de más de 350 líneas, preguntas que abarcan tres o más archivos, diffs grandes. Su última línea es "verifica los números de línea antes de editar".
La tabla de Spotify cubre un monorepo Java y tres escenarios de lectura. El caso de un solo archivo baja de unos 34.000 tokens a menos de 6.000, y el ahorro medio de las tres filas es del 90%.
| Benchmark de Spotify | Valor |
|---|---|
| Repositorios | 1 monorepo Java |
| Escenarios | 3, todos lecturas masivas |
| Caso de un solo archivo, antes | ~34.000 tokens |
| Caso de un solo archivo, después | < 6.000 tokens |
| Ahorro medio | 90% |
| Estimación de tokens | 4 caracteres por token |
| Enforcement del escritor | ninguno (solo el lector tiene un hook) |
El propio Spotify imprime dos salvedades: los tokens se estiman a cuatro caracteres cada uno y el escritor no tiene ningún tipo de enforcement. Así que el 90% es la media de tres filas de lectura masiva en tokens de entrada estimados, sin puntuación de calidad y sin una sola cifra en dólares. Ese es el número que hay que poner a prueba.
Reconstrucción, parte uno: un subagente con campo model
Claude Code trae de serie un subagente Explore integrado, y desde una versión reciente hereda tu modelo principal, con tope en Opus, así que el "lector barato" ya no es barato. La documentación da la solución en una frase: un subagente de proyecto llamado Explore sobrescribe al integrado y conserva su propio campo model. Un archivo markdown, un front matter, y la línea model dice Haiku. Ese es el lector masivo. El escritor es un segundo archivo: model Sonnet, herramientas Read y Write únicamente, y el cuerpo son las propias instrucciones de Spotify pegadas tal cual.
Funciona porque cada subagente arranca con una ventana de contexto nueva y aislada. Lo que lee aterriza ahí, no en la conversación principal. Es la delegación de un solo disparo de Spotify sin el viaje de ida y vuelta por la red.
Luego viene la parte que nadie planifica. En Reddit esta semana, a Fable se le pidió que lanzara agentes Opus y lanzó cinco agentes Fable en su lugar: el 73% de un límite semanal desaparecido en treinta minutos. La respuesta más votada era un hook que se ejecuta cuando el modelo despacha un subagente, lo obliga a elegir el modelo explícitamente y le indica que escoja el más barato capaz de hacer la tarea. Ese es el hook número tres: vigila la herramienta Agent, y una llamada sin modelo se rechaza con una sola frase, "elige el modelo explícitamente".
El skill de Spotify se convierte en tres líneas en el archivo de instrucciones del proyecto: los archivos de más de 350 líneas van al explorador, el boilerplate va al escritor, cada llamada a agente fija un modelo. La opción contundente también existe: dos variables de entorno que fuerzan un único modelo en todos los subagentes. El límite honesto es que el lector es un modelo más barato, así que lo que devuelve es todo lo que el modelo principal sabe. La sección de medición se ocupa de eso.
Reconstrucción, parte dos: el deny, en el formato actual de hooks
Recuerda la palabra "block". El script de Spotify devuelve una decisión de nivel superior, pero la documentación actual de Claude Code dice algo distinto: un hook PreToolUse devuelve su decisión dentro de un objeto de salida específico del hook, y el campo se llama permissionDecision. Tiene cuatro resultados, allow, deny, ask y defer, y el que se quiere aquí es deny. Lo que el hook escriba como motivo se le muestra a Claude, y si responden varios hooks, gana deny.
El hook de lectura reconstruido mantiene el mismo umbral de 350 y las mismas tres excepciones, y en lugar de "block" devuelve un deny con un motivo que nombra al subagente Explore y el modelo que hay que usar. Una trampa que la documentación deja clara: los hooks de tu configuración también se ejecutan dentro de los subagentes. Sin una vía de escape, al lector Haiku se le deniegan sus propias lecturas y nunca puede hacer su trabajo, así que el script comprueba quién llama y deja pasar a los dos workers.
El cableado es un solo archivo de configuración con tres matchers, Read, Bash y Agent, cada uno apuntando a su script, y el umbral fijado como variable de entorno. En la práctica, una lectura de un archivo de 1.090 líneas vuelve como un error con la frase escrita: delega esta lectura al explorador, modelo Haiku. Después sigue la delegación: el modelo principal cuenta primero las líneas, llama al explorador con el modelo fijado en Haiku, y las viñetas vuelven, cada una con su número de línea. Tres turnos, 44 segundos.
La frase de Spotify se sostiene: las capas hacen que el sistema se degrade con elegancia. La instrucción hace el enrutado, el hook es la red. La red tiene un agujero, eso sí. Un modelo que quiera el archivo entero puede trocearlo con offset y límite, que pasa, o volcarlo por la shell con un rango de sed, que este hook no atrapa. La medición cuenta ambas cosas.
La medición
El repositorio de prueba es Fastify, el framework web para Node: 294 archivos, 63 de ellos por encima del umbral. Dos clones idénticos, con la única diferencia de la carpeta .claude y el archivo de reglas. Modelo principal Opus, el valor por defecto de la CLI; lector Haiku; escritor Sonnet. Sesiones de un solo prompt, sin preguntas de seguimiento, cada escenario ejecutado dos veces por configuración, dieciséis ejecuciones en total. Los cuatro escenarios son los mismos que los de Spotify: los exports de un archivo grande, tres archivos y cómo se llaman entre sí, un archivo fuente frente a su test, y un archivo de tests nuevo escrito en disco a partir de uno existente.
| Escenario | Contexto principal, sin | Contexto principal, con | Variación | Coste total, sin | Coste total, con | Variación | Duración, sin | Duración, con | Variación |
|---|---|---|---|---|---|---|---|---|---|
| Un archivo grande | 88.693 | 51.552 | -41,9% | 0,139 $ | 0,087 $ | -37,8% | 22 s | 44 s | +100,8% |
| Tres archivos | 357.166 | 73.440 | -79,4% | 0,581 $ | 0,218 $ | -62,4% | 52 s | 129 s | +149,9% |
| Fuente frente a test | 303.808 | 114.136 | -62,4% | 0,451 $ | 0,374 $ | -17,1% | 93 s | 125 s | +33,6% |
| Archivo de tests nuevo | 143.432 | 121.818 | -15,1% | 0,295 $ | 0,302 $ | +2,6% | 66 s | 87 s | +32,2% |
| Los cuatro | 223.274 | 90.236 | -59,6% | 0,366 $ | 0,245 $ | -33,1% | 58 s | 96 s | +65,3% |
El contexto principal, los tokens que el modelo caro vio de verdad, es la primera columna que importa. En la pregunta de tres archivos baja un 79%, y en los cuatro escenarios un 59,6%. La factura baja menos, un tercio en conjunto, porque los tokens del propio lector no son gratis, y en la pequeña tarea de escribir tests la factura subió un 2,6%. El tiempo va en sentido contrario: 58 segundos de media sin el setup, 96 con él. Delegar es más lento siempre.
La calidad es donde más difieren las dos configuraciones. Sin el setup, el modelo principal volcó los archivos por la shell sin números de línea y contó a mano, produciendo números de línea erróneos por todas partes: una función reportada en la línea 149 estaba en realidad en la 156. Con el setup, una ejecución de cada cuatro se tomó el resumen del lector al pie de la letra y arrastró tres afirmaciones falsas, una de ellas una función que, según el lector, el archivo de rutas nunca llama, cuando sí lo hace, en la línea 553. Los cuatro archivos de tests generados pasan, y los hooks deny se dispararon cero veces en dieciséis ejecuciones: con el archivo de reglas presente, el modelo principal comprobó el número de líneas y delegó por su cuenta todas las veces.
Una cosa más de las trazas: sin la regla, el modelo principal nunca usó la herramienta Read. Lo leyó todo por la shell, y una lectura por rango en la shell cuesta los mismos tokens y pasa el hook. Así que la tabla de Spotify dice 90; esta dice 60 en contexto y un tercio en la factura.
Conserva el bloqueo. No esperes que la factura baje un noventa.
Tres cosas merece la pena conservar: un subagente Explore de proyecto en Haiku, una regla de tres líneas en el archivo de instrucciones y el hook de lectura como red de seguridad. El resultado medido es un 60% menos de contexto principal, un tercio menos de factura y dos tercios más de tiempo real.
Antes de fiarte del hook, arregla dos cosas. Los hooks se ejecutan dentro de los subagentes, así que exime a tus workers. Y el agujero de la shell: el hook de bash atrapa cat, head y tail, pero una lectura por rango pasa, y el modelo principal usó exactamente eso cuando no tenía regla.
Los límites que declara el propio Spotify se mantienen. No puedes delegar la edición ni puedes delegar el razonamiento; el worker pasó por alto un bug de thread safety que Claude cazó en segundos, y cada delegación es un viaje de ida y vuelta. Los escépticos de Hacker News también tenían razón en una cosa: los tokens de entrada no son la factura. Los tokens de salida cuestan más, y este setup no hace nada por ellos.
Quién ahorra depende de cómo pagas. En la API, un tercio menos. En un plan Pro o Max, el mismo setup mueve tus ventanas de cinco horas y semanales, no dólares. Vigila también el umbral: por debajo de él, delegar cuesta más de lo que ahorra, y el caso de test de 45 líneas es la prueba, con un más 2,6%. Por último, en dos ejecuciones de ocho el resumen del lector arrastró errores, y el turno de verificación del modelo principal los cazó. Sáltate ese turno y esos errores llegan a tus ediciones.
AIDive