Skip to content
AI Product Engineer Wiki

    ↑ ↓ to move between results, Enter to open, Esc to close

    Menu

    Feature flags and staged rollouts for AI

    Ship AI features gradually with feature flags, covering staged exposure, kill switches, flagging prompts and models, and rollback plans that work in minutes.

    Level: Advanced 9 min read Updated
    On this page

    Why this matters

    However good your eval set is, production will show you inputs it didn’t contain: new phrasings, unexpected languages, documents in odd formats, usage patterns nobody predicted. AI features meet more of these surprises than most, because their inputs are open-ended and their outputs vary.

    A staged rollout limits the damage while you learn. Instead of shipping to everyone at once, you expose the feature to a growing share of users, watch quality, behavior and cost at each step, and keep a switch that turns it off in seconds. Feature flags are the mechanism. This article covers how to flag AI features, plan a rollout with exit criteria, design kill switches and fallbacks, and implement it in code.

    Flags for AI features

    A conventional flag answers one question: is the feature on for this user? AI features benefit from flagging three things separately:

    • The feature itself: on or off, for whom. Your kill switch lives here.
    • The prompt version: so you can roll a new prompt out gradually, and back, without a deploy.
    • The model and its settings: so a model upgrade or effort change goes through the same staged process as any other change.

    Treating prompts and models as flagged configuration, not hard-coded constants, is what makes AI features safe to change. A prompt edit is a behavior change for every user; it deserves the same rollout discipline as code. And because flags live outside the deployment, a bad change can be reverted in seconds instead of waiting for a build.

    A staged rollout plan

    Write the plan before the first user sees the feature. Each stage has an audience, a minimum duration and exit criteria that must hold before you move on:

    A staged rollout. Move to the next stage only when the exit criteria hold; roll back at any stage.
    Show text descriptionHide text description

    A sequence of 5 steps.

    1. Internal : The team and friendly users; catch obvious problems
    2. 1% : First real traffic; watch errors, refusals and cost
    3. 10% : Enough volume to read behavior metrics
    4. 50% : Compare against the control group as an A/B test
    5. Everyone : Keep monitoring; keep the kill switch

    Example exit criteria for each stage:

    Signal Exit criterion Where it comes from
    Errors API error rate below 1% Traces
    Unfinished answers Truncation and refusal rates no higher than in evals Traces
    Quality Sampled outputs pass the eval rubric at the launch threshold Human or judge grading of a sample
    Behavior Acceptance rate at or above the prototype’s Analytics events
    Cost Cost per success within the estimate Usage data in traces
    Complaints No increase in support tickets about the feature Support tags

    Spend at least long enough at each stage to see normal weekly patterns in your traffic, and don’t skip stages because things look fine on day one. Problems with rare inputs only show up with enough volume.

    Kill switches and fallbacks

    A kill switch turns the AI feature off for everyone, immediately, without a deploy. It should be:

    • Fast: a flag change takes effect in seconds or minutes.
    • Obvious: everyone on call knows where it is and is allowed to use it.
    • Tested: flip it in staging, and once in production during a quiet period, before you need it.

    What users see when the feature is off matters as much. Plan a fallback for each AI feature:

    • Hide the entry point, so nothing looks broken.
    • Fall back to the non-AI path: the manual form, the saved replies, the plain search. This is where the non-AI baseline pays off again.
    • Fall back to the previous prompt or model if the problem is a recent change rather than the feature itself.

    Flags in code

    Here’s a small, self-contained implementation of the ideas above. Users are assigned to a bucket from 0 to 99 by hashing their ID together with the flag name, so each user always gets the same answer, on every server, without storing anything. One flag controls the feature, its prompt version and its model.

    lib/ai-flags.ts
    import { createHash } from 'node:crypto';
    
    /** One flag controls the feature, the prompt version and the model, so each can change without a deploy. */
    interface AiFeatureFlag {
      /** Kill switch: false turns the feature off for everyone. */
      enabled: boolean;
      /** Share of users who get the feature, from 0 to 100. */
      rolloutPercent: number;
      promptVersion: string;
      model: string;
      /** Always included, regardless of the percentage: the team and early testers. */
      allowlist: string[];
    }
    
    // In production, load these from your flag service or config store so they change without deploying.
    const flags: Record<string, AiFeatureFlag> = {
      'reply-drafts': {
        enabled: true,
        rolloutPercent: 10,
        promptVersion: 'reply-v4',
        model: 'claude-opus-5-5',
        allowlist: ['user-internal-1'],
      },
    };
    
    /** Stable bucket from 0 to 99: the same user and flag always land in the same bucket. */
    export function bucket(userId: string, flagKey: string): number {
      return createHash('sha256').update(`${flagKey}:${userId}`).digest().readUInt32BE(0) % 100;
    }
    
    export type Decision =
      | { on: true; promptVersion: string; model: string }
      | { on: false; reason: 'unknown-flag' | 'disabled' | 'not-in-rollout' };
    
    export function decide(flagKey: string, userId: string): Decision {
      const flag = flags[flagKey];
      if (!flag) return { on: false, reason: 'unknown-flag' };
      if (!flag.enabled) return { on: false, reason: 'disabled' };
      const included = flag.allowlist.includes(userId) || bucket(userId, flagKey) < flag.rolloutPercent;
      return included ? { on: true, promptVersion: flag.promptVersion, model: flag.model } : { on: false, reason: 'not-in-rollout' };
    }
    
    // Usage: decide once per request, and record the decision with your analytics and traces.
    console.log(decide('reply-drafts', 'user-internal-1'));
    
    // Sanity check: about 10% of simulated users should be included.
    let included = 0;
    for (let i = 0; i < 10_000; i++) if (decide('reply-drafts', `user-${i}`).on) included++;
    console.log(`${(included / 100).toFixed(1)}% of 10,000 simulated users included`);

    Properties worth noticing:

    • Raising the percentage only adds users. A user in bucket 7 is included at 10% and stays included at 50%, so nobody loses the feature mid-rollout.
    • Different flags bucket independently, because the flag name is part of the hash. The same users aren’t always first in line for every experiment.
    • The decision includes the prompt version and model, which you should log with every request. Without that, you can’t tell which configuration produced which outcome.
    • The reason for “off” is explicit, which helps when debugging why a user doesn’t see the feature.

    In a real product, use your existing feature flag service if you have one; most support percentage rollouts and allowlists. The important part is the discipline: prompts and models behind flags, decisions logged, and a tested kill switch.

    Common mistakes

    • Shipping to everyone at once. Rare inputs only appear at volume. Stage the rollout and watch each step.
    • Hard-coding prompts and models. Put them behind flags so changes can roll out, and back, without a deploy.
    • An untested kill switch. Flip it in staging and once in production before you need it.
    • No fallback. When the feature is off, users should see the non-AI path, not a broken screen.
    • Not logging the flag decision. Record the prompt version and model with every request, or you can’t attribute outcomes.

    Try it

    Exercise

    About 40 min

    Write a staged rollout plan with exit criteria

    1. Pick an AI feature and list the stages you’d use, with the audience for each.
    2. Write exit criteria for each stage using the table above as a starting point, with real numbers.
    3. Describe the kill switch: where it is, who can use it, how long it takes, and what users see when it’s off.
    4. Run the flag code above and check that bucketing gives roughly the share of users you expect.
    Show a hint

    Base your exit criteria numbers on what you measured in evals and the prototype. For example, if evals showed 2% truncation, a production truncation rate of 6% is a signal to stop and investigate.

    Show a worked solution

    Example plan for AI-drafted support replies:

    • Stages: support team only (3 days) → 1% of agents (3 days) → 10% (1 week) → 50% as an A/B test (2 weeks) → everyone.
    • Exit criteria: error rate under 1%; truncation under 3% and refusals under 0.5%; 85% of 50 sampled drafts pass the rubric; acceptance at least 40%; cost per accepted draft within 20% of the estimate; no rise in reopened tickets.
    • Kill switch: the reply-drafts.enabled flag in the flag dashboard; any on-call engineer or support lead may flip it; takes effect within a minute; agents see the old saved-replies panel.

    Check your understanding

    Question 1 of 4: Why put the prompt version and model behind flags, not just the feature?

    Show answer

    B. So prompt and model changes can roll out gradually and be reverted without a deploy

    A prompt or model change alters behavior for every user. It deserves the same staged, reversible process as a code change.

    Question 2 of 4: Why hash the user ID with the flag name to assign buckets?

    Show answer

    B. So each user gets a stable decision everywhere, and different flags bucket users independently

    Stable, independent buckets mean consistent experiences and fair, uncorrelated experiments.

    Question 3 of 4: What should users see when the kill switch turns the AI feature off?

    Show answer

    C. The non-AI path they used before, with the entry point hidden

    Turning the feature off should make things slightly less convenient, not broken.

    Question 4 of 4: Raising rollout from 10% to 50%: what happens to users who already had the feature?

    Show answer

    B. They keep it, because their bucket number is below both thresholds

    Bucket-based percentages only add users as the threshold rises.

    Key takeaways

    • Flag the feature, the prompt version and the model separately, so each change rolls out and back without a deploy.
    • Plan stages with exit criteria based on errors, unfinished answers, quality, behavior, cost and complaints.
    • Keep a fast, tested kill switch and a non-AI fallback.
    • Assign users with stable hashing, and log every flag decision with the prompt version and model.

    Progress is saved in this browser and lights up the roadmap.