gracious/feature-flag-bundle
A Symfony bundle for managing feature flags in your application. Enable or disable features at runtime, roll out changes gradually, and control access without redeploying code. Integrates cleanly with existing Symfony services and configuration.
This is a Symfony bundle, not a Laravel package. If you are working in a Laravel codebase, stop here — there is no service provider, Blade directive, or config/feature-flags.php integration. The release targets Symfony 7/8 on PHP 8.3+.
Minimal setup:
composer require gracious/feature-flag-bundle). Symfony Flex should register the bundle automatically; otherwise add it to config/bundles.php.config/packages/gracious_feature_flag.yaml, and declare your first flag with an enabled boolean and an optional description.First use case: gate a new checkout or dashboard flow behind a flag, check it in a controller/service for enforcement, and check it in Twig to hide the navigation entry. Look first at the bundle README's config reference, the manager service ID/alias, the Twig function signature, and the #[RequireFeature] attribute class. Confirm exact config keys and service IDs against the release — the notes describe capabilities, not the precise API surface.
Config-driven flags as the source of truth. Define flags centrally with descriptions. Treat descriptions as living documentation for non-developers and keep flag names stable and descriptive (checkout.new_flow, not flag1). Wrap flag names in a small constants class or enum so typos fail at one place rather than scattered string lookups.
Named managers for separate flag groups. Use the default manager for application-wide flags, and named managers for isolated domains (e.g. checkout, admin, experiments). Named managers prevent one team's flag namespace from colliding with another's. Since multiple manager services exist, alias the one you want explicitly in services.yaml or use #[Autowire] — autowiring by type alone will be ambiguous.
PHP service checks as guard clauses. Inject the manager and return early when a flag is off. Keep the check close to the entry point (controller, message handler, command) rather than buried deep in domain logic, so the disabled path is easy to reason about and remove later.
Twig for presentation, PHP for enforcement. Use the Twig check to hide links, menu items, and partials. Never rely on it for access control — always repeat the check server-side.
Route defaults for coarse gating. Attach the flag to a route default so the bundle can enforce access at routing time. This is a good fit for whole pages or admin sections where you want a consistent response when the flag is off, without repeating checks in every action.
#[RequireFeature] for controllers. Prefer the attribute over manual if blocks in controllers. It is declarative, visible in the class signature, and keeps the disabled behavior consistent. Decide deliberately whether a disabled feature should return 404 (hide existence) or 403 (acknowledge but deny), and make that consistent across the app.
Runtime overrides for tests and local debugging. Use per-process enable/disable/reset in test setUp() and always reset in tearDown(). This is the cleanest way to test both branches of a flag without touching config. It is also useful for local CLI experiments.
REST endpoint for observability. The read-only endpoint is for dashboards and ops checks, not for toggling. Keep the kill switch off by default in production, and if you enable it, put it behind your firewall/access_control and restrict it to internal networks.
@feature, no facade, and no config/feature-flags.php. Use Symfony config, DI, Twig, and attributes.bin/console cache:clear (or a deploy that rebuilds the container). Flags compiled from env vars will not update without a rebuild; prefer explicit config values for anything you expect to flip.#[RequireFeature] apply, confirm which response wins. A disabled feature behind authentication can produce confusing redirects instead of the 404/403 you intended.tearDown(), and test both enabled and disabled states for critical paths. A flag that is only tested in the "on" state is a latent production incident.How can I help you explore Laravel packages today?