Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Production Dependencies Guard Laravel Package

kalessil/production-dependencies-guard

A Composer plugin that guards production installs by detecting dev-only dependencies and blocking them from leaking into production builds. It adds a safety check so accidental require-dev packages or non-production installs are caught early.

View on GitHub
Deep Wiki
Context7

Getting Started

kalessil/production-dependencies-guard is a Composer plugin, not a Laravel package — it has no service provider, config file, or artisan command. It hooks Composer's install/update/require events and fails (or warns) when a package that looks like a development tool is placed in require instead of require-dev. That is its entire job.

Minimal setup in a Laravel app:

composer require --dev kalessil/production-dependencies-guard

Then, because Composer 2.2+ blocks plugins by default, allow it in composer.json:

"config": {
    "allow-plugins": {
        "kalessil/production-dependencies-guard": true
    }
}

Run composer dump-autoload (or any install/update) and confirm it loads without a "plugin was not loaded" error.

Where to look first:

  1. The repo README for the exact output format and any ignore/exclusion mechanism.
  2. The package's own composer.json — check "type": "composer-plugin", the extra.class entry, and which Composer events it subscribes to. That tells you precisely when it runs.
  3. Your project's composer.json require vs require-dev diff — that is what the guard evaluates.

First use case (the one that matters in Laravel): the usual suspects get composer require'd by mistake — barryvdh/laravel-debugbar, laravel/telescope, laravel/sail, barryvdh/laravel-ide-helper, nunomaduro/collision, larastan/larastan, phpunit/phpunit, pestphp/pest. Run:

composer require barryvdh/laravel-debugbar   # no --dev

and watch it get flagged. The correct form is always:

composer require --dev barryvdh/laravel-debugbar

Implementation Patterns

1. Global install for team-wide enforcement. Install once into your global Composer home so it protects every project without touching each composer.json:

composer global require kalessil/production-dependencies-guard

Note the same allow-plugins rule applies to the global config. This is the lowest-friction rollout for a team, but it is invisible to new hires and CI, so pair it with a per-project declaration.

2. Per-project require-dev + CI gate. Commit it as a dev dependency and let composer install in CI run the plugin. Because the plugin aborts the command on violation, a PR that adds a dev tool to require fails the build before merge:

# .github/workflows/ci.yml
- run: composer install --no-interaction --prefer-dist

This is the pattern with the highest long-term value — the guard becomes a policy check, not a personal habit.

3. Composer script alias. Wrap it so developers can run it deliberately, not only when Composer happens to fire:

"scripts": {
    "guard:deps": [
        "@composer validate --strict",
        "@composer install --dry-run"
    ]
}

Exact event names vary — verify against the plugin's README, since some Composer plugins expose a dedicated command while others only react to install/update events.

4. Remediation workflow when the guard fires. Composer does not move packages between sections for you:

composer remove barryvdh/laravel-debugbar
composer require --dev barryvdh/laravel-debugbar

Do this in one commit so composer.json and composer.lock stay consistent.

5. Layer with complementary Composer tooling. The guard answers "is this in the right section?" It does not answer "is this package actually used?" or "is this version vulnerable?" Pair it with composer-unused, composer-require-checker, and roave/security-advisories. Together they cover section hygiene, dead dependencies, and CVEs.

6. Production deploy scripts. Keep composer install --no-dev --optimize-autoloader in your deploy pipeline. The guard lives in require-dev, so it is intentionally absent in production — it is a development-time policy check, not a runtime protection.

Gotchas and Tips

  • allow-plugins is the number-one blocker. On Composer 2.2+ you will get a plugin-blocked error and assume the package is broken. It isn't. Add the explicit entry or "allow-plugins": true (the latter is discouraged).
  • --no-plugins and --no-scripts silently disable it. Docker builds, some CI images, and deploy scripts often pass these flags. A "passing" build may simply mean the guard never ran.
  • Detection is heuristic, not authoritative. The plugin infers "development package" from package metadata and naming patterns. False positives exist for runtime packages with dev-flavored names, and false negatives exist for genuinely dev-only packages the heuristic doesn't recognize. Treat output as a prompt to review, not an oracle.
  • It cannot judge context. symfony/var-dumper belongs in require-dev in a library but is defensible in require for a Laravel app that calls dd() in production support tooling. The guard doesn't know your intent; you do.
  • Path repositories and private packages are weakly understood. In monorepos using "type": "path", the guard has less metadata to reason about and may miss violations entirely, or flag an internal package oddly. Check the README for an ignore/exclusion list before fighting it.
  • No Laravel surface area. Don't hunt for config/production-dependencies-guard.php or php artisan commands — there are none. If you need configurable rules, you're forking it or writing your own check.
  • Debugging: run the failing command with -vvv to see the plugin's event output inline. Because it runs mid-Composer, the message is interleaved with dependency resolution output and is easy to miss in noisy CI logs.
  • Non-interactive CI: make sure the plugin's failure mode is a non-zero exit code rather than an interactive prompt. Test this once with --no-interaction before trusting it in a pipeline.
  • Maintenance risk is real. 83 stars, zero dependents, single maintainer. Pin an exact version in require-dev, and accept that if it breaks against a future Composer major you may need to drop it. The fallback is ~20 lines of your own: a PHP script that reads composer.json, cross-references packages found only in require-dev of your own history, and exits non-zero. Keep that escape hatch in mind before making it load-bearing in CI.
  • Extension points are essentially Composer's event system only. If you need allowlists per environment, a fork is the realistic path — or wrap the guard in a Composer script that does your custom checks first.
  • Ordering matters. Run the guard after composer validate --strict; a malformed composer.json produces confusing plugin errors that mask the real syntax problem.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin