Blast radius : ce qu’une pull request touche hors du diff
Le blast radius est le code qu’un changement atteint hors du diff. Sur une vraie pull request, voici comment le trouver à partir du code, sans fausse flèche.
En bref. Le blast radius (rayon d’impact) est le code qu’un changement atteint au-delà de lui-même : les fichiers qui appellent ou importent le code modifié. Cet article montre comment trouver le rayon d’impact d’une pull request à partir du seul code, sans modèle. Il explique aussi pourquoi la correspondance par nom trace de fausses flèches, et comment afficher le résultat sous forme de diagramme sur GitHub. Nous avons vérifié le comportement des outils dans leur documentation le 14 octobre 2026. L’exemple est une pull request fusionnée de la bibliothèque requests.
- Le diff ne montre pas qui appelle le code. Il montre les lignes modifiées, pas les fichiers qui les utilisent. Dans notre exemple, deux fichiers changent, et 9 autres fichiers en dépendent.
- La correspondance par nom trace de fausses flèches. Nous en avons trouvé quatre sortes dans une sortie réelle : une autre définition du même nom, un commentaire qui ne fait que citer le nom, une deuxième copie de l’arborescence et un import rattaché au mauvais fichier.
- Une flèche n’est tracée que si l’import mène au fichier modifié. Peu de flèches, mais sûres, valent mieux que beaucoup de flèches que le lecteur doit vérifier une à une.
- Pas de schéma si tout n’a pas été lu. Un schéma incomplet laisse croire que « personne n’appelle ce code ».
Qu’est-ce que le blast radius ?
Le terme vient de l’infrastructure, où il désigne jusqu’où une panne se propage. Selon le guide d’AWS sur l’architecture cellulaire, découper un système en cellules isolées les unes des autres réduit le blast radius. Le guide précise que c’est utile pour le trafic, mais aussi au moment de déployer une nouvelle fonctionnalité ou un correctif (source).
En revue de code, la question est plus étroite : hors de cette pull request, qui appelle ou importe le code qu’elle modifie ? Si le type de retour d’une fonction change, tous les endroits qui l’utilisent sont touchés, et le relecteur doit aussi garder ces fichiers en tête. On cherche aussi ce sujet sous le nom « change impact analysis » (analyse d’impact des changements).
Dans cet article, nous limitons le rayon d’impact à son cercle le plus proche : les fichiers qui appellent ou importent directement le fichier modifié. Pour dessiner ce schéma avec uniquement de vraies flèches, il faut quatre règles.

Ce que le diff ne montre pas
Quand vous ouvrez une pull request, GitHub vous montre les fichiers et les lignes modifiés. Les fichiers qui ne changent pas mais utilisent le code modifié ne sont pas sur cet écran ; c’est au relecteur d’y penser.
Un exemple réel : la pull request 7441 de la bibliothèque requests modifie deux fichiers, _types.py et models.py, et le diff ne montre que ceux-là. En parcourant l’arborescence au dernier commit de la pull request, nous avons trouvé 9 autres fichiers qui les utilisent. sessions.py et adapters.py, par exemple, utilisent directement la classe PreparedRequest de models.py : si cette classe change, le comportement de ces deux fichiers peut changer aussi.
La suite de l’article explique comment nous avons obtenu ce nombre, et pourquoi nous laissons volontairement certaines flèches de côté.
Trois façons courantes de trouver les appelants
- La recherche textuelle.
git grep -w nomtrouve chaque ligne où le nom apparaît comme mot entier (documentation). C’est rapide et sans configuration. Mais la recherche remonte aussi les commentaires, les chaînes et les autres fonctions du même nom ; à vous de trier les vrais appels. - Le serveur de langage. « Find All References » dans l’éditeur envoie la requête
textDocument/referencesdu Language Server Protocol : le serveur de langage renvoie alors toutes les références au symbole sous le curseur dans le projet (spécification). Le résultat est exact, mais ne porte que sur un symbole à la fois et suppose que le projet soit configuré dans l’éditeur. La navigation de code de GitHub, dans le navigateur, fonctionne autrement : elle utilise tree-sitter et, d’après sa documentation, parcourt toutes les définitions et références d’un dépôt pour trouver les entités qui portent un nom donné (documentation). Autrement dit, elle procède par correspondance de noms. - Les outils de graphe d’appels. Des outils comme pyan pour Python et madge pour JavaScript produisent le graphe de tout le dépôt. pyan qualifie sa propre analyse de « plutôt superficielle » (source) ; madge, lui, dessine les dépendances entre modules, pas les appels de fonctions (source). Les deux regardent tout le dépôt plutôt qu’une pull request, et leur résultat ne tient pas dans un commentaire de review.

Pour une pull request, il faut un entre-deux : un schéma aussi exact qu’un serveur de langage, aussi rapide qu’une recherche textuelle, et limité aux abords des fichiers modifiés. Pour y arriver, il faut suivre la ligne d’import, pas le nom.
Quatre fausses flèches trouvées dans une sortie réelle
En écrivant l’étape de diffox qui dessine le rayon d’impact d’une pull request, nous avons relu à la main les sorties des premières versions et trouvé quatre types de fausses flèches. Chacun a donné lieu à une règle :
- Un nom défini à plusieurs endroits. Une fonction utilitaire du même nom était définie dans trois outils différents, et l’appelant de l’une a été dessiné comme celui de l’autre. Règle : pas de flèche pour un nom qui n’est pas défini exactement une fois dans toute l’arborescence.
- Une deuxième copie de l’arborescence. Un git worktree placé dans le dépôt était une copie de toute l’arborescence ; les deux tiers de ce que la première exécution a trouvé venaient de là. Règle : on n’entre pas dans un dossier dont le nom commence par un point.
- Une ligne qui ne fait que citer le nom. Une ligne de commentaire citait le nom de la fonction en exemple. Règle : seul le code lui-même est lu ; une fois les commentaires et les chaînes retirés, il doit rester un appel ou un import.
- Un import rattaché au mauvais fichier. La ligne
from werkzeug.exceptions import NotFounda été dessinée comme un appelant du fichierexceptions.pydu dépôt lui-même, simplement parce que les deux fichiers portaient le même nom. Règle : une flèche n’est tracée que si la ligne d’import de l’appelant mène au fichier modifié lui-même. Un import relatif se résout par un calcul de chemin ; les autres sont rattachés à un seul fichier d’après les fichiers présents dans l’arborescence.
La quatrième règle a un prix : un appelant dont l’import ne peut pas être suivi n’entre pas dans le schéma. En Go et en Ruby, un import ne lie aucun nom, donc les fichiers de ces langages n’ont pas de flèche ; une méthode appelée sur un objet (obj.x()) n’est pas non plus rattachée à un fichier. Une flèche absente ne coûte rien au lecteur. Une fausse flèche, elle, lui fait perdre confiance dans tout le reste du schéma.
Garder le schéma lisible
Dans un vrai dépôt, le nombre d’appelants grandit vite. Deux choses aident.
- Laissez les tests de côté. Dans notre propre dépôt, 16 des 19 flèches de la première version venaient de fichiers de test. « Vos tests appellent cette fonction » : l’auteur de la pull request n’a pas besoin de le voir. Reconnaissez un test à son chemin, pas à son contenu : un dossier
tests/,test_*.py,*.test.ts, etc. L’effet varie selon le dépôt : dans l’exemple de requests, compter les tests n’aurait ajouté qu’un fichier. - Limitez le nombre de boîtes et signalez le reste. Un diagramme de quarante boîtes ne se lit pas, et un schéma qui ne dit pas qu’il a été tronqué passe pour complet. Nous dessinons au plus 8 boîtes et 6 appelants par fichier modifié, et nous comptons les autres. Dans l’exemple de requests, 6 des 9 fichiers ont été dessinés, et une phrase sous le schéma signale les 3 autres.
Accélérer la recherche sur les gros changements
La façon la plus directe de trouver les appelants est de chercher \bNOM\b dans chaque ligne de l’arborescence, pour chaque nom que définissent les fichiers modifiés. Un gros changement définit des centaines de noms ; sur une arborescence de centaines de fichiers, cela fait des millions de recherches par regex. Sur une grosse pull request de notre propre dépôt, cette étape a lancé 19,1 millions de recherches, et sur le serveur le schéma a dépassé son budget de 20 secondes. Deux changements réduisent ce temps :
- Filtrez d’abord les candidats. Un fichier qui ne mentionne jamais le nom du fichier modifié comme mot ne peut pas l’importer, car toute ligne d’import, comme
from service.app import xourequire("./app"), contient la dernière partie du chemin sous forme de mot. Dans notre ancien outil, indexer toute l’arborescence prenait 51 secondes pour 299 fichiers ; ce filtre l’a ramené à 38 fichiers et 3,2 secondes, sans perdre une seule flèche. - Cherchez par ligne, pas par nom. Selon la documentation de Python,
\best la frontière entre un caractère de mot et un caractère qui n’en est pas un (documentation). Pour un nom fait uniquement de caractères de mot,\bNOM\bne correspond donc que si l’un des mots de la ligne est exactement ce nom. Un seulfindallpar ligne donne la réponse pour tous les noms à la fois ; il ne reste plus qu’une intersection d’ensembles. Les noms qui contiennent d’autres caractères, comme$fooouvalid?, gardent leur propre motif.
import re
WORD = re.compile(r"\w+")
def hits(lines, names):
"""Noms présents dans chaque ligne : un seul findall par ligne, pas une regex par nom."""
wanted = set(names)
for number, line in enumerate(lines, 1):
found = wanted.intersection(WORD.findall(line))
if found:
yield number, foundLe gain dépend du nombre de noms recherchés. Nous avons mesuré les deux façons sur les mêmes arborescences et la même machine, et elles ont trouvé exactement les mêmes résultats à chaque fois :

Dans Django, quand un seul gros fichier change (127 noms), l’écart entre les deux façons est faible. Quand le changement s’étend aux cinq plus gros fichiers (577 noms), la recherche par ligne est 3,9 fois plus rapide ; sur notre propre pull request (570 noms), 8,2 fois. Ce changement n’accélère donc pas chaque review ; quand le nombre de noms grandit, il ramène le schéma dans son budget. Le goulot d’étranglement, nous l’avons trouvé avec un profileur, pas au jugé : l’essentiel du temps passait dans une seule boucle.
Quand ne pas dessiner le schéma
Si un parcours bute sur une limite de temps ou de taille et s’arrête au milieu de l’arborescence, ce qui en sort n’est pas un échantillon des appelants. Un fichier qui n’a pas pu être lu n’a pas d’appelant dans le schéma, et cela se lit comme « personne ne l’appelle ». C’est pourquoi, dans ces cas, nous ne dessinons aucun schéma :
- Le temps est écoulé. Le parcours et l’index doivent finir dans un budget ; chez nous, 20 secondes.
- Le fichier modifié n’a pas pu être lu. Il est trop gros, c’est un lien symbolique ou ce n’est pas un fichier ordinaire.
- Une limite a été dépassée. Le nombre de fichiers lus, la taille totale ou le nombre de noms recherchés a franchi le seuil.
- Il n’y a pas d’appelant. Une boîte sans flèche affirme un isolement que les données n’établissent pas.
Ne rien montrer au lecteur vaut mieux que lui montrer quelque chose de faux. En revanche, la raison de l’absence de schéma doit être consignée dans les logs : eux seuls disent s’il s’agit d’une erreur ou d’un arrêt attendu.
Afficher le schéma sur GitHub avec Mermaid
Le plus simple, pour montrer le schéma, est d’utiliser Mermaid. Selon la documentation de GitHub, les diagrammes sont rendus dans les issues, les discussions, les pull requests, les wikis et les fichiers Markdown ; il suffit d’écrire la syntaxe Mermaid dans un bloc de code portant l’étiquette de langage mermaid (documentation).
Le schéma de l’exemple requests, tel qu’il est écrit dans un commentaire de pull request :
```mermaid
flowchart LR
n0["_types.py"]:::changed
n1("utils.py")
n2("adapters.py")
n3("cookies.py")
n4("sessions.py")
n5["models.py"]:::changed
n6("__init__.py")
n7("exceptions.py")
n1 -.->|"SupportsItems"| n0
n2 -.->|"is_prepared"| n0
n3 -.->|"is_prepared"| n0
n4 -.->|"is_prepared"| n0
n6 -.->|"Request, PreparedRequest +1"| n5
n7 -.->|"Request, PreparedRequest +1"| n5
n4 -->|"Request, PreparedRequest +1"| n5
n1 -.->|"Request, PreparedRequest +1"| n5
n2 -->|"PreparedRequest, Response"| n5
classDef changed stroke:#3a6b8b,stroke-width:2px
```![EXEMPLE. Quatre éléments, un schéma. commentaire de pull request: flowchart LR / n0["_types.py"]:::changed / n5["models.py"]:::changed / n4("sessions.py") / n4 -->|"PreparedRequest"| n5 / n4 -.->|"is_prepared"| n0 / classDef changed stroke-width:2px. :::changed: Fichier modifié, cadre à part. -->: Flèche pleine : au moins un appel. -.->: Flèche pointillée : import seul. |"nom"|: Les noms utilisés, sur la flèche.](/blog/img/blast-radius-in-software-engineering-mermaid-fr.webp?v=ff6ab07e804cbaaa)
Trois petits choix rendent le schéma lisible. Les fichiers modifiés sont marqués d’un cadre à part. Les noms utilisés sont écrits sur la flèche ; au-delà de deux, le reste devient un nombre comme « +1 ». Quand un fichier ne fait qu’importer le fichier modifié, sans appel direct trouvé, sa flèche est en pointillé. Et une seule phrase sous le schéma dit ce qui n’a pas été dessiné.
Le rayon d’impact dans diffox
Chaque fois que diffox examine une pull request, il relance cette étape sur le dernier commit. S’il trouve des appelants hors du diff, il ajoute à son commentaire récapitulatif un schéma Mermaid intitulé « Ce que cette modification touche ». Les règles sont celles de cet article : pas de modèle, seulement des flèches dont l’import mène au fichier modifié, pas de tests, 8 boîtes au plus, un budget de 20 secondes, et pas de schéma si tout n’a pas été lu. Les langages pris en charge sont Python, JavaScript/TypeScript et Java. Si vous ne voulez pas du schéma, désactivez « Résumé de la modification » dans l’onglet Paramètres de la console.
Le schéma montre le rayon d’impact ; si quelque chose casse, ce sont les constats qui le disent. Le Blast Radius Agent, l’un des 17 agents spécialisés, cherche les appelants cassés, les surfaces d’API modifiées et les changements de signature ou de type de retour qui cassent d’autres fichiers (son fonctionnement fait l’objet d’un article à part).
Questions fréquentes
Qu’est-ce que le blast radius en développement logiciel ?
C’est jusqu’où s’étend l’effet d’une panne ou d’un changement. En infrastructure, il décrit combien d’utilisateurs une défaillance atteint ; en revue de code, quels fichiers appellent ou importent le code modifié en dehors de la pull request.
Blast radius et change impact analysis, est-ce la même chose ?
Ce sont des notions proches. La change impact analysis (analyse d’impact des changements) est le nom général du travail qui consiste à trouver à l’avance ce qu’un changement touchera ailleurs. Le blast radius d’une pull request est le cercle le plus proche de cette analyse : les fichiers qui utilisent directement le fichier modifié.
Comment trouver qui appelle une fonction ?
Utilisez « Find All References » dans votre éditeur : le serveur de langage trouve toutes les références du symbole. Pour un coup d’œil rapide, git grep -w nom_de_fonction suffit, mais il faut écarter à la main les correspondances dans les commentaires et dans d’autres définitions du même nom. Pour tous les fichiers modifiés d’une pull request, il faut un parcours qui suit les imports jusqu’au fichier modifié.
Un diagramme Mermaid fonctionne-t-il dans un commentaire de pull request GitHub ?
Oui. GitHub dessine les diagrammes Mermaid dans les issues, les discussions, les pull requests, les wikis et les fichiers Markdown. Il suffit d’écrire la syntaxe dans un bloc de code portant l’étiquette de langage mermaid.
Pourquoi les tests n’apparaissent-ils pas dans le schéma du rayon d’impact ?
Les tests sont parmi les fichiers qui appellent le plus souvent le code modifié, et ils encombrent le schéma ; dans notre propre dépôt, la plupart des flèches de la première version venaient des tests. Pour la personne qui ouvre la pull request, la vraie question est ce qui est touché dans le code de production.
Que signifient les flèches en pointillé du schéma ?
Une flèche en pointillé indique que le fichier appelant importe quelque chose du fichier modifié, mais qu’aucun appel direct n’a été trouvé. Une flèche pleine signifie qu’au moins un appel a été trouvé.
Dans quels langages diffox dessine-t-il le rayon d’impact ?
Python, JavaScript/TypeScript et Java. Dans ces langages, un import lie un nom, et l’on peut trouver vers quel fichier il mène. En Go et en Ruby, un import ne lie aucun nom, donc diffox n’y trace pas de flèche : plutôt que de deviner, il la laisse de côté.