SDP Clouds
← All posts
CI/CD·4 min read

Feature Flags: Deploy on Friday Without the Drama

Trunk-based development with flags instead of release branches — how we ship code continuously but expose features only when we're ready.


The Friday deploy freeze exists because of a specific fear: code reaches production that you can't take back quickly enough. Feature flags attack the fear directly — the code ships continuously, but exposure becomes a separate, instant, reversible decision.

We moved to flags-plus-trunk-based two years ago. It's not free — flags are a liability that accrues interest — but the first time I killed a bad launch by flipping a config value instead of coordinating a rollback across four services, I stopped missing release branches.

The core split: deployment ≠ release

  • Deployment — getting code into production. Do it constantly; it should be routine and boring.
  • Release — letting users experience the code. Gate this behind flags.

Once those are separate, Friday deploys lose their sting. The code for the new checkout flow is live; the flag is off; nobody sees it. Monday morning, you flip it for internal users, then 5%, then everyone — or you flip it back with one edit.

Types of flags (and their lifespans)

Treating every toggle as the same thing is how flag systems rot. I use four buckets with very different expectations:

  1. Release flags — wrap a new feature mid-development. Days to weeks of life. Deleted the moment rollout finishes.
  2. Ops flags — kill switches for risky behavior ("disable third-party recommendations"). Long-lived by design, tested occasionally so they don't bit-rot.
  3. Experiment flags — A/B splits, tied to an analytics decision. Live until the experiment concludes.
  4. Permission flags — entitlements, plan gating, internal access. Effectively permanent config.

The graveyard flag problem — toggles nobody remembers — is 100% a release-flag problem. Give every release flag an owner and an expiry date in your tracker the day it's created:

FLAG: new-checkout-flow
Owner: Priya
Created: 2026-09-23
Remove by: 2026-10-15

When the ticket closes, the flag deletion is part of "done." Stale flags double your test matrix — every combination of on/off you've never cleaned up is a path that breaks in production.

Where the flag lives

For a web app, evaluate flags client-side or server-side; what matters is consistency. A minimal server-side check looks like:

const showNewCheckout = await flags.isEnabled("new-checkout-flow", {
  userId: user.id,
  default: false,
});

if (showNewCheckout) {
  return <NewCheckout />;
}
return <OldCheckout />;

Defaults matter enormously. Default off for release flags so a flag-service outage fails toward the safe, known behavior — not toward shipping half-built features. (Defaults for kill-switch ops flags are the opposite: fail toward on, so a flag-system outage doesn't accidentally disable a core feature.)

Percentage rollouts by user ID — stable across sessions, no flicker — beat random-per-request splits for UX. Random-per-request is how you get users seeing two different checkout pages in one session and filing very confused tickets.

What flags bought us practically

Canary by exposure, not by deploy. Code is everywhere; traffic to the new path moves 1% → 10% → 50% while you watch dashboards. Rollback is a flag change, not a pipeline run — thirty seconds instead of the eight minutes a full redeploy takes, with none of the "did every service roll back in sync?" anxiety.

Trunk-based without fear. Because incomplete work is dark, main stays shippable. The integration conflicts that plagued our release-branch world mostly evaporated — everyone merges small, continuously.

Product experiments without redeploys. Marketing wants to test three headline variants? Flags, not three branches and three deploys.

The costs nobody mentions up front

  • Your test matrix explodes. Mitigation: test flag states that matter (off, on, partial), not every permutation.
  • Code review gets harder — if (flag) branches hide the shape of the system. Keep flag blocks shallow and delete them fast.
  • Flag debt is real debt. We hold a monthly "flag cleanup" hour; it's on the calendar like any other maintenance.
  • You need discipline about environments: staging should sometimes run flags forced-on so the dark code actually gets exercised before users meet it.

The habit that makes it stick

Release flags are temporary scaffolding, and everyone has to believe that. In practice: create flag → ticket with owner and remove-by date → flag off in prod, on in staging for QA → gradual rollout → flag deleted, code simplified to one path. The last step — deleting the flag and collapsing the branches — is the one teams skip, and it's the one that keeps the system honest.

Ship all you want on Friday. Just make sure releasing on Friday is still a decision, not an accident.

#feature-flags#ci-cd#continuous-delivery#devops