amjadiqbal/laravel-pennant-dashboard
A Laravel dashboard for Pennant feature flags. Inspect flags, view activation states and scopes, and manage toggles from one clean UI, making it easier to audit and control features without digging through code or config.
Architecture fit. This is a Laravel-native add-on layered on top of Laravel Pennant, not a standalone feature-flag service. Based on the release notes it contributes three things to the host application: a schema (its own tables, shipped as publishable migrations), a decision path (Rollout::decide()), and an admin surface (the dashboard). That shape fits well with teams already committed to Pennant and the Laravel HTTP/Blade/artisan lifecycle — no new runtime, no sidecar, no external control plane. The explicit run_migrations config switch is a sound acknowledgement that not every shop lets application packages own schema changes.
Integration feasibility. Install is conventional: composer, publish config and migrations, php artisan migrate. Two paths exist and both are supported — package-managed migrations (run_migrations true) and operator-managed (false, requiring vendor:publish --tag=pennant-dashboard-migrations before migrating). The 1.0.2 release is a fix, not a redesign, so nothing in it invalidates a standard integration. The notable integration constraint is that from 1.0.1 onward the package is stateful: it now has a schema whose presence must be verified per environment, not just a config file.
Technical risk. The 1.0.0→1.0.1 gap was a genuine production-severity defect, and it is worth naming precisely: Rollout::decide() silently fell back to the application default, meaning rollouts stopped being evaluated rather than erroring. A feature-flag system that fails open to default is a correctness risk in both directions — features that should be on go dark, and features that should be off can be enabled depending on which default was chosen. Dashboard writes 500'ing compounded it into an operational outage. 1.0.2 does not remove the fallback; it makes it observable (banner, artisan about pending state, one warning per process, dashboard write refusal with an explanatory message) while preserving 1.0.0 read semantics until the migration runs. That is the right mitigation and it converts a silent failure into a loud, greppable one — but it means the residual risk is now "a warning nobody reads," which is a process risk rather than a package risk.
Key questions. Unknowns I would clear before shipping this to a shared environment: (1) Is decide() cached, and if not, does it hit the database on the flag-evaluation hot path? (2) During a rolling deploy with mixed code versions, do pre- and post-1.0.2 instances agree on flag state once the migration lands? (3) Does the "one warning per process" fire once per PHP-FPM worker, i.e. could it be noisy under php-fpm but absent under Octane, or vice versa? (4) What authorization model protects the dashboard — who may flip a rollout in production, and is there an audit trail? (5) Is the 1.0.0-compatibility read path a temporary shim scheduled for removal in a minor/major, and what is that timeline? (6) Migration reversibility and table-name collision risk in multi-tenant or shared-database setups. (7) Supported Laravel/PHP version matrix, and the Pennant version constraint.
Stack fit. Clean. Laravel + Pennant shops adopt this without changing their evaluation call sites — the package wraps the decision path rather than replacing it, and the dashboard is a standard Blade/admin surface. Teams without Pennant should not adopt this as a first flag system; it presupposes Pennant's model.
Migration path. For anyone on 1.0.0, this is a two-step upgrade with a mandatory database change: bump the dependency, run php artisan migrate, and — only if run_migrations is false — re-publish the migrations first. The re-publish step is the sharp edge, since it is manual and easy to skip on a fresh environment where no prior published migration exists. Recommend pinning the publish-tag command into the deploy runbook rather than relying on memory.
Compatibility and sequencing. 1.0.2's backward-compatible read path is the important sequencing property: because Rollout::decide() continues to read the 1.0.0 tables identically until migration, code can be deployed before the schema change without behavioral drift. That permits the safer order — deploy code, verify, then migrate, then verify the banner clears — rather than the riskier migrate-then-deploy. Gate the deploy on php artisan about no longer reporting the migration as pending, and add that check to CI or a pre-deploy script so it is not a human eyeball.
Verification. Stage the upgrade against a non-production flag set, exercise a dashboard write (which should succeed post-migration and refuse with the banner message pre-migration), and confirm rollout evaluation in a lower environment matches 1.0.0 output for a known flag before touching production.
Maintenance. New ongoing obligation: migration lifecycle management for a package-owned schema, including the vendor:publish step for shops that opted out of run_migrations. Expect at least one future release to revise these tables, and plan for the same publish/migrate sequence each time.
Support. The failure signature changes from "flags quietly stopped working, nobody noticed for weeks" to "banner in the dashboard, pending line in artisan about, a log warning." That is a large supportability improvement, but only if on-call and support staff are taught what the banner means. Add one line to the runbook: banner visible ⇒ migrations pending ⇒ rollouts are evaluating against 1.0.0 semantics.
Scaling. The open question is decide() cost. If evaluations are uncached and the dashboard's tables are consulted per call, this becomes a database amplifier on traffic-heavy paths; if the package caches or defers to Pennant's own store on the read path, it is negligible. Verify before wide rollout. Dashboard traffic itself is low-volume and not a scaling concern.
Failure modes to instrument. (a) Migration drift between environments — the dominant risk, now detectable. (b) Rollout evaluation silently reverting to defaults if the compat path is ever removed or a future upgrade reintroduces fallback. (c) Dashboard write refusal misunderstood as a permissions bug rather than a migration notice. (d) Log-warning suppression under process managers that drop stderr or dedupe aggressively.
Ramp-up. Low for Laravel developers — one config file, one migration, one dashboard. Moderate for the operations side: the team must internalize that flag state now depends on schema state, and that "defaults" is a failure signal, not a configured value. Budget a short enablement session and a runbook entry; the 1.0.2 diagnostics make that session substantially easier than it would have been on 1.0.1.
How can I help you explore Laravel packages today?