romanzipp/laravel-env-diff
A Laravel package for comparing .env files side by side, highlighting missing, extra, and mismatched keys so you can spot configuration drift between environments before it causes deployment surprises.
Architecture fit. romanzipp/laravel-env-diff remains a leaf-node developer tool: a service provider, one artisan command, no domain code, no runtime surface. 2.0.0 does add a config file (config/env-diff.php), which is a small but real expansion of the package's footprint — it is no longer purely "command in, command out," it now owns a publishable config artifact with a schema that has already changed once (see below). Fit is still low-risk by construction (require-dev, removable), but the package now has a configuration contract our repo would inherit and must keep in sync.
Integration feasibility. Mechanically still simple, but no longer "no config publishing." 2.0.0 introduces a config file with files, path, and show_values, and renames the command to env:diff. Integration therefore includes a vendor:publish step and a committed config, plus updated runbook references. Composer auto-discovery still applies. The friction is unchanged in kind — version constraints — and now slightly higher in effort because of the new config surface.
Technical risk.
--ignore-platform-reqs remains unacceptable in a governed repo.'show_values' => false is the default, with values behind an explicit --values flag / config toggle. Key-only output is now the out-of-the-box behavior. Residual risk is operational, not design: someone enables --values in a CI job whose logs are externally published.# comments, escaped $, ${FOO} interpolation, duplicate keys — remain unvalidated. No sign of a fixture-based test suite over these cases.path of app_path() (medium). Defaulting env-file resolution to app_path() rather than base_path() is surprising. If the config is published and path is left at default, the command can read the wrong directory, compare two empty/absent files, and report a clean diff — a fail-open in a correctness gate. This is a concrete false-negative path introduced by 2.0.0.Key questions.
show_values => false by default, --values opt-in. Confirm the flag cannot be triggered by a stray env var in CI.composer.json resolve on our target Laravel/PHP matrix? Given the stated Laravel 7 / PHP 7.4 ceiling, assume no until proven..env" from "extra in .env" from value drift, or only diff key sets? With files now an array, what is the semantics for three or more files — pairwise, or first-vs-rest?path => app_path(), what does the command do when a configured file is absent — error, or silent empty comparison? Fail-open must be ruled out.additional_files → files?Stack fit. Unchanged: require-dev only, developer workstation and CI. No production image. The value proposition is still the "env key added to code, forgotten in .env.example" defect class, and 2.0.0 makes that slightly more credible by defaulting to masked output and by letting you list both files explicitly rather than only "additional" ones.
Migration path. Two paths, and the recommendation is unchanged — reimplement — but 2.0.0 shifts the argument's basis.
vlucas/phpdotenv's existing parser (already a transitive Laravel dependency, no new vendor), read the pair of files into arrays, array_diff_key both directions, two-column table, explicit non-zero exit on drift and on unreadable/unparseable input, opt-in --values behind the same masking posture the package now has. Zero external dependency, correct base_path() resolution, test coverage we control.The reimplementation case is now marginally weaker on feature parity (we'd rebuild --values, path, multi-file config) and still stronger on total cost of ownership, because the one differentiator the package had — sane masked defaults — is a 20-line decision in our own code.
Compatibility. Blocking, and unchanged in substance. Verify explicitly on the target matrix before any commitment; treat Laravel 7 / PHP 7.4 as the stated support envelope, not a floor to negotiate. No --ignore-platform-reqs in a shared codebase.
Sequencing.
path explicitly to base_path(), run against a deliberately awkward .env pair (quoted values, comments, interpolations, a missing key in each direction, a value-only drift) and record masking, exit codes, and behavior when a configured file is absent.env:diff-equivalent command plus fixture tests over the edge cases; masked by default with an explicit --values opt-in matching the package's posture.--values in a job with externally published logs..env.example is the canonical key list.Maintenance. Unchanged and decisive: upstream is effectively unmaintained and now sits a platform generation behind. Any Laravel or PHP upgrade converts the dependency into our maintenance. The 2.0.0 config schema is a second artifact we would inherit and must migrate on the next breaking release, if one comes. The reimplementation remains the lower-maintenance option.
Support. No upstream support channel, no release cadence, no security advisory process. 2.0.0 arrived five years after 1.0 with no commitment to a 3.x; it improves the tool but does not improve the support posture. Any bug is ours immediately.
Scaling. Negligible, as before. O(number of keys), sub-second, developer-local or per-CI-job. The only scaling dimension is organizational: adopting the tool spreads the drift-detection convention across repos and should be paired with the documented canonical-key-list policy.
Failure modes.
path default — the concrete new risk in 2.0.0: path => app_path() can point at a directory with no env files, yielding an empty comparison and a green build. Mitigate by pinning path to base_path() at integration time and by requiring a non-zero exit when a configured file is absent.--values — masking is now correct by default, so the residual risk is a human toggling it on in a pipeline that publishes logs. Prohibit it in CI by policy and review..env.example; maintain an ignore-list of key prefixes.additional_files → files already broke once; a committed config is a small ongoing liability.require-dev, but it blocks local setup for the whole team at once.Ramp-up. Near zero for developers — one command, self-describing output, masked by default, slightly more setup than 1.x because of the published config. Cost still concentrates in the owner decision: the compatibility spike, the fork-versus-reimplement call, and the CI blocking policy. Budget two to three engineer-days end to end for the recommended reimplementation path — unchanged, and the delta versus forking is now smaller in features but larger in risk, since forking 2.0.0 means porting a Laravel 7-era parser to the current framework before you can use any of it.
How can I help you explore Laravel packages today?