filefunc × Hono — Du code lisible par un agent en un passage : de 60 lignes à 18

Si votre agent de code corrige constamment les mauvaises choses dans un grand codebase, si lire un seul fichier entraîne 19 fonctions non pertinentes qui polluent le context, si vous doutez que des conventions comme « 1 fichier, 1 concept » aient vraiment un effet — voici les résultats mesurés sur un framework réel de 23k étoiles.

« Est-ce que ça ne crée pas trop de fichiers ? »

C’est la question la plus fréquente à propos de filefunc. Découper 186 fichiers en 626, n’est-ce pas ingérable ?

La réponse est dans Hono. Un framework web ultraléger qui tourne sur Cloudflare Workers, Deno, Bun et Node.js. 23k+ étoiles, 1 million+ de téléchargements hebdomadaires sur npm. Du code de production validé dans le réel. Nous avons refactorisé ce code avec filefunc. 4419 tests, tous passants. (Vérifiable directement sur le fork park-jun-woo/hono)

Les chiffres

IndicateurOriginalAprès refactorisation
Fichiers source186626
Lignes totales24 65330 244
Violations filefunc3970
vitest passants44194419
vitest échoués44 (défauts préexistants)
vitest ignorés3333

Les fichiers ont été multipliés par 3,4. Les lignes ont augmenté de 23 %. Les violations sont passées de 397 à 0. Pas un seul test n’a été cassé — plus précisément, exactement les mêmes 4 tests échouent qu’à l’origine (des défauts déjà présents dans l’original). L’augmentation de 23 % des lignes provient des annotations (//ff:func, //ff:what) et des re-export hub. La logique n’a pas changé d’une seule ligne. Une refactorisation purement structurelle.

Ce qui compte, c’est la longueur de lecture, pas le nombre de fichiers

« Zéro violation, 626 fichiers » est en réalité un proxy. L’objectif réel de filefunc n’est pas de fragmenter les fichiers — c’est de faire en sorte que lorsqu’un agent lit un concept, il ne lise que ce concept, sans longueur excessive. Le vrai chiffre à prouver n’est donc pas le nombre de violations, mais la longueur de lecture par fichier. Nous l’avons mesuré.

Lignes par fichierOriginalAprès refactorisation
Médiane60,017,5 (−71 %)
p90305119 (−61 %)
Maximum2 7781 051 (−62 %)
Proportion de fichiers ≤ 20 lignes26 %54 %

Quand un agent ouvre un concept, il absorbait auparavant en moyenne 60 lignes ; maintenant il en absorbe 18. Même dans le pire des cas (p90), on passe de 305 lignes à 120. La longueur des fonctions elles-mêmes est inchangée (médiane 11 → 12 lignes) — c’est logique : nous avons réorganisé les fonctions, pas réécrit leur logique. Ce qui a diminué, c’est « le code périphérique qu’on était obligé de lire pour accéder à un seul concept ».

Pourquoi est-ce important ? Un long context n’est pas gratuit. Les LLM ratent systématiquement les informations enfouies au milieu d’une longue entrée (Liu et al., Lost in the Middle, TACL 2023, arXiv:2307.03172). Sur des tâches de coding, les performances s’effondrent à mesure que le contexte s’allonge — dans un benchmark, la précision de Claude 3.5 Sonnet est passée de 29 % à 3 % (Rando et al., LongCodeBench, 2025, arXiv:2505.07897). Et fournir exactement les parties nécessaires découpées par concept plutôt qu’un fichier entier améliore la qualité de complétion de code (Yusuf et al., 2025, arXiv:2510.06606). Réduire la longueur de lecture n’est pas une question de style — c’est une défense de la précision.

Le problème types.ts

Quittons l’abstraction pour le concret. Le fichier original src/types.ts de Hono contenait plus de 20 interfaces et types.

Quand un agent IA cherche le type HonoRequest et lit ce fichier ? 19 types inutiles viennent avec. Pollution du context.

Après refactorisation, chaque type est dans un fichier indépendant. Si on a besoin de HonoRequest seul, on ne lit que hono_request.ts. Le types.ts original subsiste comme re-export hub pour préserver les chemins d’import existants.

# Original
import { HonoRequest } from './types'  // 20+ types viennent avec

# Après refactorisation
import { HonoRequest } from './types'  // même chemin, même comportement
// En interne : types.ts → re-export depuis hono_request.ts

De l’extérieur, rien n’a changé. Pour un agent IA, tout a changé.

De la profondeur 6 à 2

L’algorithme de routage de Hono est complexe. Le Node.search du trie-router atteignait une profondeur d’imbrication de 6.

for → if → if → for → if → if   // profondeur 6

Est-ce du mauvais code ? Non. La recherche dans un trie est intrinsèquement profonde. Mais pour qu’un agent IA comprenne cette fonction, il doit tenir simultanément 6 niveaux d’imbrication en tête. Idem pour un humain. filefunc a extrait la logique interne en méthodes privées et fonctions fléchées au niveau module. Profondeur 6 → 2. Chaque fragment ne possède qu’un seul flux de contrôle. L’algorithme global est identique.

# Original : search monolithique
Node.search()   // profondeur 6, 100+ lignes

# Refactorisé : décomposé en fragments
Node.search()           // profondeur 2, se charge uniquement de la composition
  → matchParam()        // profondeur 1, correspondance des paramètres
  → matchWildcard()     // profondeur 1, traitement des wildcards
  → mergeHandlers()     // profondeur 1, fusion des handlers

La règle F1 de TypeScript, et une queue honnête

La règle centrale F1 de filefunc est « un fichier, une fonction ». En Go, c’est intuitif. Mais en TypeScript, découper les fichiers casse le système de modules. Extraire une méthode de classe dans un fichier externe supprime le binding this. C’est pourquoi le parser TypeScript de filefunc (ts_ast.js) ne compte que les déclarations function (FunctionDeclaration) et non les const arrow — car le principe est « un fichier, un concept », pas « exactement une syntaxe function par fichier ».

Il faut être honnête ici. Cette approche sépare proprement les cas simples (types, helpers isolés), mais elle ne sépare pas tout. En remesurarant après refactorisation :

  • Sur 626 fichiers, 90 % (566) contiennent ≤ 1 fonction — satisfaisant « 1 fichier, 1 concept ». (L’original était à 70 %.)
  • Mais 60 fichiers (9,6 %) hébergent encore 2 fonctions ou plus. Et ce sont précisément les plus longs — la médiane de lignes pour ces 60 fichiers est de 151. Par exemple, src/utils/url.ts contient 14 fonctions en 319 lignes.

Autrement dit, la technique const arrow fait passer le compteur mais n’atteint le but que partiellement. Quand plusieurs fonctions fléchées cohabitent dans un fichier, l’agent qui l’ouvre lit toujours plusieurs concepts à la fois. Quand la métrique devient l’objectif, la métrique se dégrade (Goodhart). filefunc ne fait pas exception — la majeure partie du risque de lecture résiduel est concentrée dans ces 10 % de queue. Ne pas élever le chiffre « zéro violation » au rang de réponse définitive, et mesurer aussi ce qui reste à faire — c’est ça, la validation.

« Alors, qu’est-ce que ça améliore concrètement ? »

626 fichiers peuvent être inconfortables pour un humain. Ouvrir un répertoire, c’est une avalanche. Mais un agent IA n’ouvre pas les répertoires. Il fait un grep.

rg '//ff:func' --glob '*.ts' -l | head -20    # extraction des fichiers candidats
rg '//ff:what.*router' --glob '*.ts'            # seulement les fonctions liées au routeur

Quand 186 fichiers contiennent en moyenne 3 à 4 fonctions chacun, le grep trouve le fichier mais lire celui-ci amène des fonctions inutiles avec. Quand 626 fichiers contiennent chacun 1 concept, le fichier trouvé par grep = le concept recherché. L’étape intermédiaire disparaît. La « localisation » — trouver l’emplacement pertinent dans le code d’un agent — est le goulot d’étranglement de la résolution de problèmes en aval (Chen et al., LocAgent, 2025, arXiv:2503.09089) ; filefunc aligne les concepts sur les frontières de fichiers, rendant cette recherche déterministe.

La granularité fonction est-elle toujours la bonne réponse ?

Voyons aussi les contre-preuves honnêtement. Une expérience contrôlée rapporte que, pour l’autocomplétion de code avec RAG, « le découpage à granularité fonction est 3,6 à 5,6 pp moins bon que les autres stratégies et n’est pas Pareto-optimal » (Wu et al., 2026, arXiv:2605.04763). La granularité fonction n’est pas universelle.

Mais c’est une comparaison de niveaux différents. Cette expérience porte sur l’autocomplétion où un retriever découpe le code et le fourre dans un prompt. filefunc traite des unités opérationnelles — les fichiers qu’un agent choisit lui-même de lire. Une stratégie de chunking (retrieval chunk) et une unité opérationnelle (fichier qu’un agent ouvre) sont deux couches distinctes. Il est néanmoins important de rendre cette distinction explicite — « plus petit c’est toujours mieux » n’est pas la thèse de filefunc. La thèse est : « quand l’unité de lecture d’un agent s’aligne sur un concept, la longueur de lecture diminue » — et les chiffres ci-dessus le démontrent.

La contrainte est une liberté

Refactoriser Hono avec filefunc a confirmé une chose.

La contrainte structurelle ne bride pas le code. Elle libère l’exploration.

Multiplier les fichiers a un coût. Mais quand chaque fichier ne contient qu’un seul concept, l’agent lit exactement ce dont il a besoin sans se contaminer d’un context superflu — la mesure d’une réduction de 60 à 18 lignes en est la preuve. Idem pour les humains. Quand le nom de la fonction est le nom du fichier, le répertoire devient une table des matières.

397 violations réduites à 0, et 4419 tests passant de façon identique à l’original. Et le rapport de refactorisation dans le README permet à quiconque de reproduire ces résultats. C’est la preuve que « 1 fichier, 1 concept » n’est pas une théorie mais une pratique réelle. Y compris les 9,6 % de queue restants.

Articles liés

Lectures complémentaires (externes)

  • Effective context engineering for AI agents — Anthropic. Source primaire qui identifie le « context rot » et le budget d’attention limité comme problèmes fondamentaux — même fondation que la raison pour laquelle filefunc réduit le context superflu.
  • Strategies and Tactics for working with Coding Agents — Brian Kihoon Lee. Plaidoyer pour concevoir soi-même l’architecture de l’information et l’exploration par grep — directement lié aux conventions de structure de fichiers qui font lire « uniquement la cible » à un agent.
  • The Vibes Don’t Scale — Paul Stack. Mécanisme par lequel le vibe coding s’effondre en dérive architecturale à l’échelle — la même problématique que filefunc cherche à résoudre : « les grands codebases cassent les agents ».
  • Agentic Engineering Patterns — Simon Willison. Patterns de gestion du context (Context Quarantine/Pruning, etc.) — élargit la thèse agent-operable de filefunc au langage des patterns pratiques.
  • Agent Harness Engineering — Addy Osmani. Les performances d’un agent se jouent plus dans l’infrastructure environnante que dans le modèle — recontextualise les conventions de structure de code comme un axe du harness.

Bibliographie

  • Liu et al. “Lost in the Middle: How Language Models Use Long Contexts” (TACL 2023, arXiv:2307.03172)
  • Rando et al. “LongCodeBench: Evaluating Coding LLMs at 1M Context Windows” (2025, arXiv:2505.07897)
  • Yusuf et al. “Beyond More Context: How Granularity and Order Drive Code Completion Quality” (2025, arXiv:2510.06606)
  • Chen et al. “LocAgent: Graph-Guided LLM Agents for Code Localization” (2025, arXiv:2503.09089)
  • Wu et al. “How Does Chunking Affect Retrieval-Augmented Code Completion? A Controlled Empirical Study” (2026, arXiv:2605.04763)
  • Résultats de refactorisation : park-jun-woo/hono (filefunc Refactoring Report dans le README) · Convention : filefunc
  • Image principale : générée par IA (Google Gemini)