Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Competitive analysis

This document compares adotenv with common Rust .env parsers and loaders. The goal is to identify concrete work, not to rank libraries on a single number.

Comparison set

  • adotenv 0.10.4
  • dotenvy 0.15.7
  • dotenv 0.15.0
  • dotenv-compat 0.4.0
  • dotenv-ng-core 1.0.0
  • dotenv-parser 0.1.3
  • dotenv-linter 4.0.0 for CLI comparison

Method

Benchmarks use a release build on Linux x86_64. The fixture has 65,536 simple KEY=value entries and a 1.4 MB source. Each library reads the source before measurement, parses it, and builds a map where the library supports one.

  • Reference host: kbench, Intel Core i7-6700HQ, 4 cores and 8 threads, 2.6 GHz, Linux x86_64, rustc 1.97.0. The benchmark process was pinned to one CPU.
  • Wall time: hyperfine, ten process runs, multiple parses per run, using the median process time. CPU time is recorded separately.
  • Instructions: Valgrind callgrind, one parse, with a no-op baseline.
  • Memory: /proc/self/status high-water mark and retained RSS, with a no-op baseline.
  • Binary size: stripped release binary with thin LTO for a parse-and-print program.
  • Dependencies and MSRV: crates.io metadata.

Run python3 scripts/compare.py --update-doc to regenerate the benchmark section on an idle host. The tables below were recorded on kbench. Raw results are stored in benchmarks/compare/results.json.

Results

Generated by python3 scripts/compare.py on kbench with rustc 1.97.0 (2d8144b78 2026-07-07).

Parse and map build

Per parse on the 65,536-entry fixture:

ImplementationWall timeCPU timeInstructionsPeak RSSRetained RSS
adotenv parse34.9 ms34.8 ms207M26.5 MB26.5 MB
adotenv parse + values54.4 ms54.2 ms366M10.0 MB10.0 MB
adotenv parse + map42.4 ms42.3 ms277M32.6 MB14.2 MB
dotenvy iterator + map68.7 ms68.5 ms361M18.7 MB11.2 MB
dotenv-compat parse50.0 ms49.8 ms272M19.9 MB14.5 MB
dotenv-ng input-only93.7 ms93.4 ms384M26.2 MB20.2 MB
dotenv-parser map162.2 ms161.9 ms1242M27.2 MB10.6 MB

Small-file parsing, in microseconds per parse:

Implementation128 entries
adotenv parse52.2 us
adotenv parse + values76.8 us
adotenv parse + map70.8 us
dotenvy iterator + map106.9 us
dotenv-compat parse78.1 us
dotenv-ng input-only105.7 us
dotenv-parser map269.5 us

Mixed fixture

Per parse on a 4,096-block mixed fixture with quoting, spacing, assigned values, Unicode and comments:

ImplementationWall timeCPU timeInstructionsPeak RSSRetained RSS
adotenv parse20.6 ms20.5 ms106M19.5 MB11.0 MB
adotenv parse + values22.7 ms22.6 ms139M3.8 MB3.8 MB
adotenv parse + map27.6 ms27.5 ms153M19.2 MB5.8 MB
dotenvy iterator + map25.8 ms25.8 ms158M5.6 MB4.2 MB
dotenv-compat parse17.7 ms17.6 ms108M7.4 MB4.8 MB
dotenv-ng input-only24.5 ms24.4 ms137M7.6 MB6.1 MB
dotenv-parser map63.7 ms63.6 ms471M11.7 MB4.1 MB

Loader profiles

Per load on the 65,536-entry fixture:

ProfileWall timeCPU timeInstructionsPeak RSSRetained RSS
values-only load54.9 ms54.8 ms366M11.2 MB9.9 MB
default load73.9 ms73.7 ms465M47.7 MB27.9 MB
full-report load120.1 ms119.7 ms687M57.5 MB37.7 MB

Real-world corpus

Per parse on the pinned public corpus, repeated with unique key prefixes:

ImplementationWall timeCPU timeInstructionsPeak RSSRetained RSS
adotenv parse9.4 ms9.4 ms63M7.7 MB4.2 MB
adotenv parse + values9.5 ms9.5 ms66M1.2 MB1.2 MB
adotenv parse + map11.2 ms11.2 ms78M8.3 MB2.4 MB
dotenvy iterator + map11.4 ms11.3 ms72M2.7 MB1.7 MB
dotenv-compat parse7.8 ms7.8 ms56M4.0 MB2.0 MB
dotenv-ng input-only10.9 ms10.9 ms72M3.6 MB2.8 MB
dotenv-parser map36.1 ms36.0 ms277M6.1 MB1.4 MB

Dialect matrix

Per parse on the 65,536-entry simple fixture:

DialectWall timeCPU timeInstructionsPeak RSSRetained RSS
Node60.6 ms60.4 ms423M32.8 MB14.3 MB
Python50.6 ms50.4 ms329M32.6 MB14.3 MB
Compose51.7 ms51.5 ms305M32.5 MB14.2 MB
Docker42.7 ms42.5 ms269M32.6 MB14.3 MB

Parse-only binaries

BinaryStripped size
adotenv454 KB
dotenv_compat367 KB
dotenv_ng404 KB
dotenv_parser450 KB
dotenvy386 KB

Wall time varies with host load. Instruction counts are the stable CPU metric. adotenv parse uses fewer instructions than every measured alternative. Its parse-plus-map path uses more instructions than dotenv-compat and fewer than dotenvy. adotenv also retains more document memory because it keeps spans, items and diagnostics. The values-only projection path is the correct comparison for applications that do not need provenance.

Footprint

CrateRequired normal dependenciesMSRV
adotenv01.81
dotenvy01.56
dotenv0not declared
dotenv-compat41.85
dotenv-ng-core01.74
dotenv-parser2not declared

adotenv has no required runtime dependencies and supports no_std with alloc. Its parse-only binary is within about 22 percent of the smallest measured binary.

Capability matrix

Capabilityadotenvdotenvydotenvdotenv-compatdotenv-ngdotenv-parser
Literal parseYesYesYesYesYesYes
Map or iterator APIBothIteratorLoaderMapMapMap
Lossless sourceYesNoNoNoNoNo
Token spans and positionsYesNoNoNoNoNo
Recovery diagnosticsYesLimitedNoLimitedErrorsErrors
Multiple syntax presets511Node11
Verified edits and formattingYesNoNoNoNoNo
Sort, compare and examplesYesNoNoNoNoNo
Reference inspectionYesNoNoNoNoNo
Explicit resolutionYesNoNoNoSubstitutionNo
Cascade loading and provenanceYesSearch/loadSearch/loadFilesMultiple filesNo
Values-only projectionYesNoNoNoNoNo
SerdeYesNoNoNoNoNo
CLIYesNoNoNoNoNo
no_stdYesNoNoNoNoNo

dotenv-linter is a separate CLI category. It provides lint rules and fixes but does not expose adotenv’s library model or loader provenance.

Correctness

adotenv has a pinned conformance corpus for Node, Python, Compose and Docker behavior. It also has parser, edit, encoding, resolution and formatting fuzz targets.

The main gap is breadth, not absence. Add more pinned symbols and differential checks against Node and Python, especially for malformed and multiline cases. Keep every observation tied to a source identity and input hash.

Documentation

adotenv has 20 public documents covering architecture, dialects, edits, formatting, resolution, loading, serde, CLI, integrations, quality and performance. Examples in the README are doctested.

Gaps:

  • no dedicated documentation site;
  • no guided tutorial or task-based examples;
  • design examples are not compiled;
  • no searchable API-to-task index;
  • no published benchmark dashboard;
  • adotenv.github.io is unavailable because there is no adotenv GitHub organization.

A project site can use asamarts.github.io/adotenv now, or a custom domain, or an adotenv organization if that name can be created.

API assessment

Strengths:

  • one parse result supports inspection, edits, comparison and formatting;
  • policy is explicit and composable;
  • errors and diagnostics are structured;
  • loading and process mutation stay opt-in;
  • values, origins and reports are borrowed views.

Risks:

  • the public surface is broad for new users;
  • similar concepts use several types (Document, Entry, Item, views, LoadedEnvironment, ResolvedEnvironment);
  • examples are spread across the README and design docs;
  • the migration from parse to edit to load is not presented as a single guided path.

The next step is a task-based API audit, not a redesign.

Priorities

  1. Commit a reproducible comparison harness and keep these numbers current.
  2. Build a documentation site with a tutorial, task guides and search.
  3. Improve values-only performance and memory measurements against the same fixture.
  4. Expand conformance and differential tests.
  5. Audit common-task APIs and consolidate examples.
  6. Close feature gaps only where they fit the literal, explicit design. File encryption, implicit environment search and ambient command execution are not automatic goals.
  7. Complete the 1.0 evidence for fuzzing and external adoption.