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

Features Bundle Laravel Package

yannickl88/features-bundle

Symfony bundle for adding feature flags to your application. Define features, enable or disable them per environment, and gate code paths with simple checks for safer rollouts, gradual releases, and controlled deployments.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require yannickl88/features-bundle

Symfony Flex usually registers the bundle automatically. If not, add it to config/bundles.php:

return [
    // ...
    Yannickl88\FeaturesBundle\FeaturesBundle::class => ['all' => true],
];

Where to look first (read the vendor source — the README is short and there are no dependents to learn from):

  1. vendor/yannickl88/features-bundle/README.md — usage examples and the public API.
  2. vendor/yannickl88/features-bundle/DependencyInjection/Configuration.php — the exact config tree keys and defaults. This is the authoritative answer to "what can I configure".
  3. vendor/yannickl88/features-bundle/Resources/config/services.* — service ids and autowiring aliases. Confirm the id before injecting.
  4. Twig/ and EventListener/ (if present) — how tags are exposed to templates and whether a request listener resolves tags automatically.

Minimal configuration

# config/packages/features.yaml
features:
    tags:
        new_checkout: true
        beta_dashboard: false

First use case — gate a new code path behind a tag:

final class CheckoutController
{
    public function __construct(private FeatureTagManager $features) {}
    // or inject the service id you found via `bin/console debug:container features`

    public function checkout(): Response
    {
        if ($this->features->isEnabled('new_checkout')) {
            return $this->newFlow();
        }

        return $this->legacyFlow();
    }
}

Verify the wiring immediately with bin/console debug:container --show-private | grep -i feature and bin/console debug:config features.

Implementation Patterns

1. Centralize access behind your own interface. The bundle is small (19 stars, 0 dependents), so don't let its service leak through your codebase:

interface FeatureFlags
{
    public function isEnabled(string $tag): bool;
}

final class BundleFeatureFlags implements FeatureFlags
{
    public function __construct(private FeatureTagManager $manager) {}

    public function isEnabled(string $tag): bool
    {
        return $this->manager->isEnabled($tag);
    }
}

Alias the interface in services.yaml. If you later swap to a different flag library, only one class changes.

2. Config-per-environment is the sweet spot. Put defaults in features.yaml and override per environment or per deployment:

# config/packages/features.yaml
features:
    tags:
        new_checkout: false

# config/packages/prod/features.yaml
features:
    tags:
        new_checkout: '%env(bool:FEATURE_NEW_CHECKOUT)%'

This gives ops a runtime switch without a redeploy of code — just an env var change and container rebuild/cache clear.

3. Template branching. If the bundle ships a Twig extension, use {% if feature('new_checkout') %} around the new markup and keep the legacy branch. If no extension is registered, expose a Twig global from your own service instead of calling the manager from templates.

4. Controller attributes / annotations. If the bundle supports tagging actions (check the README), prefer that for coarse endpoint gating and fall back to explicit isEnabled() checks for finer logic. Don't mix both styles for the same feature.

5. Progressive rollout workflow. Treat tags as short-lived:

  • Add tag default false.
  • Merge the gated code.
  • Enable in staging, then a canary/prod cohort.
  • Remove the tag and the dead branch on a scheduled cleanup ticket.

Keep a config/features.yaml-adjacent inventory (or a simple markdown table) listing tag name, owner, and removal date. Feature tags rot fast; with a low-traffic package nobody will enforce cleanup for you.

6. Testing.

  • In config/packages/test/features.yaml, set every tag explicitly so tests are deterministic — never rely on production defaults.
  • For kernel/browser tests, flip tags via config overrides in the test case rather than mutating runtime state if possible.
  • Write one test per branch (enabled and disabled) for each gated code path. The whole value of the package is conditional behavior, so both paths need coverage.

7. Integration tips.

  • If tags are DB-backed (check Entity//Doctrine/ in the bundle), run migrations as part of deploy and warm any cache the bundle provides.
  • In long-running workers (Messenger consumers), resolve tags per message rather than caching a boolean in the constructor, or you'll pin the flag for the process lifetime.
  • Use %env()% indirection so you can toggle without touching code, but remember container values are compiled.

Gotchas and Tips

Strict config tree. Symfony's Configuration rejects unknown keys with a InvalidConfigurationException at cache:clear. If a key seems ignored, run bin/console debug:config features to see the normalized values and compare against DependencyInjection/Configuration.php — don't guess from the README.

Autowiring may not be configured. Small bundles sometimes register services without a class-name alias. If FeatureTagManager can't be autowired, bind it explicitly:

services:
    Yannickl88\FeaturesBundle\FeatureTagManager:
        alias: 'features' # confirm the real id via debug:container

Config is compiled — changes need a cache clear. Toggling a tag defined in YAML does nothing until the container is rebuilt (bin/console cache:clear). This is the most common "the flag didn't work" report. If you need runtime toggles without a rebuild, drive the value through an env var (still requires rebuild for compiled params) or use whatever runtime/storage backend the bundle offers.

Environment precedence bites. Files under config/packages/prod/ override the base file; an env-var fallback written as %env(bool:FEATURE_X)% will fail hard if the variable is unset. Provide a default: %env(default:false:bool:FEATURE_X)%.

Templates. If the Twig extension isn't auto-registered on your Symfony version, register it manually and confirm with bin/console debug:twig | grep -i feature. A missing function throws at render time, often only on the page you gated — check both branches in a smoke test.

Version/Symfony compatibility. Last release was June 2024; confirm the constraint in composer.json supports your Symfony major (6.x/7.x). With only 19 stars and zero dependents there is no community pressure to keep up with new Symfony releases — treat upgrades as your responsibility and pin the version in composer.json.

Debugging checklist.

  1. bin/console debug:container <service> — is it registered and public/aliasable?
  2. bin/console debug:config features — did my key get merged as expected?
  3. bin/console cache:clear — did the compiled config actually change?
  4. Log the resolved boolean at the decision point once, then remove the log.

Extension points. Decorate the bundle's tag manager (or wrap it in your FeatureFlags adapter) to add cross-cutting behavior: audit logging of flag evaluations, per-user/cohort resolution, or a fallback to a remote flag provider. Prefer decoration over forking; the package is small enough that a fork is tempting, but a decorator survives vendor updates and keeps you on the MIT-licensed upstream.

Risk note. Zero dependents and a low star count mean no external battle-testing. Read the tag-resolution code before relying on it for anything security-sensitive, avoid using feature tags as an authorization mechanism (a flag is not a permission check), and keep the gated code paths independently tested so a broken flag evaluation fails safe rather than opening a path you didn't intend.

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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
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
cgv/localized-resource
noith/settings