# Coming from dotenv

> A dotenv alternative with a drop-in path: import dotenv from seniority/dotenv, graded 106 / 141 by dotenv's own test suite (the declined .env.vault is most of the gap) — then the same package resolves every value across flags, env, config files and defaults, with provenance as text, --json data or an agent event.

Source: https://seniority.interlace.tools/docs/coming-from/dotenv

**seniority** is a **dotenv alternative** that also covers what `cosmiconfig`, `rc` and
`find-up` do beside it: one resolution for flags, environment, config files, a
`package.json` field and defaults, in a fixed order, with no dependencies.

## Migrate from dotenv in one import

```diff
- import dotenv from 'dotenv';
+ import dotenv from 'seniority/dotenv';
```

`seniority/dotenv` carries dotenv 17's `config`, `parse` and `populate`, as named exports and
on a default export shaped like dotenv's `module.exports`. `parse` is dotenv's line grammar,
character for character. `config()` behaves as dotenv's does when told nothing: it reads
`./.env` from the working directory into `process.env`, and it takes dotenv's options —
`path` (one file or several, a `URL`, a leading `~`), `processEnv`, `encoding`, `override`
and `debug` — returning `{ parsed }` or `{ parsed, error }` rather than throwing for a missing
file.

**There is no `dotenv/config` preload entry.** Where you had `import 'dotenv/config'` or
`node -r dotenv/config`, call it once at your entry point:

```js
import { config } from 'seniority/dotenv';

config();
```

**Not built, on purpose:** `decrypt`, `DOTENV_KEY` and the `.env.vault` format, which dotenv
deprecated in favour of dotenvx, and `config`'s `quiet` banner and tips.

## Is seniority compatible with dotenv?

Not fully, and the number says so. dotenv's own suite, vendored at 17.4.2 and unmodified
apart from the import specifier, runs against `seniority/dotenv` beside a control that runs
it against real dotenv:

| | passing | rate |
| :-- | --: | --: |
| `seniority/dotenv` | 106 / 141 | 75.2% |
| dotenv itself (control) | 141 / 141 | 100.0% |

From [Compatibility](https://burgee.interlace.tools/docs/compatibility), which `npm run compat:page` generates from the
oracle's last run; that page is the authority. The suite runs under node-tap, whose plan
counts assertions rather than test cases, so 141 is assertions. Of the 35 not passing, the
oracle's row accounts for each: 27 are the vault and `decrypt` path and 2 are dotenvx tips,
both declined above, and 6 load dotenv's private `lib/*` modules by path, which the harness
maps to the package root. Below 100%, seniority is never described as dotenv-*compatible*;
this is a grade.

The same page grades `seniority` (cosmiconfig's own suite), `seniority/lilconfig` and
`seniority/rc`.

## What you gain over dotenv

dotenv puts a file into the environment. seniority resolves the value, and keeps the
receipt:

- **Provenance on every value.** `resolve()` returns each option's winning source and every
  candidate it beat. `explain('region', result)` prints
  `region = "eu-2"   from config file ./app.config.json` with the unset flag and env
  candidates beneath it.
- **The same record for agents.** `explanationJson()` is that record as `--json` data, and
  `explanationEvent()` is it as an agent event — generated by the code that picked the value,
  so neither can drift from the text.
- **An order nobody can rearrange.** `flag > env > config file > package.json field >
  default`, exported as `ORDER`. `populate` already keeps a key the environment holds unless
  you pass `override`; `resolve` states that rule once for every source, and a plugin can add
  a source between them but can never beat the flag the user typed.
- **`resolve` is pure.** No filesystem, no `process.env`: the environment is passed in, so it
  is testable without a process. Only the drop-in façades reach for `process.env` by default,
  because their incumbents do.

## When to switch from dotenv

- "Why is this set to that?" is a question your users or agents ask, and the answer depends
  on whether a flag, the environment or a config file won.
- You load `.env` and then hand-write the order in which a flag, an env var and a config file
  override each other.
- You do not use `.env.vault` or `DOTENV_KEY`.

The API, the discovery rules and the other drop-in paths are on
[seniority](/docs).
