Merge pull request 'docs(ops): feature flags are not worth a manager here (#318)' (#329) from spike/318-feature-flags into main
Reviewed-on: #329
This commit was merged in pull request #329.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Feature flags: whether this project wants a manager
|
||||
|
||||
Findings for #318. Tool capabilities and pricing were checked on 2026-09-08; everything about this repository was read from the code on 2026-09-09.
|
||||
|
||||
**Status: do not adopt a feature flag manager. Not yet, and possibly not ever at this size.** This is written down so the question does not get reopened from scratch — including the conditions that would change the answer.
|
||||
|
||||
## The short version
|
||||
|
||||
This project already has two flag mechanisms, uses both idiomatically, and is not short of a third. What it lacks is a boolean in one of them, which is about twenty lines of work and needs a reason before it is worth doing.
|
||||
|
||||
No tool was compared until that was established, because a spike that starts by comparing platforms will always find one.
|
||||
|
||||
## What already exists
|
||||
|
||||
**Environment variables, for per-environment gating.** `DEMO_MODE`, `REMBG_URL`, `BREVO_TRACKER_KEY`, `ANTHROPIC_API_KEY`, the USPS and SMTP credentials. The convention is consistent and documented: unset means the feature does not exist, and the application is *working* rather than broken. QA runs with several deliberately empty, which is how it cannot reach PayPal, cannot email anyone, and cannot report browsing into the live Brevo account.
|
||||
|
||||
That is deploy-time gating, and it is genuinely a feature flag system — it just is not called one.
|
||||
|
||||
**`admin_settings`, for runtime tuning by whoever runs the shop.** A typed key/value store with an admin UI, ten settings, per-setting fallbacks, and one place to add another. Its own header says the point plainly: adding a setting means adding a row and nothing else. `drafting_model` and `intake_notify_email` are there rather than in the environment for exactly the reason a flag would be — they are changed by the person running the shop, not the person deploying it, and a redeploy to change an email address would be absurd.
|
||||
|
||||
The two are complementary and the split is already the right one.
|
||||
|
||||
## What is actually missing
|
||||
|
||||
`admin_settings` supports `hours`, `count`, `text` and `choice`. **There is no boolean.**
|
||||
|
||||
So the one thing a flag manager would give this project that it does not have — an admin-flippable on/off for a code path, without a rebuild — is a missing value type in a store that already does everything else. Adding it is a definition entry, a parse branch, and a control in the settings tab.
|
||||
|
||||
That is the whole gap. It is not a reason to run another service on the NAS.
|
||||
|
||||
## Is there anything to flag right now
|
||||
|
||||
No, and the issue said a spike that cannot name one should say so.
|
||||
|
||||
Nine issues are open. Seven are the Passkeys epic and #313, and neither wants a flag:
|
||||
|
||||
- **Passkeys (#37–#42)** is naturally incremental — schema, then registration, then the authentication ceremony, then management, then the login page. Nothing is user-visible until #41 puts it on the login form, so the ordering does the gating that a flag would otherwise do. The epic has also been untouched since 2026-08-21 and its parent #36 is closed, so its status is undecided in a way no tooling addresses.
|
||||
- **#313's `trust proxy` change cannot be flagged.** It is only correct once Cloudflare is actually in front, so it must ship with the DNS cutover. A runtime toggle would let it be wrong on purpose, which is worse than not having one.
|
||||
|
||||
**The strongest hypothetical is a kill switch for passkey login**, because #41 records that it is the one feature in the project that cannot be fully proven in QA — credentials bind to the RP ID, so production is the first place it runs for real. Being able to turn it off without a rebuild would have value there.
|
||||
|
||||
That is a real argument, and it is still not enough today: passkeys are not being built, and **production is not live**, so there is no customer-facing behaviour to protect. Revisit it when #41 is actually being written.
|
||||
|
||||
## What flags would buy here, honestly
|
||||
|
||||
The usual headline — decouple deploy from release — buys much less than it does elsewhere, because there is no CD. Every change already reaches production through a manual NAS rebuild, so the deploy is a deliberate act either way.
|
||||
|
||||
What is left is narrower:
|
||||
|
||||
- **A kill switch**, avoiding a rebuild. The strongest case, because the rebuild is this project's slowest and most error-prone step.
|
||||
- **Shipping half-finished work** behind a flag rather than holding a long branch. Weak here: branches are short and merged the same day.
|
||||
- **Per-environment behaviour** without another env var. Already solved, by the env vars.
|
||||
|
||||
Against all of it: every flag is a branch in the code that has to be removed later, and stale flags are their own debt. For a single-developer shop, that cost is paid by the same person who would benefit.
|
||||
|
||||
## If a tool is ever wanted
|
||||
|
||||
Constraints that decide it: the NAS is CPU and memory limited and already runs Gitea, the CI runner, QA and production; Portainer manages the stacks; cost should stay at zero.
|
||||
|
||||
| Tool | Fit |
|
||||
| --- | --- |
|
||||
| **Flipt** | Lightest — single binary, no heavy datastore. The only one that obviously fits this machine. |
|
||||
| **Flagsmith** | Open source, self-host free with no feature limits, Docker Compose. Heavier than Flipt. |
|
||||
| **GrowthBook** | Free self-hosted and a free cloud starter. Flags and experiments together, which is more than this project needs. |
|
||||
| **Unleash** | Best known, but **OSS Edge sunsets 2026-12-31**, after which self-hosters at scale need Enterprise Edge. A free tool acquiring a paid dependency inside a year is the wrong shape here. |
|
||||
|
||||
A hosted free tier is also possible and is worse for this specific project: it makes the storefront depend on a third party being reachable in order to decide its own behaviour, which is a new outage mode for a site that currently has none.
|
||||
|
||||
**Whatever is chosen, fail-safe behaviour matters more than features.** A flag service that takes the shop down when it is unreachable is worse than having no flags. `admin_settings` reads from the database the application already cannot run without, so it has no failure mode of its own — which is a real advantage over every option in that table.
|
||||
|
||||
## What would change this answer
|
||||
|
||||
Any one of these, and it is worth reopening:
|
||||
|
||||
- A concrete change that genuinely needs to be dark in production while it settles — passkey login being the likely first.
|
||||
- Percentage rollouts or per-customer targeting becoming something anyone actually wants. Nothing in the project has ever asked for either.
|
||||
- More than one person deploying, so "who turned that off" needs an audit trail.
|
||||
- CD arriving, which would make decoupling deploy from release mean something.
|
||||
|
||||
Until one of those, the answer is a boolean column type in `admin_settings`, written when there is a flag to put in it.
|
||||
Reference in New Issue
Block a user