Comparison
Where mmz sits among build systems, task runners and compiler caches — and which of them you should reach for instead.
mmz is not a build system, and most of what follows is about which tool you should be using instead of — or alongside — it.
| mmz | make / ninja | Bazel / Buck | Turborepo / Nx | sccache / ccache | |
|---|---|---|---|---|---|
| Unit of work | a command you name | a file target | a declared action | a package task | a compiler invocation |
| Declares deps | you do, as globs | you do, per target | you do, exhaustively | inferred from the graph | the compiler does |
| Ordering | none | graph | graph | graph | none |
| Replays output | exit code only | — | full artifacts | logs + artifacts | object files |
| Remote cache | never | — | first-class | first-class | optional |
| Language-aware | none | none | rulesets | JS-centric | C/C++/Rust |
| Setup cost | one YAML file | a Makefile | a build system migration | a workspace config | a wrapper binary |
| Wrong-answer risk | an under-declared scope | a missing prerequisite | sandboxed | an inferred edge | hash of the real inputs |
When to use something else
You need ordering
If the answer to “what runs first?” is anything but “my task runner already knows”, mmz is the wrong tool. It has no graph and will not grow one — that is make, ninja, just, or your CI’s own dependency syntax. mmz goes on one of those, one line at a time.
You need the artifacts back
mmz caches an exit code, never output. A hit on cargo build means the build was not re-run — it does not conjure target/ back into existence. If you want a cache that restores artifacts across machines, that is Bazel, Turborepo, or a language-specific cache, and it costs the modelling those tools require.
Declared outputs are the deliberate half-measure here: mmz checks that an artifact still exists, so a cargo clean voids the record instead of leaving it fresh. It never stores or restores one.
You want compilation caching
sccache and ccache operate one compiler invocation below where mmz sits, and they know how to hash a compiler’s real inputs because they are language-aware. The two compose fine: mmz skips the whole cargo test, and sccache speeds up the runs it does not skip.
What mmz is actually for
The gap those tools leave: a project-level check that is expensive, that you run constantly, and whose real dependency is a set of files you can name.
- A test suite, linter or formatter check on a Justfile line.
- An expensive verification a pre-push hook wants to require rather than run — see Gating with tags.
- A slow generation step whose artifact must still be there — see declared outputs.
In every case the value comes from the same trade: you declare the inputs by hand, in one readable file, and get memoization without adopting a build system.
mmz cannot see a dependency you did not declare, and a wrongly-fresh rule is silent. A tool that sandboxes or traces its inputs makes that impossible instead — at a setup cost mmz exists precisely to avoid. Choose accordingly, and read the correctness contract before you do.