Scenario Recipes

Real-world scenarios showing how Feature Flags for Craft CMS solves problems you already have.

1. Safe-Launch a Home­page Redesign #

You’re rebuild­ing a client’s home­page. The new home­page changes things up con­sid­er­ably: new hero sec­tion, restruc­tured CTAs, and dif­fer­ent con­tent blocks. The client wants to see it on the live site before you flip the switch to roll it out.

You need a way to show on the new home­page in pro­duc­tion with­out any­one see­ing it except you and the client. Once they approve, you want to make it live with a sin­gle tog­gle. No addi­tion­al deployment.

The Flag

Prop­er­tyVal­ue
Namehomepage-redesign
Typerelease
Enabledtrue
Roll­out %0
Rulesuser = 42 (your client’s user ID)

Walk­through

1. Cre­ate the flag in the con­trol pan­el at Fea­ture Flags > New. Set the name to homepage-redesign, type to Release, and tog­gle it on. Add a tar­get­ing rule: type user, val­ue 42 (your client’s Craft user ID).

2. Wrap the home­page tem­plate with a flag check:

{# templates/_pages/homepage.twig #}

{% if craft.featureFlags.isEnabled('homepage-redesign') %}
    {# New redesigned homepage #}
    {% include '_partials/hero-v2' %}
    {% include '_partials/features-grid' %}
    {% include '_partials/testimonials-carousel' %}
{% else %}
    {# Current homepage - untouched #}
    {% include '_partials/hero' %}
    {% include '_partials/features-list' %}
    {% include '_partials/testimonials' %}
{% endif %}

3. Send the client a link. When they log in to Craft and vis­it the home­page, they see the new design. Every­one else sees the exist­ing page. No one panics.

4. Client approves. Remove the user rule in the con­trol pan­el so the flag has no tar­get­ing rules. With no rules and the flag enabled, it returns true for every­one — the redesign is live.

5. Slow roll­out. Maybe you decide that you’d like to roll out the home­page redesign to only 25% of vis­i­tors just in case there’s a bug or unknown per­for­mance issue with the new mega menu and 4K video hero. Add a roll­out per­cent­age to 25 and then mon­i­tor the site.

6. Increase the roll­out. Every­thing is look­ing snap­py and no errors, so you increase the roll­out per­cent­age to 50, then 75, then 100 or empty. 

7. Clean up. Once you’re con­fi­dent, remove the {% if %} wrap­per from the tem­plate, remove the old home­page par­tials, and delete the flag. Deploy the code changes and the home­page roll­out is complete!

Zero down­time, zero risk. The client reviewed the actu­al pro­duc­tion page instead of a stag­ing approx­i­ma­tion, and you went live with a click. If any­thing had gone side­ways you could have tog­gled the flag off in seconds.

2. Grad­u­al­ly Roll Out a New Check­out Flow #

It’s time to change your check­out flow because 68% cart aban­don­ment is becom­ing a prob­lem and your pay­check has been 9 days two months in a row. It’s time to take mat­ters into your own hands to beef up the company’s rev­enue. You know the three-step check­out is the prob­lem. A design­er mocks up a sim­pli­fied sin­gle-page flow, but nobody wants to bet the whole rev­enue stream on a hunch.

You want to show the new check­out to 25% of users, mea­sure the results, and scale up or roll back based on data. Fea­ture Flags’ per­cent­age roll­out buck­et­ing is sta­ble: the same user always lands in the same buck­et for a giv­en flag, so their expe­ri­ence doesn’t flip-flop between visits.

The Flag

Prop­er­tyVal­ue
Namesimplified-checkout
Typeexperiment
Enabledtrue
Roll­out %25
Rules(none)

You don’t need any tar­get­ing rules because the roll­out per­cent­age han­dles assign­ment. Users are buck­et­ed by a con­sis­tent hash of their user ID and the flag han­dle, so the same user always gets the same result for this flag.

Walk­through

1. Cre­ate the flag with name simplified-checkout, type Exper­i­ment, enabled, roll­out per­cent­age 25. No rules.

2. Branch the check­out template:

{# templates/shop/checkout/index.twig #}

{% set useSimplifiedCheckout = craft.featureFlags.isEnabled('simplified-checkout') %}

{% if useSimplifiedCheckout %}
    {% include 'shop/checkout/_single-page' %}
{% else %}
    {% include 'shop/checkout/_multi-step' %}
{% endif %}

3. Pass the cohort to your ana­lyt­ics lay­er so you can mea­sure con­ver­sion by group:

{# In your layout or checkout template #}

<script>
    window.checkoutCohort = '{{ useSimplifiedCheckout ? "single-page" : "multi-step" }}';

    {# Send to your analytics platform #}
    analytics.track('checkout_started', {
        cohort: window.checkoutCohort,
        cartTotal: {{ cart.totalPrice }},
    });
</script>

4. Mon­i­tor results. After a week of data, the sim­pli­fied check­out shows a 12% improve­ment in com­ple­tion rate. Increase the roll­out to 50%, then 75%, then 100%.

5. When you reach 100%, remove the flag check from the tem­plate and delete the old mul­ti-step check­out code and deploy the changes to pro­duc­tion. The roll­out is com­plete; the new flow is permanent.

You test­ed a risky check­out change on real traf­fic with­out an all-or-noth­ing deploy. The 75% on the old flow were com­plete­ly unaf­fect­ed. The deci­sion was backed by data, and you could have rolled back to 0% in sec­onds if the new flow had tanked conversions.

3. Zero-Down­time Main­te­nance Mode #

Your pay­ment gate­way has noti­fied you of sched­uled main­te­nance this Sat­ur­day from 2:00 – 4:00 AM. Check­out will fail dur­ing that win­dow. The rest of the site should stay up.

You could deploy a code change to dis­able check­out, then deploy anoth­er to re-enable it. Or you could set a flag that ops can tog­gle from the con­trol pan­el with­out touch­ing code or wak­ing up a devel­op­er. This is the text­book use case for an ops flag: it con­trols infra­struc­ture behav­ior, not a fea­ture release.

The Flag

Prop­er­tyVal­ue
Namecheckout-maintenance
Typeops
Enabledfalse (flip to true when main­te­nance starts)
Roll­out %0
Rulesenvironment = production

Walk­through

1. Cre­ate the flag with name checkout-maintenance, type Ops, dis­abled. Add an environment rule with val­ue production so it only takes effect on the pro­duc­tion serv­er — your stag­ing and dev envi­ron­ments con­tin­ue work­ing normally.

2. Add a main­te­nance check to your cart and check­out templates:

{# templates/shop/_includes/checkout-guard.twig #}

{% if craft.featureFlags.isEnabled('checkout-maintenance') %}
    <div class="maintenance-banner">
        <p>Our payment system is undergoing scheduled maintenance.
           Checkout will be available again shortly. You can continue
           browsing and your cart will be saved.</p>
    </div>
{% endif %}

3. Gate the check­out button:

{# templates/shop/cart.twig #}

{% if craft.featureFlags.isEnabled('checkout-maintenance') %}
    <button disabled class="btn btn--disabled">
        Checkout Temporarily Unavailable
    </button>
{% else %}
    <a href="{{ url('shop/checkout') }}" class="btn btn--primary">
        Proceed to Checkout
    </a>
{% endif %}

4. Sat­ur­day at 1:55 AM: Your ops team mem­ber opens the con­trol pan­el, nav­i­gates to Fea­ture Flags, and tog­gles checkout-maintenance to enabled. Check­out is dis­abled. No deploy. No SSH. No devel­op­er paged.

5. Sat­ur­day at 3:50 AM: The gate­way con­firms main­te­nance is com­plete. Tog­gle the flag off. Check­out is back. Total devel­op­er involve­ment: zero.

6. Leave the flag in place for the next main­te­nance win­dow. It’s a per­ma­nent oper­a­tional con­trol, not a one-time release tog­gle. The audit log records who tog­gled it and when — use­ful for post-inci­dent reviews.

A 2 AM deploy­ment became a con­trol pan­el tog­gle, and the audit log has a clean record of the whole thing for the next retrospective.


4. Beta-Test a Pre­mi­um Fea­ture #

Audi­ence: Com­merce + prod­uct teams

A new cus­tomer dash­board is in the works for pre­mi­um sub­scribers: order his­to­ry, saved pay­ment meth­ods, loy­al­ty points. Big fea­ture. 2,000 pre­mi­um sub­scribers. And you don’t want all of them hit­ting it on day one.

The plan is to beta-test with a hand-picked group of 20 pow­er users first, gath­er feed­back, fix rough edges, and then open it up to every­one on the pre­mi­um plan. That means lay­ered tar­get­ing — first gate by user group, then lat­er by sub­scrip­tion plan. The permission flag type sig­nals that this flag con­trols access to a capa­bil­i­ty, not a release or an experiment.

The Flag

Prop­er­tyVal­ue
Namecustomer-dashboard
Typepermission
Enabledtrue
Roll­out %0
RulesuserGroup = beta-testers

Lat­er, replace the rule with: subscriptionPlan = premium

Walk­through

1. Cre­ate a beta-testers user group in Craft’s Set­tings > Users > User Groups. Add your 20 select­ed beta users to this group.

2. Cre­ate the flag with name customer-dashboard, type Per­mis­sion, enabled. Add a rule: userGroup = beta-testers.

3. Gate the dash­board nav­i­ga­tion item so only flagged users see it:

{# templates/_layouts/_account-nav.twig #}

<nav class="account-nav">
    <a href="{{ url('account/orders') }}">Orders</a>
    <a href="{{ url('account/addresses') }}">Addresses</a>

    {% if craft.featureFlags.isEnabled('customer-dashboard') %}
        <a href="{{ url('account/dashboard') }}">Dashboard</a>
    {% endif %}
</nav>

4. Option­al: Pro­tect the dash­board route so some­one can­not nav­i­gate direct­ly to it. Your call if this worth­while or not.

5. Col­lect feed­back. Beta testers use the dash­board for two weeks. You fix bugs and pol­ish the UI based on their input.

6. Open it up to all pre­mi­um sub­scribers. In the con­trol pan­el, edit the flag. Remove the userGroup = beta-testers rule and add subscriptionPlan = premium. Every active pre­mi­um sub­scriber now has access. The beta-testers group can be cleaned up later.

7. Even­tu­al­ly, make it gen­er­al­ly avail­able. Remove all rules so the flag is glob­al­ly enabled. Or keep the subscriptionPlan rule if the dash­board should remain a pre­mi­um-only fea­ture per­ma­nent­ly — the permission flag type is designed for exact­ly this kind of long-lived access control.

You beta-test­ed with a small group, iter­at­ed on real feed­back, and expand­ed access grad­u­al­ly with­out rede­ploy­ing. Who can see this” and is the code deployed” were clean­ly sep­a­rat­ed, which meant your dev and release cycles were ful­ly decoupled.