Your Own Bootleg Deprecator
If you find yourself working with really, really legacy code, sometimes your typical workflow is going to fail. When you’re doing work with a reasonably popular, type-safe language, your tools will often prove very useful. If a function, module, or class is unused, a good editor can make note of this, and safely remove it for you. Yay.
There are plenty of ways that process can end up difficult even with modern tools, but where you really get into trouble is when you have to work with old code, for old legacy frameworks, written in old, poorly-supported languages.
And god help you if the whole thing’s loosely-typed.
The Task
In our most recent case, the job was to analyze a large Classic ASP codebase and delete unused code files. The idea being that future work to properly migrate away from this legacy stuff would at least be made easier by jumping off from a smaller starting point.
On this project, for those keeping score at home, that meant slogging through a lot of visual basic.
But where do you turn when Right-Click => Find Usages is no longer an option?
When IDE support has been deprecated for years?
When there’s so much manual string manip. you can barely see straight?
The Analysis
There are, of course, several approaches available here, but what ultimately ended up working was a hodge-podge application of your typical UNIX tools until the results started to take shape.
Lucky for us, there were logs available for every public-facing file a user had hit, and for a good chunk of time. It’s not the sort of resource that’s always going to be at your disposal, but keep an eye out for places you might be storing similar data. Or, start storing it now!
Depending on the language, or framework, or how your project is organized, the following script may still be useful for finding dead code, with or without such a list. But I’ll be the first to admit that it’s quite a bit harder to feel confident getting rid of things, if you don’t know how they’re actually right now.
#!/bin/bash
if [[ "$1" =~ "^-h|--help$" ]]; then
echo "Scans the current directory for any files whose names"
echo "aren't referenced in another file"
echo
echo "USAGE: $0 [KNOWN_USED_FILES]"
echo
echo "Specifying a file containing a list of known used files"
echo "will prevent those files from being reported as unused."
echo "Otherwise, any file whose name is not mentioned will"
echo "be reported."
exit 1
fi
known_used_files="${1:-/dev/null}"
# Get basenames of all not-gitignored files in the current directory.
function all-basenames {
# fd implicitly ignores .gitignored files, but you can probably
# use find with a few excludes, if 'fd' is not available for you.
if ! command -v fd &> /dev/null; then
echo "Script requires 'fd' to function!"
exit 1
fi
fd --type file | xargs -I %% basename %%
}
total=0
unused=0
>&2 echo "Scanning for unused files..."
>&2 echo
while read -r file; do
((total++))
file_no_ext="$(echo "$file" | sed 's/\(.*\)\..*/\1/')"
if ! grep -iFq "$file_no_ext" "$known_used_files" &&
! grep -RIsiFq -- "$file_no_ext"; then
((unused++))
echo "$file"
fi
done < <(all-basenames)
>&2 echo
>&2 echo "$unused/$total scanned files appear unused"
This is a fairly brute-force approach, merely scanning project files for file names, but when good syntax-aware tooling isn’t available, it’s at least a start. For what it’s worth, the original version of this script was used to automate the deletion of >800 files from a severely aging repo. Or, about a quarter of the total file count. (N.b. that script was a bit more intelligent than this one, with some ASP-specific tweaks, and working slightly less dumbly outward from the afrementioned list of known-used files. Unfortunately, it has since been lost to time.)
All of that to say, when working with legacy code, sometimes the ugly solution puts you on the path toward the beautiful one. You can’t let perfect be the enemy of good, but sometimes you end up in a spot where you can’t let good be the enemy of better.
Thanks!
Slightly shorter article today, but please, do reach out if you found this at all helpful, or if you have a similar task in front of you that we might be able to help out with. Either way, thanks for reading!