AIDive

Le setup Claude Code de Spotify ne coupe pas 90 %, mais 60

Par AIDive · Publié le

Agents de codeModèles d'IA

Quatre-vingt-dix pour cent, et la phrase qui l'a vendu

Le setup Claude Code de Spotify est un billet de blog de Dimitri Mazmanov, product manager chez Spotify, avec son code sur GitHub : il affirme que la configuration utilisée par son équipe a réduit de 90 % sa consommation de tokens Claude Code. Sa première ligne porte tout l'argument : l'essentiel de ce que fait un agent de code n'est pas de la réflexion, c'est de l'I/O. Lire cinq fichiers pour répondre à une question sur une seule méthode, ou écrire le vingt-et-unième fichier de test qui copie les vingt d'à côté, brûle des milliers de tokens avec presque aucun raisonnement.

Un tweet a porté le billet à un million et demi de vues sur une seule phrase : des règles écrites sont une suggestion, un blocage n'en est pas une. Hacker News l'a mis en première page, 271 points et 173 commentaires, et la moitié des commentaires posaient la même question : 90 % de quoi ? Le qualificatif de Spotify lui-même est « bulk read », lecture en masse. Cet article reconstruit le setup dans Claude Code seul, puis le mesure, pour que vous sachiez exactement ce que ce qualificatif vous rapporte.

Ce qu'est vraiment Portal (et pourquoi vous ne pouvez pas le lancer)

Portal n'est pas un routeur. C'est le portail développeur interne de Spotify, construit sur Backstage, la plateforme développeur que Spotify a publiée en open source. La fonctionnalité qui nous intéresse s'appelle Modes : selon la définition de Spotify, un mode est un agent déclaratif qui tourne sur un runtime éphémère, en gros AWS Lambda pour agents. Vous écrivez les instructions, choisissez un modèle, fixez une température, attachez des outils. Mazmanov en a construit deux, un lecteur en masse et un rédacteur de code, tous deux sur Gemini Flash à température 0,2, donc tous deux bon marché et ennuyeux à dessein.

Le routage vit dans un plugin Claude Code appelé Shunt. Il est public sur GitHub et s'installe en deux commandes. L'étape deux, en revanche, authentifie la ligne de commande Portal contre votre instance Portal, et vous n'en avez pas. Le plugin est public ; la chose à laquelle il délègue ne l'est pas.

La démarche utile consiste donc à oublier le plugin et à garder le pattern. Il a trois couches, selon ses propres mots : hooks, scripts, skills. Chacune a un équivalent dans Claude Code seul, et c'est ce que le reste de cet article construit et mesure.

Couche un : le hook qui bloque au lieu de demander

La version 1 du setup était un bloc de règles de routage dans le fichier d'instructions du projet. Selon Mazmanov, cela « marchait à peu près » : les règles étaient consultatives, pas appliquées, Claude pouvait les ignorer, et chaque projet avait besoin de sa propre copie. La version 2 sort la décision du prompt et la place dans la couche outils avec deux hooks, qui se déclenchent tous deux avant un appel d'outil. L'un surveille chaque lecture de fichier, l'autre surveille le shell.

Le hook de lecture fait 33 lignes de bash. Il lit un seuil dans l'environnement, 350 lignes par défaut, puis laisse passer trois choses :

  • Une lecture avec un offset ou une limite, parce que Claude sait déjà ce dont il a besoin.
  • Un fichier qui n'existe pas.
  • Un fichier au seuil ou en dessous, parce que déléguer quelque chose de petit coûte plus cher que de le lire.

Tout le reste est bloqué, avec un message que Claude lit à la place du fichier : ce fichier fait tant de lignes, utilisez le skill de lecture en masse, et si vous avez besoin du contenu exact pour une édition, relisez seulement cette section. Le hook shell attrape cat, head, tail, less et more sur un gros fichier. Une commande avec un pipe passe, parce qu'un pipe vers grep est une lecture ciblée.

Le point de Mazmanov sur l'empilement des couches est le plus important : même si Claude ne lit jamais la description du skill, le hook bloque quand même la lecture coûteuse. Le skill rend la redirection plus fluide ; le blocage la rend réelle. Un détail compte pour la suite : le script répond avec une décision de premier niveau nommée « block ». Gardez ce mot en tête.

Couches deux et trois : les workers et leurs chiffres

Les workers sont deux prompts. Le lecteur : « tu es un analyste de code précis, sortie en puces structurées uniquement, pas de salutations, pas de prose, commence chaque puce par le nom exact, le type ou le numéro de ligne ». Le rédacteur : « respecte exactement les patterns, le nommage et le style existants ; sors uniquement le code, pas de fences, pas d'explications ». Sans cette dernière ligne, le modèle enveloppe tout dans du Markdown que Claude doit ensuite parser.

Deux scripts les enrobent. Bulk-read prend une question et des chemins de fichiers et les envoie. Code-write prend une spec et un fichier de référence et écrit le résultat directement sur le disque, si bien que Claude ne voit jamais le code généré. Chaque délégation est one-shot : une relance renvoie les fichiers. C'est gratuit là où ça compte, parce que le corpus va au worker et n'entre jamais dans le contexte de Claude.

La couche trois est un fichier de skill qui dit à Claude quand déléguer : fichiers de plus de 350 lignes, questions portant sur trois fichiers ou plus, gros diffs. Sa dernière ligne est « vérifie les numéros de ligne avant d'éditer ».

Le tableau de Spotify couvre un monorepo Java et trois scénarios de lecture. Le cas du fichier unique passe d'environ 34 000 tokens à moins de 6 000, et l'économie moyenne sur les trois lignes est de 90 %.

Benchmark de Spotify Valeur
Dépôts 1 Java monorepo
Scénarios 3, toutes en lecture en masse
Cas du fichier unique, avant ~34,000 tokens
Cas du fichier unique, après < 6,000 tokens
Économie moyenne 90%
Estimation des tokens 4 caractères par token
Contrainte sur le rédacteur aucune (seul le lecteur a un hook)

Deux réserves sont imprimées par Spotify lui-même : les tokens sont estimés à quatre caractères chacun, et le rédacteur n'a aucune contrainte. Le 90 % est donc la moyenne de trois lignes de lecture en masse en tokens d'entrée estimés, sans score de qualité et sans le moindre chiffre en dollars. C'est ce nombre qu'il faut tester.

Reconstruction, partie un : un subagent avec un champ model

Claude Code embarque un subagent Explore intégré, et depuis une version récente il hérite de votre modèle principal, plafonné à Opus, si bien que le « lecteur bon marché » n'est plus bon marché. La documentation donne la solution en une phrase : un subagent de projet nommé Explore remplace celui intégré et garde son propre champ model. Un fichier markdown, un front matter, et la ligne model dit Haiku. Voilà le lecteur en masse. Le rédacteur est un second fichier : model Sonnet, outils Read et Write uniquement, et le corps reprend les instructions de Spotify collées telles quelles.

Cela fonctionne parce que chaque subagent démarre avec une fenêtre de contexte fraîche et isolée. Ce qu'il lit atterrit là, pas dans la conversation principale. C'est la délégation one-shot de Spotify, moins l'aller-retour réseau.

Vient ensuite la partie que personne ne prévoit. Sur Reddit cette semaine, Fable a reçu l'ordre de lancer des agents Opus et a lancé cinq agents Fable à la place : 73 % d'une limite hebdomadaire envolés en trente minutes. La meilleure réponse était un hook qui s'exécute quand le modèle dispatche un subagent, le force à choisir le modèle explicitement, et lui dit de prendre le moins cher capable de faire la tâche. C'est le hook numéro trois : il surveille l'outil Agent, et un appel sans modèle est refusé avec une phrase, « choisis le modèle explicitement ».

Le skill de Spotify devient trois lignes dans le fichier d'instructions du projet : les fichiers de plus de 350 lignes vont à l'explorateur, le boilerplate va au rédacteur, chaque appel d'agent fixe un modèle. L'option brutale existe aussi : deux variables d'environnement qui imposent un seul modèle à tous les subagents. La limite honnête est que le lecteur est un modèle moins cher, donc ce qu'il renvoie est tout ce que le modèle principal sait. La section sur la mesure couvre ce point.

Reconstruction, partie deux : le deny, au format hook actuel

Souvenez-vous du mot « block ». Le script de Spotify renvoie une décision de premier niveau, mais la documentation actuelle de Claude Code dit autre chose : un hook PreToolUse renvoie sa décision dans un objet de sortie spécifique aux hooks, et le champ s'appelle permissionDecision. Il a quatre issues, allow, deny, ask et defer, et celle qu'on veut ici est deny. Ce que le hook écrit comme raison est montré à Claude, et si plusieurs hooks répondent, deny gagne.

Le hook de lecture reconstruit garde le même seuil de 350 et les trois mêmes exceptions, et au lieu de « block » renvoie un deny avec une raison qui nomme le subagent Explore et le modèle à utiliser. Un piège que la doc énonce clairement : les hooks de vos settings s'exécutent aussi dans les subagents. Sans échappatoire, le lecteur Haiku se voit refuser ses propres lectures et ne peut jamais faire son travail, donc le script vérifie qui appelle et laisse passer les deux workers.

Le câblage tient dans un fichier de settings avec trois matchers, Read, Bash et Agent, chacun pointant vers son script, et le seuil fixé comme variable d'environnement. En pratique, une lecture sur un fichier de 1 090 lignes revient comme une erreur avec la phrase écrite : délègue cette lecture à l'explorateur, modèle Haiku. La délégation suit alors : le modèle principal compte d'abord les lignes, appelle l'explorateur avec le modèle fixé à Haiku, et les puces reviennent, chacune avec un numéro de ligne. Trois tours, 44 secondes.

La ligne de Spotify tient : l'empilement des couches fait que le système se dégrade avec grâce. L'instruction fait le routage, le hook est le filet. Le filet a pourtant un trou. Un modèle qui veut le fichier entier peut le découper avec offset et limit, ce qui passe, ou le vider par le shell avec une plage sed, ce que ce hook n'attrape pas. La mesure compte les deux.

La mesure

Le dépôt de test est Fastify, le framework web Node : 294 fichiers, dont 63 au-dessus du seuil. Deux clones identiques, la seule différence étant le dossier .claude et le fichier de règles. Modèle principal Opus, le défaut du CLI ; lecteur Haiku ; rédacteur Sonnet. Sessions à prompt unique, sans relance, chaque scénario exécuté deux fois par configuration, seize runs au total. Les quatre scénarios sont les mêmes que ceux de Spotify : les exports d'un gros fichier, trois fichiers et la façon dont ils s'appellent, un fichier source face à son test, et un nouveau fichier de test écrit sur le disque à partir d'un existant.

Scénario Contexte principal, sans Contexte principal, avec Variation Coût total, sans Coût total, avec Variation Durée, sans Durée, avec Variation
Un gros fichier 88,693 51,552 -41.9% $0.139 $0.087 -37.8% 22 s 44 s +100.8%
Trois fichiers 357,166 73,440 -79.4% $0.581 $0.218 -62.4% 52 s 129 s +149.9%
Source contre test 303,808 114,136 -62.4% $0.451 $0.374 -17.1% 93 s 125 s +33.6%
Nouveau fichier de test 143,432 121,818 -15.1% $0.295 $0.302 +2.6% 66 s 87 s +32.2%
Les quatre 223,274 90,236 -59.6% $0.366 $0.245 -33.1% 58 s 96 s +65.3%

Le contexte principal, les tokens que le modèle cher a réellement vus, est la première colonne qui compte. Sur la question à trois fichiers, il baisse de 79 %, et sur les quatre scénarios de 59,6 %. La facture baisse moins, d'un tiers au total, parce que les tokens du lecteur ne sont pas gratuits, et sur la petite tâche d'écriture de test la facture a augmenté de 2,6 %. Le temps va dans l'autre sens : 58 secondes en moyenne sans le setup, 96 avec. La délégation est plus lente à chaque fois.

La qualité est là où les deux configurations diffèrent le plus. Sans le setup, le modèle principal a vidé les fichiers par le shell sans numéros de ligne et a compté à la main, produisant des numéros de ligne faux d'un bout à l'autre : une fonction annoncée à la ligne 149 se trouvait en réalité à la ligne 156. Avec le setup, un run sur quatre a pris le résumé du lecteur pour argent comptant et a porté trois affirmations fausses, dont une fonction que le lecteur disait jamais appelée par le fichier de routes, alors qu'elle l'est, à la ligne 553. Les quatre fichiers de test générés passent, et les hooks deny se sont déclenchés zéro fois en seize runs : avec le fichier de règles présent, le modèle principal a vérifié le nombre de lignes et délégué de lui-même à chaque fois.

Une chose de plus tirée des traces : sans la règle, le modèle principal n'a jamais utilisé l'outil Read. Il a tout lu par le shell, et une lecture de plage par le shell coûte les mêmes tokens et passe le hook. Le tableau de Spotify dit donc 90 ; celui-ci dit 60 sur le contexte et un tiers sur la facture.

Gardez le blocage. N'attendez pas une facture divisée par dix.

Trois choses valent la peine d'être gardées : un subagent Explore de projet sur Haiku, une règle de trois lignes dans le fichier d'instructions, et le hook de lecture comme filet de sécurité. Le résultat mesuré est 60 % de contexte principal en moins, un tiers de facture en moins, et deux tiers de temps réel en plus.

Avant de faire confiance au hook, corrigez deux choses. Les hooks s'exécutent dans les subagents, donc exemptez vos workers. Et le trou côté shell : le hook bash attrape cat, head et tail, mais une lecture de plage passe, et le modèle principal a utilisé exactement cela quand il n'avait pas de règle.

Les limites énoncées par Spotify tiennent. Vous ne pouvez pas déléguer l'édition et vous ne pouvez pas déléguer le raisonnement ; le worker a raté un bug de thread-safety que Claude a attrapé en quelques secondes, et chaque délégation est un aller-retour. Les sceptiques de Hacker News avaient raison sur un point aussi : les tokens d'entrée ne sont pas la facture. Les tokens de sortie coûtent plus cher, et ce setup ne fait rien pour eux.

Qui économise dépend de la façon dont vous payez. Sur l'API, un tiers en moins. Sur un plan Pro ou Max, le même setup déplace vos fenêtres de cinq heures et hebdomadaires, pas des dollars. Attention au seuil aussi : en dessous, la délégation coûte plus qu'elle n'économise, et le cas de test de 45 lignes en est la preuve à plus 2,6 %. Enfin, dans deux runs sur huit le résumé du lecteur portait des erreurs, et le tour de vérification du modèle principal les a attrapées. Sautez ce tour et ces erreurs atteignent vos éditions.

Sources

Questions fréquentes

Spotify a-t-il vraiment réduit de 90 % la consommation de tokens de Claude Code ?
Le 90 % de Spotify est la moyenne de trois scénarios de lecture en masse dans un monorepo Java, comptée en tokens d'entrée estimés à quatre caractères par token, sans score de qualité ni chiffre en dollars. Reconstruite avec Claude Code seul sur Fastify, la même idée a réduit le contexte du modèle principal de 59,6 % et le coût total de 33 %.
Qu'est-ce que Portal de Spotify et puis-je l'utiliser avec Claude Code ?
Portal est le portail développeur interne de Spotify, construit sur Backstage ; sa fonctionnalité Modes fait tourner des agents déclaratifs sur un runtime éphémère. Le plugin Shunt qui route Claude Code vers ces agents est public sur GitHub, mais il s'authentifie contre une instance Portal que vous n'avez pas : vous pouvez seulement copier le pattern, pas lancer le plugin.
Comment faire déléguer par Claude Code les lectures de gros fichiers à un modèle moins cher ?
Créez un subagent de projet nommé Explore avec le champ model fixé à Haiku, ce qui remplace l'agent Explore intégré qui hérite désormais de votre modèle principal. Ajoutez une règle de trois lignes au fichier d'instructions du projet (les fichiers de plus de 350 lignes vont à l'explorateur, le boilerplate va au rédacteur, chaque appel d'agent fixe un modèle) et un hook PreToolUse sur Read qui refuse les grosses lectures avec une raison nommant ce subagent.
Comment un hook PreToolUse bloque-t-il un appel d'outil dans Claude Code aujourd'hui ?
Le hook renvoie sa décision dans l'objet hookSpecificOutput, dans un champ nommé permissionDecision, avec quatre valeurs possibles : allow, deny, ask et defer. La chaîne de raison est montrée à Claude, et quand plusieurs hooks répondent, deny gagne. Le script de Spotify utilise une ancienne décision de premier niveau nommée block.
Déléguer les lectures à un subagent Haiku rend-il Claude Code moins cher ?
Sur l'API, cela a retiré un tiers de la facture sur quatre scénarios, mais les tokens du lecteur ne sont pas gratuits : sur une tâche d'écriture de test de 45 lignes, le coût total a augmenté de 2,6 %. Sur un plan Pro ou Max, l'économie apparaît en quota dans les fenêtres de cinq heures et hebdomadaires, pas en dollars, et les tokens de sortie restent intacts.
Pourquoi les hooks Claude Code se déclenchent-ils dans les subagents ?
Les hooks définis dans vos settings s'exécutent pour chaque agent, y compris les subagents que vous dispatchez. Un hook qui bloque les lectures refuse donc au lecteur Haiku ses propres lectures, sauf si le script vérifie qui appelle et laisse passer les agents workers.

Vidéos liées