Grep the Blast Radius of a Contract Change
Type: Open-source — client-agnostic methodology, no project-specific detail.
Created by akbarsha — https://github.com/iamakbarsha1
Distilled from repeated cases where a shared contract changed, the edited
package's tests stayed green, and sibling consumers broke silently.
Licence: Released under CC BY 4.0 — share and adapt for any purpose with
credit. Full text: LICENSE at the repository root.
Feedback & Support: If a rule here proves wrong or needs sharpening,
open an issue on the repository or contact the author at the profile link
above. If the problem is the agent not following a rule below rather than
the rule itself, that's an execution failure — acknowledge and correct it.
The core rule
Verification scope follows the contract, not the diff. When you change
something shared, the OLD name (the field you renamed, the enum member you
removed, the flag you retired) is the most complete index of its consumers
that exists. Grep for it across the ENTIRE workspace — source, tests, e2e
scripts, other apps, generated code, docs that assert behaviour — before you
call the change done, and run every suite that crosses the changed boundary.
A green suite in the package you edited proves nothing about the package that
imports it.
Checks
- Grep the retired name across everything, then delete it last. Before
removing or renaming a shared symbol,
grep -r the old name over the whole
repo (not just src/ of the current package). Each hit is a consumer you
must update or consciously leave. Do the rename everywhere, THEN delete the
old definition — deleting first turns the compiler/grep index you need into
a pile of unrelated errors. (A shared union gained a member; an exhaustive
switch and a Record<Union, ...> in a sibling package silently lost
exhaustiveness, and the editing package's tests never touched them.)
- Run the suites that cross the boundary, not the one you touched. A
contract change is verified by the consumers' tests, not the definer's. If
the shared thing is used by three apps and an e2e script, a passing unit
test in the shared library is the least informative signal available. Run
the cross-boundary suites, or state explicitly which you could not run.
(Illustrative: A shared function's failure mode changed from throwing to returning
null; the library's own suite passed, but the one consumer app's e2e
suite, which depended on the throw, was never run before merge, and the
null passed through unnoticed.)
- Mirrored and duplicated encodings count as consumers. If the same
intent is written in more than one place — an array whose order mirrors an
enum, a hardcoded list that must match a DB column set, a client copy of a
server type — the grep must find all copies. A contract with N copies has N
consumers even when only one is imported. (An endpoint's field list was
mirrored in a hand-maintained client array; adding a field server-side left
the client array one short, with no type error to catch it.)
Pre-flight check — before you call a shared change done
If any box is unchecked, the blast radius is unmeasured — go grep it.
1---2name: grep-the-blast-radius3description: Use when changing a shared contract — a flag, column, enum/union member, shared type, exported symbol, endpoint shape, or config key. The retired name is the cheapest complete index of consumers: grep the whole workspace and run every suite that crosses the boundary, not just the package you edited. Triggers on "rename", "change this type", "add/remove a field", "change the API shape", "update shared config", "who uses this", "will this break anything", and editing any code imported by more than one module.4---56# Grep the Blast Radius of a Contract Change78**Type:** Open-source — client-agnostic methodology, no project-specific detail.910**Created by akbarsha — https://github.com/iamakbarsha1**1112Distilled from repeated cases where a shared contract changed, the edited13package's tests stayed green, and sibling consumers broke silently.1415**Licence:** Released under CC BY 4.0 — share and adapt for any purpose with16credit. Full text: `LICENSE` at the repository root.1718**Feedback & Support:** If a rule here proves wrong or needs sharpening,19open an issue on the repository or contact the author at the profile link20above. If the problem is the agent not following a rule below rather than21the rule itself, that's an execution failure — acknowledge and correct it.2223## The core rule2425Verification scope follows the contract, not the diff. When you change26something shared, the OLD name (the field you renamed, the enum member you27removed, the flag you retired) is the most complete index of its consumers28that exists. Grep for it across the ENTIRE workspace — source, tests, e2e29scripts, other apps, generated code, docs that assert behaviour — before you30call the change done, and run every suite that crosses the changed boundary.31A green suite in the package you edited proves nothing about the package that32imports it.3334## Checks3536- **Grep the retired name across everything, then delete it last.** Before37 removing or renaming a shared symbol, `grep -r` the old name over the whole38 repo (not just `src/` of the current package). Each hit is a consumer you39 must update or consciously leave. Do the rename everywhere, THEN delete the40 old definition — deleting first turns the compiler/grep index you need into41 a pile of unrelated errors. *(A shared union gained a member; an exhaustive42 `switch` and a `Record<Union, ...>` in a sibling package silently lost43 exhaustiveness, and the editing package's tests never touched them.)*44- **Run the suites that cross the boundary, not the one you touched.** A45 contract change is verified by the consumers' tests, not the definer's. If46 the shared thing is used by three apps and an e2e script, a passing unit47 test in the shared library is the least informative signal available. Run48 the cross-boundary suites, or state explicitly which you could not run.49 *(Illustrative: A shared function's failure mode changed from throwing to returning50 null; the library's own suite passed, but the one consumer app's e2e51 suite, which depended on the throw, was never run before merge, and the52 null passed through unnoticed.)*53- **Mirrored and duplicated encodings count as consumers.** If the same54 intent is written in more than one place — an array whose order mirrors an55 enum, a hardcoded list that must match a DB column set, a client copy of a56 server type — the grep must find all copies. A contract with N copies has N57 consumers even when only one is imported. *(An endpoint's field list was58 mirrored in a hand-maintained client array; adding a field server-side left59 the client array one short, with no type error to catch it.)*6061## Pre-flight check — before you call a shared change done6263- [ ] You grepped the retired/old name across the WHOLE workspace (src +64 tests + e2e + other apps + generated), not just the edited package.65- [ ] Every consumer the grep found is updated, or consciously left with a66 reason.67- [ ] You ran the suites of the CONSUMERS that cross the changed boundary, or68 named the ones you couldn't run.69- [ ] Any mirrored/duplicated encoding of the same intent was found and70 reconciled.7172If any box is unchecked, the blast radius is unmeasured — go grep it.