Ship Features Fast with Feature Flags

18 Mar, 2021

Hey folks - I'm the creator of Taskord and I often deploy more than 100 times a day. The only way that's safe is with disciplined feature flagging.

This is a short, practical playbook: how to plan, develop behind a flag, test in prod with staff, gradually roll out, observe errors and metrics, then graduate to GA and remove the flag.

TL;DR

  • Ship behind flags by default; keep new code dark until ready.
  • Rollout path: dev → staff → beta/cohorts → 100% → remove flag.
  • Add a fast kill switch and alerting on key metrics/errors.
  • Prefer server‑controlled flags (remote config), not build‑time envs.
  • Clean up flags promptly to avoid dead code and confusion.

Planning

Suppose we're building a new Explore page at /explore. Start with the smallest viable plan:

  • Define the UI and acceptance criteria.
  • Check component availability and API gaps.
  • Note data migrations and rollout constraints.
  • Identify blockers and a rollback plan.

Don't overthink - iterate and refine as you go.

First Pull/Merge Request

Open with the smallest PR: a page that renders “Hello, World!” behind a flag at /explore. Get CI signal and unblock parallel work.

Push to GitHub/GitLab and open your first PR.

merge-request

Tip: Even solo, PRs create a review point and CI history.

Development

Build the feature behind the flag. If backend APIs aren't ready, mock responses and share the contract to keep moving. Clear, early agreements reduce rework.

Testing

Let CI pass, then merge to main and deploy to staging. Use a short checklist (happy paths, feature off/on, permissions, performance) before promoting to production.

ci-pipeline

What is a Feature Flag?

Feature flags (toggles) gate behavior at runtime based on rules. They allow progressive delivery, instant rollback, A/B tests, and targeted access without redeploying.

Minimal examples

// Client (React)
function ExploreGate({ enabled }: { enabled: boolean }) {
  return enabled ? <ExplorePage /> : <NotFound />;
}

// Server (Node/Hono/Express)
app.get('/explore', (c) => {
  const enabled = flags.isEnabled('explore_page', c.var.userId);
  if (!enabled) return c.text('Not Found', 404);
  return renderExplore(c);
});

Flags should be remotely configurable, auditable, and queryable by user/cohort.

Staff Release

After staging looks good, deploy to production with the flag default OFF. Announce internally, e.g. in Slack:

We've shipped the Explore page to production. Staff can enable it via Staff Ship. Feedback welcome!

This enables safe testing in production without exposing the feature to users.

staff-badge

What is Staff Ship?

Staff Ship is a flag limited to admins/mods. Often it adds an admin/perf bar above navigation (like GitHub) with quick toggles and links.

On Taskord, admins can toggle Staff Ship using the backtick key or the pb shortcut.

Taskord's admin bar looks like this:

staff-bar

GitHub's staff bar (GH Enterprise) looks like this:

github-staff-bar

Beta Release

Once internal testing is solid, roll out gradually. Common strategies:

  • Percentage rollout (10% → 25% → 50% → 100%)
  • Cohorts (users with a “beta” flag, staff friends, allowlist)
  • Geography, platform, account age, or plan‑based rules

Always use stable bucketing so a user stays in the same variant.

beta-badge

Feedback

Collect feedback quickly (in‑app prompts, a Slack channel, short forms). Triage and iterate before widening exposure.

Automatic Error Tracking

Use tools like Sentry for real‑time errors and dashboards for key metrics. Wire alerts to a kill switch so you can disable the flag within seconds.

sentry-errors

General Availability

When confident in stability:

  • Flip to 100%, remove the flag and dead code.
  • Keep monitoring errors and performance for a few days post‑launch.

ga-badge

Guardrails & Pitfalls

  • Always have a kill switch; practice using it.
  • Ship small, reversible PRs; avoid long‑lived branches.
  • Don't gate database migrations solely by flags - use phased, backward‑compatible migrations.
  • Document flag ownership and an expiry date to ensure cleanup.

Quick Checklist

  • Built behind a remotely controlled flag (default OFF)
  • Server‑side gated and client‑side guarded
  • Observability added (events, metrics, error tags, alerts)
  • Staff test in production complete
  • Beta cohort rollout plan with stable bucketing
  • Kill switch verified; rollback plan documented
  • GA criteria defined; cleanup task created

Happy Shipping!