How to implement feature flags in PHP monoliths to release features gradually
By Kevin Marcondes · Published on · Updated on · 7 min read
Want to know how to add feature flags to a PHP monolith so you can release features gradually? You can lower deploy risk, control who sees new behavior, and turn changes off with one click. Below is a minimum viable architecture, rollout strategies, and a practical step-by-step plan to get started safely in PHP monolithic apps.
When your deploys block the business
Large deploys in monoliths create risk windows. Teams hold back changes until they can “ship a big batch,” and when something breaks, rollbacks are painful. The result is fear of releasing and long feedback cycles.
Feature flags reduce that pressure. You ship code to production but control exposure. Turn a feature on for 1% of users, measure impact, and expand. If something goes wrong, turn it off. If the root cause is process-related, a process deployment audit can help stabilize the flow.
What feature flags are in PHP and why they help
Feature flags (or feature toggles) are runtime switches in code. Instead of deciding at deploy time, you decide at runtime whether a block executes. This enables low-risk testing in production.
In PHP monoliths they work well because the decision point is centralized, the codebase is single, and control can be applied by user, segment, percentage, or environment. Here are the most common types and when to use them:
| Toggle type | Typical use | Risk | Expected duration |
|---|---|---|---|
| Release | Roll out features step by step | Low if there is a fallback | Short |
| Ops / Kill switch | Quickly turn off unstable parts | Very low | Long |
| Permission | Enable by profile, customer, or plan | Medium | Long |
| Experiment | Test A/B variants | Medium (requires metrics) | Short |
Minimum viable architecture in the monolith
You need three pieces: flags storage, a lightweight PHP SDK, and a simple panel to change values. In a monolith, a database table is enough to start.
Flag reads must be fast. Use local in-memory cache (APCu) with a short TTL. For near-instant changes, invalidate by version: when a flag is edited, increment a version key and workers reload on next access.
Decision logic should happen near the request edge: middleware, controller, or domain service. For long-lived workers (queues), reload flags periodically. In layered architectures, isolate checks inside a FeatureFlag service to keep code clean. If you plan to evolve the design, consider process deployment audit and architectural patterns for legacy PHP.
Gradual rollout in practice: strategies
Strategies vary by risk and audience. Start with internal exposure, then pilot customers, then expand by percentage. Define health metrics before increasing exposure.
Common segmentations include user, tenant, plan, region, and request header. Percentage rollout must be stable per user: use consistent hashing of the identifier to avoid bucket hopping between requests. Keep a kill switch to turn features off quickly.
- Internal first: team and QA access via allowlist
- Canary by tenant or sensitive business segment
- Stable percentage rollout (e.g., 1%, 5%, 25%, 50%, 100%)
- Gate by plan / premium feature
- Emergency kill switch separate from the main flag
Implementation step by step
Start simple and evolve as you learn. A practical path for PHP monoliths:
- Model a feature_flags table with a unique name, state, variant, and scope (global, per-user, per-tenant).
- Implement a FeatureFlag service with APCu cache and version-based reload.
- Use consistent hashing for percentage rollouts by user or tenant.
- Create middleware/helpers for explicit checks. Avoid scattered ifs without a pattern.
- Implement a global kill switch for critical areas (payments, search, email).
- Audit who changed a flag, when, and why.
- Monitor error rates, latency, and conversion per flag.
- Cover behavior with tests. See guidance on testing legacy PHP code for next steps.
- Tie flags to your delivery pipeline: promote flags between environments and gate approvals. Integrate with CI/CD with GitHub Actions for PHP/Laravel.
- Perform periodic cleanup: release flags should have expiration dates.
Best practices and common pitfalls
Name flags by intent, not implementation. For example: checkout_step_two, not newFlow. Assign an owner and a target removal date.
Avoid complex dependencies between flags. If needed, compose decisions in a single place. Watch query performance for large segments by using proper indexes and local caching.
- Document the flag contract (audience, risk, fallback).
- Do not expose sensitive flags to the front end without protection.
- Synchronize queue workers to reload flags periodically.
- Keep fallbacks safe and idempotent.
- Remove dead code as soon as a flag has served its purpose.
Observability, audit, and rollback
Feature flags only work well with visibility. Build dashboards per flag showing error rate, response time, and business metrics. Set alerts for thresholds before advancing a rollout.
Have a rollback plan: disable the flag, revert configuration, and notify the team. If recurring failures appear around deploys, run a process deployment audit to strengthen the flow.
- Events for flag state changes
- Logs correlating flagId with requests
- Clear SLOs and thresholds to advance percentages
- Rollback checklist per system area
Want to apply quickly and safely?
If you need to reduce deploy risk in a PHP monolith and build confidence to ship smaller and more frequently, feature flags are a strong first step. Technical guidance helps avoid pitfalls and accelerate value from pilot to production. Ready to discuss your context and the best design to get started?
Frequently asked questions
Where should feature flags be stored in a PHP monolith?
Start with a database table that models flags with a unique name, state, variant and scope, and add indexes by name and scope for queries. Combine that storage with a local in-memory cache such as APCu and a short TTL, and use a version key to invalidate quickly when flags change. Avoid storing flags only in .env because that prevents dynamic runtime changes.
How do you implement a stable percentage rollout?
Use consistent hashing of a stable identifier (user or tenant) to map to a 0–100 range, then compare the resulting value to the flag's percentage. This keeps each user or tenant in the same bucket across requests and prevents bucket hopping. Implement the hashing and comparison inside the FeatureFlag service so rollouts are consistent across the monolith.
Do feature flags impact performance?
When implemented correctly the impact is minimal: read flags from storage once, cache them locally with a short TTL (APCu), and invalidate by version when edited. Run the decision logic early in the request (middleware or controller) and measure latency per flag. For long-lived workers, reload flags periodically and monitor error rates to confirm actual cost.
When should a feature flag be removed?
Assign release flags an expiration date and plan to remove them after the feature reaches 100% exposure and behavior is stable; then delete the conditional code and the flag. Permission flags and emergency kill switches may be long-lived but still require periodic review and documentation. Audit who changed a flag and why before removing related code to avoid regressions.
How do you integrate flags into CI/CD?
Treat flag changes as versioned configuration promoted through staged environments. Use approvals to advance percentages, automated checks, and block rollouts when metrics or tests fail. Tie flags to the delivery pipeline so promotions follow the same process as code, and include checks that prevent advancing a rollout if error or business metrics fall below defined thresholds.
Implement feature flags in your PHP monolith safely
Assess your situation, design a minimal architecture, and run a pilot with gradual rollout and a kill switch. Schedule a no‑obligation conversation.
Talk about feature flagsSee also
- How to recover corrupted statistics and indexes in PostgreSQL that caused slow queries
- REST API latency spikes from N+1 queries and PHP GC — how to diagnose and fix
- Postgres 'Too many connections' at billing peak — practical diagnosis and remediation guide
- Reduce costs migrating Magento to OpenMage with a rollback plan and acceptance tests