novaway/feature-flag-bundle
Novaway FeatureFlagBundle is a Symfony bundle for defining and toggling feature flags via configuration, annotations, and Twig. It supports strategy-based activation, gradual rollouts, and safer releases without redeploying.
First, the honest framing: novaway/feature-flag-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no Blade directive, and no config/feature-flags.php. If you're in a pure Laravel app, you cannot composer require it and use it day one — you'd be pulling in Symfony DI/Twig contracts for a feature that a 40-line config class could cover. Treat this assessment as "what it does, and how I'd use it" if you're in a Symfony codebase (or a hybrid app where Symfony components are already present).
For a Symfony developer, getting started is genuinely minimal:
composer require novaway/feature-flag-bundle
With Symfony Flex the bundle registers itself; otherwise add it to config/bundles.php. Then create config/packages/novaway_feature_flag.yaml:
novaway_feature_flag:
features:
new_checkout:
enabled: false
description: "Rewrite of the checkout flow"
beta_dashboard:
enabled: true
Where to look first: run bin/console debug:config novaway_feature_flag to confirm your flags parsed, and bin/console debug:container | grep -i feature to find the manager service ID and its exact class/namespace (these have shifted across versions — verify before copy-pasting imports). The bundle's README is short and is effectively the whole API surface.
First use case: gate a single new route or template block behind new_checkout, flip it to true in the dev environment config only, and confirm prod stays dark. That's the entire value proposition: a boolean map in YAML plus a manager service to read it.
Inject the manager, don't scatter getParameter() calls. The manager is the seam you want in tests and the seam you'd swap if you outgrow the bundle:
use Novaway\Bundle\FeatureFlagBundle\Manager\FeatureManager;
final class CheckoutController
{
public function __construct(private FeatureManager $features) {}
public function __invoke(): Response
{
if ($this->features->isEnabled('new_checkout')) {
return $this->newFlow();
}
return $this->legacyFlow();
}
}
Twig for view-level toggles. The bundle ships a Twig extension, so templates stay readable:
{% if feature_flag('beta_dashboard') %}
{% include 'dashboard/beta.html.twig' %}
{% else %}
{% include 'dashboard/legacy.html.twig' %}
{% endif %}
Environment-scoped rollout is the idiomatic pattern. Because config is static YAML, the cheapest "canary" is per-environment overrides:
# config/packages/prod/novaway_feature_flag.yaml
novaway_feature_flag:
features:
new_checkout: { enabled: true }
Combine with Symfony's when@env blocks if you prefer a single file. This is coarse (all-or-nothing per environment) but it's honest and auditable in git — a real advantage over DB-backed flag systems for small teams.
Wrap flags in a domain-named accessor. Rather than sprinkling string literals, define constants or a thin AppFeatures class that delegates to the manager. This gives you IDE navigation and a single place to grep when a flag dies. It also gives you an obvious boundary to replace the package later.
Kill-switch usage. Keep a small set of flags mapped to operational toggles (e.g. disable_third_party_sync) and let ops flip them via config + deploy. Do not use this bundle for anything that needs to change without a deploy — see Gotchas.
Testing. Since the manager is a service, bind a stub in the test container with the flag forced on/off, and write both branches of every gated path. In Symfony, a FeatureManager double with a hardcoded map is usually faster and clearer than loading YAML fixtures.
Laravel integration, if you insist. Don't install the bundle. Port the idea: a FeatureFlags class bound as a singleton, reading config/feature-flags.php, plus a Blade directive @feature('new_checkout'). You'll get the same behavior in ~30 lines with zero cross-framework dependency. The bundle's only real asset is its naming and config shape, which you can copy without the dependency.
prod, bin/console cache:clear (or deploy) is mandatory. A "flag didn't change" bug is almost always stale cache, not the bundle.debug:container and debug:config rather than trusting a blog post. This is a small package with a short README; the container is the source of truth.feature_flag() will throw an unknown-function error. Register the bundle correctly before blaming the template.description includes a ticket ID and target removal date) and audit the YAML monthly. The bundle won't do this for you.How can I help you explore Laravel packages today?