Automatically remove dead code from OCaml projects by leveraging the power of .mli files
This tool automatically deletes code from your project.
- Always use version control - Commit your changes before running
prune- Review all changes - Use
--dry-runfirst to see what will be removed- Test thoroughly - Automated removal can miss edge cases
- Not for libraries - Removing exports can break downstream consumers
This tool was developed with significant AI assistance. While we've tested it extensively, AI-generated code can have subtle issues. The legal and ethical landscape around AI-generated code remains unsettled. See AI Transparency section below.
OCaml's module system with separate interface files (.mli) is one of
the language's greatest strengths, allowing you to precisely control
what gets exposed from your implementation. However, as projects
evolve, these interfaces tend to accumulate unused exports—functions,
types, and values that are no longer needed.
This problem is especially acute in AI-assisted development, where code generation tools often create comprehensive interfaces with many exports "just in case." Over time, this leads to significant code bloat and maintenance burden.
prune solves this by automatically detecting and removing unused
exports from your .mli files, along with their corresponding
implementations. This helps you:
- Reduce code size by eliminating dead code
- Improve maintainability by keeping only what's actually used
- Enhance API clarity by showing only the exports that matter
prune uses an iterative approach to remove dead code:
- Discovery: Scans your project for
.mlifiles and uses Merlin to analyze exports - Analysis: For each export, checks if it's used anywhere else in the codebase (apart from the assoaciated implementation)
- Removal: Removes unused exports from
.mlifiles - Iteration: After removal, the build may reveal new errors
(e.g., "value required but not provided").
pruneautomatically fixes these by removing the corresponding implementations - Convergence: Continues iterating until the build succeeds with no more removable code
This iterative approach is key—removing one piece of dead code often
reveals more dead code that was only kept around to support it. By
automatically handling the cascade of removals, prune can achieve
more thorough cleaning than a single-pass approach.
Unlike many dead code tools for other languages, prune leverages
OCaml's distinctive module system:
- Interface-driven: By analyzing
.mlifiles, prune knows exactly what's meant to be public API vs internal implementation - Type-safe removal: OCaml's strong typing ensures that if the code compiles after removal, it's definitely safe
- Module-aware: Handles OCaml's sophisticated module features including functors, module types, and nested modules
- Merlin-powered: Uses the same tool that powers your editor for accurate, project-wide analysis
dune build # compile
dune install # install the `prune` binary into the current OPAM switch# Show what would be removed from the current directory
prune show .
# Show unused exports in specific directory
prune show /path/to/project
# Show unused exports in specific files
prune show lib/foo.mli lib/bar.mli
# Mix files and directories
prune show lib/ src/important.mli
# Remove unused exports (interactive - will ask for confirmation)
prune clean .
# Remove without confirmation (force mode)
prune clean . --force
# or shorter:
prune clean . -f
# Single-pass mode (only one iteration)
prune clean . --step-wise
# Exclude test directories from analysis
prune show . --exclude-dirs test,_buildprune has two main commands:
- Safe way to see what would be removed
- No changes are made to your files
- Actually modifies your files
- Will prompt for confirmation unless
--forceis used
| Flag | Purpose | Default |
|---|---|---|
-f, --force |
Force removal without confirmation prompt | off |
-s, --step-wise |
Single-pass mode (only removes exports once) | off (iterative is default) |
--exclude-dirs |
Comma-separated list of directories to exclude | none |
-v, --verbose |
Increase verbosity (can be repeated: -vv) | off |
-h, --help |
Display help and exit | — |
prune doesn't just detect unused code—it automatically fixes the
cascade of errors that result from removal:
| Warning/Error | Description | What prune does |
|---|---|---|
| Warning 32 | Unused value declaration | Removes the entire let binding |
| Warning 33 | Unused open statement | Removes the open statement |
| Warning 34 | Unused type declaration | Removes the entire type declaration |
| Warning 69 | Unused record field | Removes the field or just the mutable keyword |
| Signature mismatch | "Value required but not provided" | Removes from .mli file |
| Unbound errors | References to removed code | Removes the referencing code too |
The real power comes from the iterative fixing: removing one unused export often triggers a chain reaction where prune automatically cleans up all the newly orphaned code.
- OCaml ≥ 5.3 with dune (for full Merlin support)
- Merlin (
ocamlmerlinin your$PATH)
prune is particularly effective for:
- AI-assisted projects: Where code generation creates many speculative exports
- Before using AI assistants: Smaller codebases mean lower token costs and better AI comprehension
- Refactoring: After major changes when old APIs are no longer needed
- Before releases: To minimize your public API surface
- Legacy codebases: To identify and remove years of accumulated dead code
- The analysis relies on Merlin's project view. Generated code or unusual dune rules can hide references
- Like all static analysis tools,
pruneshould be used with version control. Always review changes before committing
Bug reports and pull requests are welcome. Please run make test and
make fmt before submitting a patch.
MIT — see LICENSE.md for details.
Many thanks for the Merlin maintainers for an indispensable API that makes OCaml tooling possible.
This project was developed almost entirely using AI (Claude Code by Anthropic). While the tool has been tested extensively and works well in practice, users should be aware that:
-
Technical implications: AI-generated code may have unique patterns or subtle bugs. We've used
pruneon itself and other projects successfully, but thorough testing is always recommended. -
Legal uncertainty: The copyright status, license implications, and liability for AI-generated code remain legally untested. We cannot trace which training data influenced specific code patterns.
-
Practical use: Despite these unknowns,
prunehas been tested on real OCaml Projects and provide useful results. The tool is actively maintained and used in practice.
For deeper context on these issues, see the Software Freedom Conservancy and FSF positions on AI-generated code.
By using this tool, you acknowledge these uncertainties. As with any code modification tool: use version control, review all changes, and test thoroughly.