API change blast radius
Task. You're changing a public API — adding a required parameter to
PaymentClient.Charge, or making a previously-optional field mandatory. You
want the full reachable blast radius, not just direct callers, because
indirect behavioural changes matter for a public interface.
This is closely related to Refactor impact analysis, but for public APIs the depth and thoroughness matter more. You also want to know which tests exercise each path.
Tool sequence
1. Pin down the exact symbol
{ "name": "symbols", "arguments": { "name": "Charge", "kind": "method" } }
Pick the specific Charge on PaymentClient. Note its file, line, and
visibility — visibility: "public" confirms this is, in fact, an API call.
2. Full-depth callers, impl scope
{
"name": "dependencies",
"arguments": {
"symbol": "Charge",
"direction": "callers",
"depth": 5
}
}
Depth 5 is the maximum; for public APIs in a large codebase, you want it. Results come back in depth-order — direct callers at depth 1, their callers at depth 2, and so on. Each record includes the symbol, its file, and the depth at which it was reached.
Sort or filter by depth in the output:
shaktiman deps Charge --root . --direction callers --depth 5 --format json \
| jq 'sort_by(.depth) | group_by(.depth) | map({depth: .[0].depth, count: length})'
A typical response:
[
{ "depth": 1, "count": 3 },
{ "depth": 2, "count": 7 },
{ "depth": 3, "count": 14 },
{ "depth": 4, "count": 22 },
{ "depth": 5, "count": 35 }
]
Direct callers (depth 1) are the sites you must update. Depths 2+ are
behavioural blast radius — their correctness may depend on what Charge does,
even if they don't call it themselves.
3. Test-side blast radius
{
"name": "dependencies",
"arguments": {
"symbol": "Charge",
"direction": "callers",
"depth": 5,
"scope": "test"
}
}
Gives you the tests that will need updating — directly or transitively.
4. Consumer-side context
If your API is consumed by code outside your repo, dependencies can't see it.
For first-party repos you also publish:
- Run
dependencieson each repo separately (they have their own Shaktiman indexes). - Communicate the change before merging —
dependenciescan't cross repo boundaries.
5. Spot-check with search
{
"name": "search",
"arguments": {
"query": "PaymentClient Charge invocation",
"max_results": 30,
"scope": "all"
}
}
Ranked by relevance, this surfaces call sites the dependency graph might have missed — e.g. calls through function variables, interface assertions, or reflection — which the structural extractor doesn't catch.
Deciding whether to proceed
A rough triage rule:
- Depth 1 ≤ 5 callers → change is local; rename freely.
- Depth 1 in 10s → worth a deprecation window. Add the new signature, keep the old one wrapping the new, update callers over time.
- Depth 5 reaches most of the codebase → the API is load-bearing; break the change into multiple steps, each backwards-compatible.
Token math
- Without Shaktiman.
grep -r Charge, disambiguate from otherChargemethods, walk callers manually, re-grep for their callers. Days of work on a large codebase. - With Shaktiman. Two
dependenciescalls (impl + test) return the full reachable set in seconds. Triage rule runs itself.
See also
dependencies— direction and depth.- Refactor impact analysis — the non-public counterpart.
- Known Limitations — what
dependenciesmisses (indirect calls via function variables, cross-repo calls).