# How to Ship Risky Changes Behind Feature Flags
> How to ship risky changes behind feature flags: deploy dark, roll out in stages, keep a kill switch, fail safe, and delete flags fast.

```yaml
url: "https://makerhunt.io/studio/how-to-ship-risky-changes-behind-feature-flags"
markdown: "https://makerhunt.io/studio/how-to-ship-risky-changes-behind-feature-flags.md"
title: How to Ship Risky Changes Behind Feature Flags
type: studio_article
category: Growth
tags: "feature flags, feature toggles, kill switch, gradual rollout, release management, continuous delivery, deployment, developer tools, makers, Growth"
featured_image: "https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx9anx88yZJsOEgxD9oIMj2XZ36Y1qQH7rah5db"
published_at: "2026-10-19T09:53:13.940Z"
updated_at: "2026-10-19T09:53:14.515Z"
```

## About this studio article

How to ship risky changes behind feature flags: deploy dark, roll out in stages, keep a kill switch, fail safe, and delete flags fast. Plus a 7-day plan.

## Article

To ship a risky change behind a feature flag, you deploy the new code switched off, turn it on for yourself, then for a handful of accounts, then for a growing share of users, and if anything looks wrong you flip it off in seconds without a redeploy. The flag is just an if statement that reads its on or off value from somewhere outside your code. For a solo maker, a flags table in your own database is usually enough to start. The real work is the habits around it: name every flag, decide what happens when the lookup fails, practise the off switch once, and delete the flag when the rollout is done.

Below: flag jobs, when to skip one, setups and tools, naming, safe defaults, rollout, the kill-switch drill, cleanup, an example and a 7-day plan.

## What is a feature flag, in plain words?

A feature flag is a conditional that your code checks while it's running, with its value stored somewhere you can change without shipping new code. In its smallest form:

`if (flags.isOn("enable-new-search", account)) { return newSearch(query) } else { return oldSearch(query) }`

Both paths are in production. The flag decides which one runs, for whom, right now.

Normally deploy and release happen together: the code goes out and every user gets it. With a flag, the deploy changes nothing for anyone, and the release becomes a separate decision you make slowly, one group at a time. Pete Hodgson's article [Feature Toggles (aka Feature Flags) on Martin Fowler's site](https://martinfowler.com/articles/feature-toggles.html) describes this as the principle of "separating [feature] release from [code] deployment," and it's still the best plain explanation of the patterns.

Why bother when you're one person? Because the scary part is the moment a change meets real data at 11pm and your only undo is a revert and redeploy. A flag turns that undo into one click.

## What jobs can a feature flag do?

Hodgson sorts flags into four categories. Same machinery, very different lifespans, so know which one you're creating.

- **Release toggle.** Hides unfinished work so you can deploy it switched off. Short-lived: Hodgson says these generally shouldn't stick around much longer than a week or two.
- **Kill switch (ops toggle).** Turns off something that might break or overload your system: a new import job, a heavy report, a third-party integration. Some stay on purpose; Unleash's [feature flag best practices docs](https://docs.getunleash.io/topics/feature-flags/feature-flag-best-practices) list kill switches as a valid exception to "keep flags short-lived."
- **Permission toggle.** Shows a feature to specific accounts only: you, a test workspace, the customer who asked for it. Useful for a while, risky if it quietly becomes your whole access-control model.
- **Experiment toggle.** Splits users into groups to compare two versions.

Experiments are their own discipline with their own maths, and low traffic limits them a lot. This guide to [running A/B tests when your product has low traffic](https://makerhunt.io/studio/how-to-run-ab-tests-when-your-product-has-low-traffic) covers that. This post is about the first three: getting a risky change out safely.

## When should you not use a feature flag?

Every flag adds a second code path you have to keep working, and Hodgson is clear that teams should keep the number of flags low. Skip the flag when:

- **The change is tiny.** A copy fix, a colour, a typo. Just ship it.
- **A revert handles it fine.** If undoing is a two-minute redeploy and nothing is lost meanwhile, a flag is ceremony.
- **It's configuration.** Credentials, API base URLs and ports belong in config. Unleash draws the line well: if you need to restart your app to turn it on, that's configuration, not a feature flag.
- **It can't be toggled.** Most schema changes are one-way; you can't flag your way out of a dropped column. Use expand and contract: add the new column, write to both, move reads over (that step can be flagged), then remove the old one once nothing uses it.

Rule of thumb: flag it when the change touches a busy path, depends on data you can't reproduce locally, or would be painful to undo under pressure.

## What's the simplest feature flag setup that works?

You don't need a vendor to start. Hodgson covers options from hardcoded values up to distributed config stores; for a small product, four are realistic:

Setup

How you flip it

Per-account or percentage?

The catch

Environment variable

Change it, then restart or redeploy

No

Not instant, so a poor kill switch

Config file in your repo

Edit, commit, deploy

Only if you write the logic

Still needs a deploy

Flags table in your database

Update a row (SQL or a tiny admin page)

Yes, with a few lines of code

You build history and the admin screen

Feature flag tool

Click in a dashboard

Yes, built in

Another dependency, account and SDK

My default for a one-person product is the database table. It flips instantly, can target accounts, and lives next to data you already back up:

`create table feature_flags (name text primary key, enabled boolean not null default false, account_ids text[], rollout_percent int not null default 0, owner text, remove_by date, note text);`

Keep the check in one small function so every part of the app asks the same question the same way. Unleash's docs suggest evaluating a flag once per request and passing the result down instead of re-checking it all over the code. Move to a tool when you want percentage rollouts without writing them, a change history, or a second person flipping flags without touching the database.

## Which feature flag tool should a small team pick?

Five names come up most for feature flags for small teams. I checked each pricing page on Oct 19 2026; free tiers change, so treat this as a dated snapshot.

Tool

Open source?

Free option (as of Oct 19 2026, per their pricing page)

Worth a look if...

Unleash

Yes

Unleash Open Source: free, self-hosted, no seat limit; 1 project, 2 environments, 5,000 flags per instance

You'll run one more service and want flags only

GrowthBook

Yes

Cloud Starter free for up to three users with unlimited flags and experiments; free self-hosted with unlimited users

You expect to add experiments later

Flagsmith

Yes

Free plan: up to 50,000 requests per month, 1 team member, unlimited flags

You want a simple hosted dashboard for one

PostHog feature flags

Has an MIT-licensed open source product

1M feature flag requests per month, resets monthly

You already use PostHog for analytics

LaunchDarkly

No

Free Developer plan: unlimited flags and seats, 5 service connections, 1K client-side monthly active users

You want the established commercial option

The tool matters less than the habits below. Pick one whose free limits fit your traffic and whose SDK covers your stack, then stop shopping.

## How should you name and track each flag?

Name flags as a verb plus the area, so the name says what "on" means: `enable-new-search`, `use-v2-csv-import`, `kill-pdf-export`. Not `new-stuff` or `test2`. Unleash's docs add a good rule: never reuse an old flag name, or a stale check somewhere could switch an old feature back on.

Hodgson suggests a human-readable description per flag, and notes some teams also store a creation date, an owner and an expiry date. I keep a flag card for each one, as table columns or a note in the repo:

Field

What to write

Example

Name

Verb plus area; what "on" means

enable-new-search

Type

Release, kill switch, permission or experiment

Release (kill switch during rollout)

Owner

Who flips it and removes it

Me

Default

Value used if the flag can't be read

Off (old search)

Who sees it

Current audience

My test account plus 6 named accounts

Created

Date added

2026-10-20

Remove by

Date the flag and old code path go

2026-11-10

"Remove by" is the field people skip, and the one that matters most later.

## What should happen if your flags can't be read?

The database hiccups or the flag tool times out. Your code still has to pick a path, so decide which one in advance.

The Unleash docs say your application shouldn't depend on the flag system being available, and should keep running on cached values or defaults if it fails. Hodgson's convention makes defaults easy: "off" means existing behaviour and "on" means new behaviour. Then "off" is almost always the safe fallback, because the old path is the one you know works.

1. **A default in code for every flag.** Your helper takes a fallback value and returns it when the lookup throws or times out, instead of crashing the request.
2. **A short timeout on the lookup.** A slow flag read shouldn't slow every page. Use the last known value if you have one.
3. **A conscious choice for kill switches.** The safe default might be "running as normal" or "off," depending on which failure you'd rather live with for an hour. Put it on the flag card.

Check your tool's behaviour too. PostHog's pricing page, for example, says that on the free plan, past the free limit, flags return a default quota limited response. That's when your fallback matters.

![image](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx9BxEezZflsGM90RmwkXzHlFeUv1NEhSTKbaC4)

## Should you check flags on the server or in the browser?

On the server, whenever you can. If the browser gets the whole flag configuration, anyone with dev tools can read unreleased feature names, who gets them, and sometimes the feature code itself. The Unleash docs recommend evaluating flags server-side so user data and configuration don't reach the client, and sending the browser only the result.

- **Hide the endpoint, not just the button.** Hodgson points out that a UI-only toggle can leave unreleased functionality reachable at a URL nobody has found yet. If the feature has an API route, check the flag there too.
- **Keep server keys on the server.** Unleash lists exposed API keys among the risks of client-side evaluation.

## How do you roll out a risky change step by step?

A gradual rollout widens the audience in stages; at each stage, watch your error logs and support inbox before moving on. (For a simple way to keep an eye on things while you're away from the desk, see this guide on [monitoring a side project during your day job](https://sidehunt.io/studio/how-to-monitor-a-side-project-during-your-day-job).)

Step

Who sees it

How long (rule of thumb)

Check before moving on

1

Just you, in production

A day of normal use

Use it for real, on real data, both on and off

2

Internal and test accounts

1-2 days

New entries in your error logs; odd behaviour on large or messy accounts

3

5-10 friendly accounts you've asked first

2-4 days

Support inbox and replies; error logs; whether they used it

4

A percentage of everyone (say 10%, then 50%)

1-3 days per step

Error logs, support inbox, anything slow or heavy on your server

5

100%

Keep the flag about a week

Same checks; then schedule cleanup

Those durations are rough rules of thumb. With low traffic, stay at each step until enough people have actually used the feature.

Make percentage splits sticky: hash the account ID with the flag name and compare it to the percentage, so an account doesn't flicker between old and new. Hodgson describes the same idea using a modulo of the user ID.

Step 3 means people you message one to one and ask first. A favour, not a program.

## How do you run a kill-switch drill?

A kill switch you've never flipped is a guess. Flip it once in production, on purpose, before you need it:

1. Pick a quiet hour. Tell your friendly accounts if they'll notice.
2. Flip it off. Time how long the change takes to reach users (cache lifetimes count).
3. Load the page as a normal user. Check the old path works and nothing half-new is left, like a button that leads nowhere.
4. Flip it back on and check again.
5. Write down exactly how you did it, somewhere tired, stressed you will find it.

Could you flip it from your phone at dinner? If not, a tiny admin page only you can reach is worth the hour.

![image](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx90kn0VUuUMGtDgAeTkzHpnxayuvbZw12cLKE8)

## How do you clean up flag debt?

Hodgson treats flags as inventory with a carrying cost. He describes teams that add a removal task the moment a release toggle is created, put expiry dates on flags, or add "time bombs" that fail a test when a flag outlives its date. A small routine that works:

- **Every flag gets a "remove by" date at creation.** Two to three weeks after the planned 100% day is a fair rule of thumb for release toggles.
- **A weekly 10-minute flag review.** List flags by "remove by." Anything at 100% for a week goes this week. Anything past its date gets a new date and a reason, or goes.
- **Delete the code path, not just the flag.** Removing the row but keeping both branches leaves dead code nobody dares touch. Delete the old branch, the check and its tests, then the flag. Unleash's docs say the same: once a rollout is complete, remove the flag and the old code paths it replaced.
- **Cap the count.** Hodgson mentions teams that limit live flags, so adding one means removing one. For a solo maker, five is plenty.

Retiring a whole feature is a different job; this piece on [removing a feature nobody uses without upsetting the few who do](https://praneetbrar.hashnode.dev/remove-the-feature-nobody-uses-here-s-how-i-do-it-without-upsetting-the-few-who-do) covers it.

## A made-up example: Spinecrate swaps its search behind a flag

Everything in this example is made up: the product, the maker and every number. Spinecrate is a catalogue and pre-order tool for independent bookshops. Its maker, Ines, wanted to replace slow catalogue search (a plain database query) with a proper search index. Shops search all day, so a bad release would show in minutes.

She added `enable-new-catalogue-search` to her flags table: default off, owner Ines, remove-by three weeks out. Both search paths shipped in one deploy.

Day

Audience

What happened

Decision

1

Ines's test shop

Faster results; one sorting bug fixed

Move on

3

Two test shops with large catalogues

Nothing new in error logs

Move on

4

6 friendly shops she asked first

One shop: ISBN searches with dashes returned nothing

Off for that shop, fixed, back on next day

7

10% of shops (sticky by shop ID)

Quiet inbox, no new errors

Move on

9

50% of shops

Search index ran out of memory on a busy afternoon

Flipped off in under a minute; resized the index

11-13

50% again, then 100% on day 13

Quiet

Kept the flag a week

20

Everyone

No issues since day 13

Deleted old search code, the check and the flag row

Day 9 is the whole argument. In this made-up story, without the flag that memory problem would have meant a revert and redeploy mid-afternoon. With it, half the shops had slow search for a few minutes while Ines fixed the real problem calmly.

## What traps make feature flags dangerous?

- **Flags that never die.** Release toggles at 100% for months, both branches still in the code.
- **Nested flags.** A check inside a check hides which combination a user is on. Combine them or finish one first.
- **Testing only the "on" path.** Hodgson advises testing the configuration you expect to go live and the fallback with new flags off. The off path is your safety net.
- **Secrets and rules in client code.** Server keys or the full rule set shipped to every browser.
- **Flags as a permissions system forever.** Fine for a rollout. When flags decide who can do what long term, move that into real roles and settings.
- **Flipping flags with no record.** If you can't answer "what changed at 3pm?", you'll lose an evening to it. Keep changed_at and changed_by columns, or use a tool with history.

## A 7-day plan to start using feature flags

Day

Task

Time

1

Create the flags table (or pick a tool); write one helper with a fallback value and a short timeout

60-90 min

2

Put your riskiest pending change behind a flag, default off; fill in its flag card

60 min

3

Check flags on the server only; test the "off" path; confirm nothing unreleased reaches the browser

45 min

4

Deploy with the flag off; turn it on for yourself and use it on real data

30 min, then normal use

5

Run the kill-switch drill and write down the steps

20 min

6

Switch it on for 5-10 friendly accounts who said yes; watch error logs and support inbox

30 min

7

Book a weekly 10-minute flag review; confirm the "remove by" date

10 min

Those times are rough rules of thumb, not a benchmark. Percentage steps come after, at the rollout table's pace.

## FAQ

### Do I need a feature flag tool to start?

No. A flags table and one helper function covers most solo makers for a long time. Switch when you want built-in percentage rollouts or change history.

### Do feature flags slow my app down?

Only if you check them carelessly, like one database query per flag per page. Read flags once per request or cache them for a few seconds. Unleash, for example, says its backend SDKs fetch the configuration once, cache it in memory and evaluate flags locally.

### Are feature flags the same as feature branches?

No, and they work well together. As the Unleash docs put it, branches isolate work before it's ready for production, while flags control who sees it after it's deployed.

## Ship it dark, then turn it on slowly

Feature flags turn a risky change from one scary moment into a few small, reversible steps. That's how to use feature flags well: put your next risky change behind one, practise the off switch once, and delete the flag when you're done. Before a new feature goes to 100%, it also helps to have fresh eyes on it: list your product on [MakerHunt](https://makerhunt.io/) and ask other makers for honest feedback while the flag is still yours to flip.

## Links

- Article (HTML): https://makerhunt.io/studio/how-to-ship-risky-changes-behind-feature-flags
- AI-friendly Markdown: https://makerhunt.io/studio/how-to-ship-risky-changes-behind-feature-flags.md

## Explore

- [Studio index](https://makerhunt.io/studio)
- [Blog](https://makerhunt.io/blog)
- [Browse projects](https://makerhunt.io/projects)

_HTML version: https://makerhunt.io/studio/how-to-ship-risky-changes-behind-feature-flags_