Find dead code across a repository
A four-step pass that maps the repo, lists exported symbols, checks each one for real callers and produces a deletion list you can actually act on, with the risky cases separated from the safe ones.
Tested on Claude Sonnet · 4.5 ·
-
Map the repository first
Do not start from a file. Ask for the shape of the project so later steps have something to anchor to, and so you notice immediately if the model has misread the structure.
Step 1 Here is the file tree of a repository. Give me, in under 200 words: the entry points, the framework in use, and where the boundaries are between modules. Do not summarise what each file does. {{file_tree}} -
List every exported symbol
One pass per module, not the whole repo at once. Long input makes the model start guessing at names it has not seen.
Step 2 For this module, list every exported function, class and constant, one per line, as: name | file | line. No commentary. {{module_source}} -
Check each symbol for callers
Feed the list back with the search results. The model is not searching your codebase, you are - it is deciding what the results mean.
Step 3 Here is an exported symbol and every textual match for its name in the repository. Decide: is it called anywhere in production code, only in tests, or nowhere? Answer with one word - USED, TESTS_ONLY or UNUSED - then one line of evidence. Symbol: {{symbol}} Matches: {{grep_output}} -
Separate safe from risky before you delete
This step is the reason the workflow is worth running. Anything reached by reflection, a route table, a plugin loader or a serialised class name will look unused and is not.
Step 4 Here is a list of symbols marked UNUSED. For each one, say whether it could still be reached at runtime through: a route or URL table, reflection or dynamic dispatch, a plugin or hook registry, serialisation by class name, or a public package API. Output two lists: SAFE TO DELETE and CHECK BY HAND, with the reason for anything in the second list. {{unused_list}}
Running this on a medium repository takes about twenty minutes, most of it waiting. The output is a list you review, not a change you apply blindly: dynamic dispatch, reflection and framework entry points all look like dead code to a model, so the last step exists to catch them.
Notes from the author
Step three is where people cut corners and it is the step that makes the result trustworthy. If you skip the grep and let the model guess whether something is called, you get a confident list of symbols that are very much in use. On a repository with heavy dependency injection, expect most of the output to land in CHECK BY HAND - that is the workflow working, not failing.
Tagged