Find and remove dead code and unused APIs in OCaml projects
at main 230 lines 8.2 kB view raw view rendered
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.