A FIELD GUIDE TO LARAVEL · VOLUME II · THE ECOSYSTEM
ENTRY II.27 · FEATURE FLAGS
Pennant
Laravel\Pennant · Package · first recorded February 2023, with Laravel 10
Feature flags without a vendor. A flag is a rule in code, resolved once for a given user or team and remembered in your own database, so a rollout or an experiment is stable for the person seeing it.
Specification
Kind Package, in-process. No server, no dashboard
Interface The Feature facade; a closure in a provider or a class in app/Features
Storage database driver by default, a features table; array driver for tests
Installed by composer require laravel/pennant · vendor:publish · migrate
Voice pennant:feature · pennant:purge
Scales by One row per feature per scope; Feature::load eager-loads a collection
Range Cloud: yes. A table in the attached database
Forge, VPS: yes, the same
Vapor: yes. Nothing resident is needed
Neighbours
Gates and policies decide whether a user may do something; Pennant decides which version of the app they see, and remembers the answer. Config and env values change per deploy, not per person. A hosted flag service adds a dashboard outside the code; Pennant keeps the rule in code and the state in your database.
Field marks
PENNANT_STORE=database
config/pennant.php
app/Features/
@feature('...') in Blade
Fig. II.27.1 · One flag, resolved once, exploded
Parts
1 Definition. Feature::define('new-api', fn (User $user) => ...) in a service provider, or a class made by pennant:feature.
2 Scope. What the flag is checked for. The authenticated user unless Feature::for() says otherwise.
3 Resolver, run once per scope. A rule, a Lottery, or a rich value such as a colour for an experiment.
4 Store. The result is written to the features table, so the answer is stable for that scope from then on.
5 Request cache. Checked once, held in memory for the rest of the request, so a loop does not re-query.
6 Check site. Feature::active, @feature in Blade, or EnsureFeaturesAreActive on a route.
Hidden joinery
A null scope resolves to false unless the definition accepts null (nullable or untyped), so an anonymous visitor never trips a flag. A rich value is stored per scope, so a variant stays with its customer across visits. pennant:purge --except-registered clears what is no longer defined. Feature::discover() registers app/Features.
Joinery checked on 2026-10-08 against the Pennant docs and the source of its purge command and decorator.
TITLE
Pennant · one flag, resolved once
SOURCES
laravel.com/docs/13.x/pennant · github.com/laravel/pennant/blob/1.x/config/pennant.php · github.com/laravel/pennant/blob/1.x/database/migrations/2022_11_01_000001_create_features_table.php · github.com/laravel/pennant/blob/1.x/src/Drivers/Decorator.php · github.com/laravel/pennant/blob/1.x/src/Commands/PurgeCommand.php · packagist.org/packages/laravel/pennant · packagist.org/packages/laravel/framework · laravel.com/cloud/docs/resources/databases/laravel-mysql · docs.vapor.build/projects/environments · laravel.com/blog/from-zero-to-product-a-guide-for-building-a-saas-with-laravel · laravel.com/docs/13.x/authorization · laravel.com/docs/13.x/configuration · Monitor 576
STATUS
Verified · 2026-10-08
SHEET
II.27 of 48
REV
A · 2026-10-08
DRAWN
BS
FINISH
All faces, including the back