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

Feature Flag Bundle Laravel Package

em411/feature-flag-bundle

A Symfony bundle for feature flags: enable or disable code paths based on context-aware callables. This is a Symfony 4.4 / PHP 7.4 backport of ajgarlag/feature-flag-bundle, keeping the same API and namespace for a drop-in upgrade from the upstream package.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package is intentionally narrow: one service (FeatureCheckerInterface), one provider contract (ProviderInterface), a chain aggregator, an in-memory/service-backed provider, a Twig bridge, and a profiler collector. That is the correct shape for a feature-flag primitive in a Symfony 4.4 application. It follows ordinary bundle conventions (auto-configuring tagged services, a compiler pass in FeatureFlagPass, XML service wiring), so it slots into existing DI patterns rather than introducing a parallel configuration model. The provider abstraction is the strongest design decision: it lets teams front the same feature_is_enabled() API with a simple callable today and later swap in Unleash/GitLab or a Doctrine-backed assignment table without touching call sites. This is the difference between a two-week demo and something an engineering org can standardize on.

Integration feasibility. Low friction. composer require em411/feature-flag-bundle, register Ajgarlag\FeatureFlagBundle\FeatureFlagBundle::class in config/bundles.php (Flex handles this), and start declaring features via @AsFeature annotations or the ajgarlag.feature_flag.feature service tag. The Twig functions and profiler collector are available automatically if Twig and WebProfiler are present. There are no database migrations, no queue workers, no outbound network calls unless a provider adds them. The realistic integration cost is measured in hours, not sprints.

Technical risk. This is the highest-signal section and should gate adoption. Specific concerns:

  • EOL stack by design. The package targets Symfony 4.4 and PHP 7.4, both of which are out of upstream security support. The maintainer acknowledges this in 1.0.4: they explicitly set config.audit.block-insecure: false in CI because Composer's security audit blocks the pinned Symfony/Twig versions. That is a defensible posture for a backport, but it means the consumer's Composer install will also surface audit failures unless they override the same setting. Enterprises with hardened CI pipelines need a documented policy for this.
  • Single-maintainer fork, 0 stars, 0 dependents. The upstream ajgarlag/feature-flag-bundle carries the credibility; this fork carries the compatibility burden. There is no evidence of external adoption, which means no community bug-catching and no de facto SLO. The changelog is professional (semver discipline, explicit upstream attribution, clear scoping to CI vs. runtime), which is reassuring but does not substitute for a support commitment.
  • Namespace/package-name mismatch. The Composer package is em411/feature-flag-bundle but the PHP namespace is Ajgarlag\FeatureFlagBundle\. This is deliberate for drop-in compatibility, but it creates real confusion during code review, SBOM generation, and incident response ("which package owns this class?").
  • Doctrine annotation dependency. @AsFeature requires the doctrine/annotations runtime. Doctrine deprecated annotations in favor of native attributes, which this fork cannot use on PHP 7.4. Teams must keep doctrine/annotations alive and accept that the annotation syntax will be a code-review and onboarding friction point for engineers who have moved to PHP 8 attributes.
  • Routing integration is absent. Route condition expressions cannot evaluate services in Symfony 4.4, so feature gating of routes must be done in controllers or a kernel.controller listener. This is documented but easy to miss and pushes gating logic into controllers, which is where it is hardest to audit. Any team with a large routing surface should plan a listener abstraction before rollout.
  • Quantitative signal is weak. A score near 14/100 is a reasonable reflection of EOL targets and zero adoption. TPMs should not let a working demo override this; treat the package as a viable tactical choice and not a strategic one.

Key questions for the maintainer / sponsor team.

  1. What is the stated support horizon for this backport? Is there a plan to keep shipping patches through your Symfony 4.4 → 5.4 → 6.x upgrade window, and what is the trigger to archive the fork?
  2. Will security fixes for doctrine/annotations, symfony/framework-bundle, or twig/twig be backported here, or is the expectation strictly "your app pins the same versions"? The 1.0.4 audit note suggests the latter, but it should be explicit.
  3. Is there agreement to upstream any 4.4-specific fixes back to ajgarlag/feature-flag-bundle, or is the fork permanently divergent?
  4. What is the internal owner of the dependency — a platform team, an architecture group, or individual product teams — and what is the deprecation date in your roadmap?
  5. If we standardize on FeatureCheckerInterface today, will that API exist unchanged in the upstream when we upgrade? (The README says yes; get it in writing in an ADR.)

Integration Approach

Stack fit. This package exists to serve exactly one integration scenario: an application that is on Symfony 4.4 and PHP 7.4 and cannot move to Symfony 6+/PHP 8.1+ within the current planning horizon. If the application can reach PHP 8.1 and Symfony 6.x, the correct decision is to use upstream ajgarlag/feature-flag-bundle and skip this package entirely. The README states this plainly and TPMs should treat that statement as the primary acceptance criterion. Do not adopt this package as a hedge against an eventual upgrade — adopt it as an explicit, time-boxed bridge.

Migration path. Two directions matter:

  • From upstream, in: replace ajgarlag/feature-flag-bundle with em411/feature-flag-bundle in composer.json. Because the namespace is preserved, no use statements change and no bundle class registration changes. The only code change is #[AsFeature] attributes becoming @AsFeature Doctrine annotations — mechanical, but a real diff that must be reviewed. Treat this as a one-way, in-place substitution and do not run both packages side by side.
  • To upstream, out: the reverse substitution is the intended endgame. The @AsFeature annotation becomes a #[AsFeature] attribute, and route-level gating can move from controllers/listeners into Symfony's native route condition expressions. The FeatureCheckerInterface callsites should be identical. Budget this as part of the eventual Symfony 5.4/6.x upgrade, not as a separate workstream.

Compatibility. Confirmed supported combinations: PHP 7.4, PHP 8.0; Symfony 4.4; Twig 2.x (Twig is require-dev only and therefore optional at runtime, which is correct for a bundle with a Twig bridge). Not supported / untested: PHP 8.1+ (the changelog explicitly capped twig/twig to keep the CI matrix green on PHP 7.4 and 8.0, so do not assume 8.1 compatibility), Symfony 5+, PHP 8 attributes. If your application runs PHP 8.0 you are technically within scope but at the edge of the maintainer's CI matrix — flag this in your risk register.

Sequencing. A pragmatic rollout:

  1. Spike (1 week). Install in a branch, declare two trivial features (@AsFeature on an invokable class, and one on a method of an existing service), and exercise feature_is_enabled() from a controller, a service, and a Twig template. Confirm the profiler panel resolves feature values per request.
  2. Decision gate. Confirm with engineering leadership that the application is genuinely stuck on Symfony 4.4/PHP 7.4 and that the audit-override policy (config.audit.block-insecure: false) is acceptable to the security team. If either answer is no, stop here.
  3. Provider strategy (1 week). Decide between: (a) in-memory/annotation-only features for pure code-path toggles; (b) a custom ProviderInterface implementation backed by a table or config store; or (c) a third-party flag service via a provider (the README shows an Unleash/GitLab pattern that is copy-pasteable). For most teams, (b) is the right target because it gives product/ops a way to flip flags without a deploy.
  4. Route gating pattern (1 week). Because route condition expressions do not work on 4.4, standardize on either a kernel.controller listener or a small base-controller check. Write this once, in a shared trait or listener, so it does not get copy-pasted across the codebase.
  5. Rollout. Introduce flags behind a naming convention (e.g., prefix by domain) and require every flag to have an owner and a removal date. The bundle does not enforce this; your process must.
  6. Exit plan. Add the eventual Symfony upgrade → upstream package swap to the technical roadmap with a target date, and include it in the ADR.

Operational Impact

Maintenance. The package is small and the changelog shows disciplined semver. 1.0.5 was a CI-only release; 1.0.4 was a CI/security-audit fix; 1.0.1–1.0.3 were packaging and Twig-2 compatibility fixes. There is no evidence of runtime churn, which is the right signal for a low-level primitive. The real maintenance burden is not in the package — it is in the EOL stack underneath it. Symfony 4.4, PHP 7.4, and Doctrine annotations are all receiving at most security-only fixes (and Symfony 4.4 is past its free support window), which means the application's patching burden dominates this bundle's. TPMs should not credit the package for the health of its dependencies.

Support. Community-only via GitHub issues. No commercial backing, no published SLA, no external adopters to fall back on. Two consequences: (1) internal code ownership matters more than usual — assign a named engineer to be the internal point of contact; (2) for anything touching a revenue path, fork-and-patch internally rather than waiting on upstream. The package is small enough that this is realistic, but it should be an explicit decision, not a hope.

Scaling. Two axes:

  • Feature count. ChainProvider iterates providers in priority order and stops at the first hit. Complexity is O(number of providers) per lookup, not O(number of features), so annotation- and tag-declared features scale linearly in class metadata and constant in lookup cost. Thousands of features are fine; tens of thousands would suggest an architectural review of why so many flags exist, not a change to this bundle.
  • Request throughput. The package does not cache. Each isEnabled() call invokes a provider callable, and any network- or database-backed provider will hit its backend on every call unless the provider itself caches. For Unleash/GitLab providers, the README's PSR-16 cache wiring is mandatory, not optional; budget a cache pool and a TTL for it. For a Doctrine-backed provider, expect a query per lookup unless you memoize per request — easy to add, easy to forget.

Failure modes to plan for.

  • Provider exceptions. A network-backed provider that throws will propagate through FeatureChecker and can take down a request. Wrap external providers with try/catch that fails off for risky new features and on for kill-switch-style flags, and document the choice per flag.
  • Callable type drift. Features return "a value" matched against an expected one, not strictly booleans. A refactor that changes an expected value silently disables a feature. Enforce a convention (boolean-only for production flags, string/int only for variants) and cover it in tests.
  • Stale flags. Nothing in the bundle removes dead flags. Without an ownership registry and a lint rule ("this annotation is older than 90 days"), flags accumulate into permanent, undocumented configuration and erode the value of the whole system.
  • Profiler leakage. The web profiler panel exposes feature names and resolved values. In shared pre-production environments, ensure the profiler is off or scoped appropriately — feature names often leak roadmap information.
  • Audit-policy surprise. A team that adds this package without the config.audit.block-insecure: false override will see their pipeline go red on next Composer update. Document the override alongside the install instructions in your internal runbook, not in the README.

Ramp-up. Low for engineers: the public API is three concepts (FeatureCheckerInterface, ProviderInterface, @AsFeature), and the README is complete enough to onboard a mid-level engineer in an afternoon. The friction points are Doctrine annotation syntax for engineers who have already moved to PHP 8 attributes, and the conceptual "feature vs. configuration vs. experiment" distinction, which the bundle does not help with — that is a product-policy problem, not a software problem. Budget a short internal doc and a naming convention before rollout; it will save more time than any tooling decision.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect