A static review, human and AI, both mark processRefundLegacy as unused. Nothing imports it directly, no test calls it, and the diff to remove it looks like a clean deletion. It is reached from a string key in a dispatch table that only activates under a non-default payment flow, and deleting it breaks that flow on the next release.

A function can be reached without a direct call

Many codebases route behavior through a registry instead of a conditional chain. A handlers object maps string keys to functions: standard_refund points at processRefundStandard, legacy_refund points at processRefundLegacy, and a runtime lookup such as handlers[config.refundFlow] selects the entry instead of an if chain.

The function exists as a reference inside the map, but no line calls it with parentheses. Static reachability follows direct calls, imports, and type references. It sees a name stored in an object and does not expand which string value selects it, so it reports the function as never called. An AI reviewer reading the file tends to trust that same signal and generalizes "unused, safe to remove" without expanding the registry.

The same pattern hides callers in plugin manifests, feature-flag maps, per-tenant config, dynamic import by key, and event-bus topic registrations. In every case the caller is a string, not a symbol.

What ships when the helper is removed

  • The typecheck passes. The function had no direct caller to break, so removing it produces no compile error.
  • The default-path tests stay green. The legacy key is never set under the default test configuration, so the exercised suite never touches the gated function.
  • Production fails on a real refund. A tenant on the legacy flow reads config.refundFlow = "legacy_refund", looks the key up, gets undefined, and fails on the handler call. The error surfaces after release, usually as a missing-handler exception rather than a clean test failure.

The deletion diff itself looks tidy: one function gone, fewer lines, no visible import to update. That tidiness is exactly why it slips past review.

Prove reachability before deleting; do not assume it

Treat "no direct caller" as a proposal to investigate, not a finding to act on:

  • Grep the symbol and its registration key. Search the function name, then every string it could be registered under. The caller lives in the map value, and the key lives somewhere in config.
  • Expand the dispatch table. List each string key that points to the function and the lookup expression that reads the selected key, such as handlers[config.refundFlow].
  • Trace the key back to its source. Find where that string is set: environment variable, feature flag, per-tenant setting, deployment manifest, or migration record. A non-default setting can still be in use for a subset of traffic.
  • Check whether tests cover the gated path. A suite that only runs under default config proves the default path works; it says nothing about the gated one.
  • Require evidence before deletion. Keep the function if the only proof of liveness is a non-default config, and label it config-gated rather than dead. Remove it only after a recorded caller trace shows no environment selects the key.

Dead code is proven, not assumed

An unused-looking function inside a registry is dead only when no reachable configuration selects its key. Static "unused" reports are reliable for direct-call codebases and unreliable the moment routing passes through a string or a manifest. Before approving a cleanup that removes a helper, expand the map, trace the key, and confirm the gated path has no live tenant.