Find and remove dead code and unused APIs in OCaml projects
1# prune
2
3<p align="center">
4 <img src="prune.png" alt="prune logo" width="200">
5</p>
6
7<p align="center">
8 <em>Automatically remove dead code from OCaml projects using .mli file analysis</em>
9</p>
10
11---
12
13## Important Warnings
14
15> **This tool automatically deletes code from your project.**
16>
17> - **Always use version control** - Commit your changes before running `prune`
18> - **Review all changes** - Use `--dry-run` first to see what will be removed
19> - **Test thoroughly** - Automated removal can miss edge cases
20> - **Not for libraries** - Removing exports can break downstream consumers
21>
22> This tool was developed with significant AI assistance. While we've tested it
23> extensively, AI-generated code can have subtle issues. The legal and ethical
24> landscape around AI-generated code remains unsettled. See [AI Transparency](#ai-transparency)
25> section below.
26
27---
28
29## Why prune?
30
31OCaml's module system with separate interface files (`.mli`) is one of
32the language's greatest strengths, allowing you to precisely control
33what gets exposed from your implementation. However, as projects
34evolve, these interfaces tend to accumulate unused exports—functions,
35types, and values that are no longer needed.
36
37**This problem is especially acute in AI-assisted development**, where
38 code generation tools often create comprehensive interfaces with
39 many exports "just in case." Over time, this leads to significant
40 code bloat and maintenance burden.
41
42`prune` solves this by automatically detecting and removing unused
43exports from your `.mli` files, along with their corresponding
44implementations. This helps you:
45
46- **Reduce code size** by eliminating dead code
47- **Improve maintainability** by keeping only what's actually used
48- **Enhance API clarity** by showing only the exports that matter
49
50---
51
52## How it works
53
54`prune` uses an iterative approach to remove dead code:
55
561. **Discovery**: Scans your project for `.mli` files and uses Merlin
57 to analyse exports
582. **Analysis**: For each export, checks if it's used anywhere else in
59 the codebase (apart from the assoaciated implementation)
603. **Removal**: Removes unused exports from `.mli` files
614. **Iteration**: After removal, the build may reveal new errors
62 (e.g., "value required but not provided"). `prune` automatically fixes
63 these by removing the corresponding implementations
645. **Convergence**: Continues iterating until the build succeeds with
65 no more removable code
66
67This iterative approach is key—removing one piece of dead code often
68reveals more dead code that was only kept around to support it. By
69automatically handling the cascade of removals, `prune` can achieve
70more thorough cleaning than a single-pass approach.
71
72## What makes prune unique for OCaml?
73
74Unlike many dead code tools for other languages, `prune` leverages
75OCaml's distinctive module system:
76
77- **Interface-driven**: By analysing `.mli` files, prune knows exactly
78 what's meant to be public API vs internal implementation
79- **Type-safe removal**: OCaml's strong typing ensures that if the
80 code compiles after removal, it's definitely safe
81- **Module-aware**: Handles OCaml's sophisticated module features
82 including functors, module types, and nested modules
83- **Merlin-powered**: Uses the same tool that powers your editor for
84 accurate, project-wide analysis
85
86---
87
88## Installation
89
90```bash
91dune build # compile
92dune install # install the `prune` binary into the current OPAM switch
93```
94
95## Basic usage
96
97```bash
98# Show what would be removed from the current directory
99prune show .
100
101# Show unused exports in specific directory
102prune show /path/to/project
103
104# Show unused exports in specific files
105prune show lib/foo.mli lib/bar.mli
106
107# Mix files and directories
108prune show lib/ src/important.mli
109
110# Remove unused exports (interactive - will ask for confirmation)
111prune clean .
112
113# Remove without confirmation (force mode)
114prune clean . --force
115# or shorter:
116prune clean . -f
117
118# Single-pass mode (only one iteration)
119prune clean . --step-wise
120
121# Exclude test directories from analysis
122prune show . --exclude-dirs test,_build
123```
124
125## Commands and options
126
127`prune` has two main commands:
128
129### `prune show` - Display unused exports without removing them
130- Safe way to see what would be removed
131- No changes are made to your files
132
133### `prune clean` - Remove unused exports
134- Actually modifies your files
135- Will prompt for confirmation unless `--force` is used
136
137### Common options
138
139| Flag | Purpose | Default |
140|------|---------|---------|
141| `-f`, `--force` | Force removal without confirmation prompt | off |
142| `-s`, `--step-wise` | Single-pass mode (only removes exports once) | off (iterative is default) |
143| `--exclude-dirs` | Comma-separated list of directories to exclude | none |
144| `-v`, `--verbose` | Increase verbosity (can be repeated: -vv) | off |
145| `-h`, `--help` | Display help and exit | — |
146
147## Automatic error fixing
148
149`prune` doesn't just detect unused code—it automatically fixes the
150cascade of errors that result from removal:
151
152| Warning/Error | Description | What prune does |
153|---------|-------------|-----------------|
154| Warning 32 | Unused value declaration | Removes the entire `let` binding |
155| Warning 33 | Unused open statement | Removes the `open` statement |
156| Warning 34 | Unused type declaration | Removes the entire type declaration |
157| Warning 69 | Unused record field | Removes the field or just the `mutable` keyword |
158| Signature mismatch | "Value required but not provided" | Removes from `.mli` file |
159| Unbound errors | References to removed code | Removes the referencing code too |
160
161The real power comes from the iterative fixing: removing one unused
162export often triggers a chain reaction where prune automatically
163cleans up all the newly orphaned code.
164
165## Requirements
166
167- OCaml ≥ 5.3 with dune (for full Merlin support)
168- Merlin (`ocamlmerlin` in your `$PATH`)
169
170## When to use prune
171
172`prune` is particularly effective for:
173
174- **AI-assisted projects**: Where code generation creates many speculative exports
175- **Before using AI assistants**: Smaller codebases mean lower token costs and better AI comprehension
176- **Refactoring**: After major changes when old APIs are no longer needed
177- **Before releases**: To minimize your public API surface
178- **Legacy codebases**: To identify and remove years of accumulated dead code
179
180
181## Limitations
182
183- The analysis relies on Merlin's project view. Generated code or
184 unusual dune rules can hide references
185- Like all static analysis tools, `prune` should be used with version
186 control. Always review changes before committing
187
188## Contributing
189
190Bug reports and pull requests are welcome. Please run `make test` and
191`make fmt` before submitting a patch.
192
193## Licence
194
195MIT — see LICENSE.md for details.
196
197## Acknowledgements
198
199Many thanks for the [Merlin](https://github.com/ocaml/merlin)
200maintainers for an indispensable API that makes OCaml tooling
201possible.
202
203## AI Transparency
204
205**This project was developed almost entirely using AI** ([Claude
206 Code](https://www.anthropic.com/claude-code) by Anthropic). While
207 the tool has been tested extensively and works well in practice,
208 users should be aware that:
209
2101. **Technical implications**: AI-generated code may have unique
211patterns or subtle bugs. We've used `prune` on itself and other
212projects successfully, but thorough testing is always recommended.
213
2142. **Legal uncertainty**: The copyright status, license implications,
215and liability for AI-generated code remain legally untested. We cannot
216trace which training data influenced specific code patterns.
217
2183. **Practical use**: Despite these unknowns, `prune` has been tested
219on real OCaml Projects and provide useful results. The tool is
220actively maintained and used in practice.
221
222For deeper context on these issues, see the [Software Freedom
223Conservancy](https://sfconservancy.org/blog/2022/feb/03/github-copilot-copyleft-gpl/)
224and [FSF
225positions](https://www.fsf.org/blogs/licensing/fsf-funded-call-for-white-papers-on-questions-around-copilot/)
226on AI-generated code.
227
228**By using this tool, you acknowledge these uncertainties.** As with
229 any code modification tool: use version control, review all changes,
230 and test thoroughly.