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 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 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 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.
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.
A short timeout on the lookup. A slow flag read shouldn't slow every page. Use the last known value if you have one.
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.
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.)
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:
Pick a quiet hour. Tell your friendly accounts if they'll notice.
Flip it off. Time how long the change takes to reach users (cache lifetimes count).
Load the page as a normal user. Check the old path works and nothing half-new is left, like a button that leads nowhere.
Flip it back on and check again.
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.
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 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 and ask other makers for honest feedback while the flag is still yours to flip.