Skip to content

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:

CratePurposeTransitive deps
aho-corasickAho-Corasick automaton for single-pass literal alternation search1 (memchr)
bitflagsType-safe option flags replacing type OnigOptionType = u320
memchrSIMD-vectorized byte search for the forward-scan pipeline (ADR-007)0
smallvecStack-allocated small vectors for ScannerMatch::capture_indices0

Each was selected because:

  1. Zero or near-zero transitive dependencies. No dependency tree surprises.
  2. Battle-tested in the Rust ecosystem. Used by regex, ripgrep, serde, and other foundational crates.
  3. Solves a specific problem better than hand-rolling. aho-corasick provides 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; memchr provides SIMD dispatch across architectures; bitflags provides compile-time flag safety; smallvec eliminates heap allocation for the common case.
  4. No feature creep. Each crate does one thing.

Dev dependencies

CratePurpose
criterion / codspeed-criterion-compatBenchmark framework + CI regression tracking
serde_jsonParsing Shiki grammar JSON in benchmarks
ccBuilding 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.rs in normal builds.