Dependency Philosophy
Status
Accepted
Context
Ferroni is a pure-Rust regex engine that ships as a single crate. The question is how many third-party dependencies to take on, and under what criteria.
Decision
Ferroni uses four runtime dependencies, each chosen because it solves a specific problem better than hand-rolling:
| Crate | Purpose | Transitive deps |
|---|---|---|
aho-corasick | Aho-Corasick automaton for single-pass literal alternation search | 1 (memchr) |
bitflags | Type-safe option flags replacing type OnigOptionType = u32 | 0 |
memchr | SIMD-vectorized byte search for the forward-scan pipeline (ADR-007) | 0 |
smallvec | Stack-allocated small vectors for ScannerMatch::capture_indices | 0 |
Each was selected because:
- Zero or near-zero transitive dependencies. No dependency tree surprises.
- Battle-tested in the Rust ecosystem. Used by
regex,ripgrep,serde, and other foundational crates. - Solves a specific problem better than hand-rolling.
aho-corasickprovides a production-grade Aho-Corasick automaton with Teddy SIMD prefilters for single-pass multi-pattern matching — hand-rolling this correctly for all architectures is non-trivial;memchrprovides SIMD dispatch across architectures;bitflagsprovides compile-time flag safety;smallveceliminates heap allocation for the common case. - No feature creep. Each crate does one thing.
Dev dependencies
| Crate | Purpose |
|---|---|
criterion / codspeed-criterion-compat | Benchmark framework + CI regression tracking |
serde_json | Parsing Shiki grammar JSON in benchmarks |
cc | Building C Oniguruma for benchmark comparison (optional, behind ffi feature) |
The cc crate is gated behind the ffi feature and only used for benchmark comparisons -- it is never part of a normal cargo build.
Rationale
Every dependency is a trust decision. A convenience crate that saves 20 lines of code adds audit burden, supply-chain risk, and a potential MSRV constraint. Ferroni's target audience (syntax highlighters, editors, language tooling) values reliability over developer convenience.
Consequences
- Adding a new runtime dependency requires explicit justification against the criteria above.
- The total dependency tree for
cargo build(no features) stays minimal and auditable. - No framework-style dependencies, no proc-macro dependencies at runtime, no
build.rsin normal builds.