How I Built Rollgate: From Side Project to Feature Flags SaaS

How I Built Rollgate: From Side Project to Feature Flags SaaS
A year ago, Rollgate was a folder on my laptop: a handful of Go files, a Postgres schema I kept rewriting, and the stubborn idea that feature flags did not need to cost $70,000 a year.
Today it is a feature flags SaaS running in production, with the capabilities I wanted from LaunchDarkly (gradual rollouts, user targeting, A/B testing, kill switches) and none of the enterprise invoice.
This is the story of how that folder became a product: the architecture I chose, the features that took longer than I planned, the mistakes, and the real numbers most people keep to themselves.
The Problem
If you have shipped in a team, you know the spot. The feature is ready, you are fairly sure it works, but "fairly sure" across every user and edge case is doing a lot of lifting. Without flags, a deploy is all-or-nothing. It is live for everyone or for no one.
Feature flags split deployment from release. The code ships to production turned off, then you ramp it: 1% of users, then 5%, then a quarter, watching as you go. If it misbehaves, you flip it off. No revert commit, no hotfix at 2am.
LaunchDarkly does all of this, and does it well. It also bills somewhere between $25k and $150k a year, which is a non-starter for most startups and mid-size teams. So the question I kept circling back to was whether one person could build something close and sell it for a fraction of that.
Choosing the Tech Stack
The stack came first. Two things mattered more than anything else. Latency, because flags get evaluated on basically every request, and operational simplicity, because I am the only one on call and fewer moving parts means fewer 3am pages.
Backend: Go
The API is Go. A flag lookup has to finish in microseconds, and Go compiles to a native binary with no GC pauses worth worrying about, chewing through thousands of concurrent connections on goroutines without much fuss. Deployment is a single static binary, no runtime to install, and the final Docker image is 20MB. The standard library covers most of what I need (HTTP, JSON, crypto, testing), so I am not pulling in a dependency for every little thing.
Routing is Chi. It is thin and plays nice with net/http. I skipped the big frameworks; I would rather compose small pieces than learn someone's conventions.
Frontend: Next.js
The dashboard is Next.js with React and Tailwind. SSR and static generation handle the marketing pages for SEO, the API routes proxy to the Go backend, and day to day the dev experience is pleasant enough that I do not fight it.
Database: PostgreSQL + Redis
Postgres holds the source of truth. JSONB for flag metadata, partial indexes where they earn their keep, LISTEN/NOTIFY to invalidate caches. It has grown to 29 tables, 67 indexes, 29 foreign keys.
Redis sits in front as the evaluation cache. When an SDK asks whether flag X is on for user Y, Redis answers in milliseconds, and pub/sub keeps it fresh.
Rollgate's Feature Set
There is more under the hood than an on/off switch. The feature set covers most of the release lifecycle, so here is the tour.
Feature Flags with Multiple Types
A flag can return one of four types:
- Boolean, the classic on/off, which is what you want for kill switches and plain toggles.
- String, handy for A/B variants like "variant-a" / "variant-b", or for swapping copy without a deploy.
- Number, for limits, percentages, or a knob on some algorithm.
- JSON, when you need a whole config object: a layout, a plan's feature set, a chunk of business rules.
Gradual Rollouts
Percentage rollout turns a flag on for a slice of users. The trick is consistent hashing (MurmurHash3) over the user ID, so a given user lands the same way every time and I do not have to store who is in and who is out:
hash = MurmurHash3(flagKey + userId) % 10000
isEnabled = hash < rolloutPercentage * 100
That gets you 0.01% precision. Ramp 1, 5, 25, 100 at whatever pace your nerves allow, watching the dashboards between steps.
User Targeting and Segments
Targeting switches a flag on for specific groups based on attributes:
- Match on user attributes: email, plan, country, app version, or anything custom you send.
- Operators include equals, contains, startsWith, endsWith, regex, in, greaterThan, lessThan.
- Define a segment like "Beta Users" or "Enterprise Customers" once and reuse it across flags.
- Combine conditions with AND/OR when one rule is not enough.
Kill Switches
If I had to keep one feature, it would be the kill switch. Something breaks in production (a bug, a slow query, a payment provider having a bad day) and you need that feature gone now, not after a deploy.
Every flag has a toggle that propagates over SSE in seconds. No deploy, no SSH, no developer required. A product manager can kill a feature from their phone on the train.
I have watched teams burn thirty to forty-five minutes on a traditional rollback: revert the commit, wait for CI, deploy, verify it actually took. A kill switch is two seconds. Mid-incident, the difference is real money.
Scheduled Changes
Scheduled changes let you queue a flag change for a date and time:
- Line up a launch: enable the feature Friday at 2pm, right when marketing publishes the blog post.
- Run a promo on rails: Black Friday banner on from Friday, off Monday.
- Automate the ramp: 1% Monday, 10% Wednesday, 50% Friday, 100% the Monday after.
- Sunset something on schedule: turn the old API off on April 1st and forget about it.
Each one shows up in the flag's timeline as pending, executed, or cancelled. A cron job on the server wakes up every minute and applies whatever is due.
Instant Rollback
Every change to a flag gets saved with a timestamp, who did it, and a full snapshot of the state. Rollback puts the flag back exactly as it was at some earlier point. The value, sure, but also the targeting rules, the percentages, the scheduled changes.
It is more than an undo. You can jump back five changes, or to a specific version from last Tuesday. The audit log keeps the who and the when.
A/B Testing
String variant flags give you native A/B testing. Instead of true/false, the flag hands back a variant ("control", "variant-a", "variant-b") with whatever split you set.
Evaluation tracking records which variant each user saw, with timestamp and context, so you can line variants up against business metrics in whatever analytics tool you already use.
I deliberately left out a built-in stats engine. Teams already have analytics they trust, and I did not want to be one more data silo to reconcile.
Multi-Environment
Every project gets separate environments: development, staging, production, plus any others you spin up. Flags live independently in each, so a flag can be on in staging and off in prod.
Each environment carries its own API keys (server and client), its own rules, its own history. It is the guardrail against the classic "oh no, that was prod, not staging".
Audit Log
Everything lands in an immutable audit log. Who created, changed, or deleted a flag, the before/after diff, the exact timestamp, the IP and user agent it came from.
Retention follows the plan: 3 days on Free, 14 on Starter, 90 on Pro, a year on Growth. When you are chasing a bug or answering a compliance question, that trail is the thing that saves you.
Webhooks
Webhooks tell other systems when a flag moves. Create, update, delete: each one fires an HTTP POST to your URLs with the full event payload.
People wire them to ping Slack on a prod change, kick off a deploy, nudge monitoring, sync a project tool. There is automatic retry with exponential backoff, and every delivery is logged so you can see what landed.
Analytics and Usage Tracking
Evaluations get tracked as they happen: total count, unique users, the true/false split, how it trends over time. Usage caps go by plan: 500K requests a month on Free, 1M on Starter, 3M on Pro, 12M on Growth.
12 SDKs
There are 12 SDKs (plus a shared core package), which is probably the part that ate the most evenings:
- JavaScript and TypeScript: sdk-node, sdk-browser, sdk-react, sdk-vue, sdk-angular, sdk-svelte, all built on a shared sdk-core.
- Mobile: sdk-react-native, sdk-flutter.
- Backend: sdk-go, sdk-java, sdk-python, sdk-dotnet.
They all share the same machinery: a local cache, a circuit breaker, retry with backoff, event buffering, live SSE updates. If the API goes dark, the SDK keeps serving the last values it had instead of falling over.
import { RollgateProvider, useFlag } from '@rollgate/sdk-react';
function App() {
return (
<RollgateProvider apiKey="rg_client_your_key">
<Dashboard />
</RollgateProvider>
);
}
function Dashboard() {
const showNewFeature = useFlag('new-dashboard', false);
return showNewFeature ? <NewDashboard /> : <LegacyDashboard />;
}
System Architecture
Client SDK → API Server (Go) → Redis Cache → PostgreSQL
↑
SSE Connection (real-time updates)
A flag evaluation lands around 200μs. P99 stays under a millisecond. The system holds tens of thousands of SSE connections open at once.
Technical Challenges
Race Conditions on Updates
Two people editing the same flag at once used to worry me. Optimistic locking with a version counter handles it: if the flag changed under you, the write comes back 409 Conflict and you reconcile.
Circuit Breaker in SDKs
If the API is down, an SDK absolutely cannot take the host app down with it. The circuit breaker runs the usual three states (Closed, Open, Half-Open), with exponential retry and request dedup so a thundering herd does not pile on.
SEO for a SaaS App
I split marketing and app across domains: rollgate.io for the SEO pages (Server Components) and app.rollgate.io for the authenticated dashboard.
Resilience and Monitoring
- Backups: a full PostgreSQL dump every night, shipped to a server in a different datacenter.
- Monitoring: Prometheus, Grafana, Alertmanager, with alerts that actually page me.
- Health checks: API, Web, DB, and Redis get poked every 60 seconds.
- Disaster recovery: one script rebuilds the whole stack in about 20 minutes, and I have tested it more than once.
- SDK resilience: local cache plus circuit breaker means clients survive an outage.
The Numbers
| Metric | Value |
|---|---|
| Go files (non-test) | 147 |
| TypeScript/TSX files | 168 |
| API endpoints | 114 |
| DB tables | 29 |
| Total tests | ~850 |
| Published SDKs | 13 |
| Codebase score | 7.86/10 |
What I Learned
1. Simplicity Wins
No Kubernetes. Docker Compose runs the whole thing and I sleep fine. No microservices either; a modular monolith is plenty for one person to reason about. Every bit of complexity bills you in maintenance forever, and most of it is not worth the rent.
2. Tests Save Lives
Around 850 tests is what lets me refactor without holding my breath. CI runs the lot on every PR and will not let a deploy through if anything is red.
3. Billing Is Harder Than the Product
Payments with Paddle were harder than half the product. Webhooks, subscription lifecycle, proration, dunning, tax. Take the provider that swallows all of it (Paddle, Lemon Squeezy) instead of wiring raw Stripe yourself, unless billing is the thing you want to spend your life on.
4. SEO Requires Constant Attention
If Google does not index you, you do not exist. I lost weeks to SSR, JSON-LD, sitemaps, meta tags, and it is never actually finished.
5. Building Solo Is Sustainable (With Limits)
Writing the code turned out to be the comfortable part. Finding people who want it is the hard one. Marketing, support, growth: different muscles, and I am still building them.
What's Next
Rollgate is live at rollgate.io with a generous free tier (500K requests/month). On the bench: smarter A/B testing that calls the winner on its own, teams with SSO, the self-hosted build, and a full OpenAPI spec.
The Market: Competitors and Positioning
A handful of players own the feature flags market, most of them priced for enterprise:
| Platform | Pricing | Strengths | Limitations |
|---|---|---|---|
| LaunchDarkly | From $833/mo | Market leader, enterprise-ready, vast integrations | Prohibitive for startups, excessive complexity |
| Flagsmith | From $45/mo | Free self-hosted, API-first | Less polished UI, fewer SDKs |
| Unleash | From $80/mo | Mature self-hosted, good docs | Complex setup, dated UI |
| PostHog | Usage-based | Complete suite (analytics + flags), open-source | Flags are not the focus, overhead |
| Rollgate | Free → €45/mo | Accessible pricing, 12 SDKs, 5-min setup | New to market, growing community |
Choosing the Pricing Model
Pricing was one of the calls I changed my mind on the most. Looking across competitors, it came down to three shapes:
- Per-seat, like LaunchDarkly. Scales with headcount and punishes big teams; $70k a year for 50 developers is not a weird number.
- Usage-based, like PostHog. Scales with traffic and makes budgeting a guessing game, since a spike can blow up the bill.
- Fixed tiers, which is what I went with. The price is set by features and limits, so a customer knows the number before the month starts.
I picked fixed tiers because for startups and small teams, a predictable invoice beats a clever one. The four plans:
| Plan | Monthly | Annually | Requests/mo | Team | Target |
|---|---|---|---|---|---|
| Free | €0 | €0 | 500K | 3 members | Side projects, MVPs |
| Starter | €45/mo | €39/mo | 1M | 5 members | Early-stage startups |
| Pro | €119/mo | €99/mo | 3M | 15 members | Growing teams |
| Growth | €349/mo | €299/mo | 12M | 50 members | Scale-ups, high traffic |
The free tier is deliberately generous. 500K requests a month covers an app with thousands of active users. I would rather lower the bar to entry: if someone tries Rollgate and it just works, moving up to Starter when the project grows is the easy, obvious step.
Why Not Open Source?
I looked hard at open-core (free core, paid enterprise bits). It really needs a live community to pull its weight, and for a one-person project the failure mode is handing everything away and monetizing none of it. So it is SaaS with a generous free tier for now, and a self-hosted build under BSL (Business Source License) on the roadmap, the same route HashiCorp and Sentry took.
A year after that folder on my laptop, Rollgate is live, the SDKs are published, and the Postgres schema finally stopped changing. That is how I built it, and it is still being built.
If you are looking for a LaunchDarkly alternative that does not cost as much as an employee, try Rollgate. It is free to get started.
Continue reading
What Are Feature Flags? A Complete Guide for 2026
What is a feature flag? A remote switch that enables features without redeploying code. How feature flags work: types, use cases, best practices.
18 min readtestingHow We Test a Feature Flag Platform: 1,192 Tests Across 12 SDKs
How we test a feature flag platform with 1,192 tests across 12 SDKs: unit tests, contract tests for cross-SDK consistency, and stress testing 28K SSE connections.
12 min readfeature flagsFeature Flags in Go: A Practical Guide with Examples
Implement feature flags in Go applications. From simple boolean checks to gradual rollouts, targeting, and A/B testing with the Rollgate Go SDK.
14 min readfeature flagsFeature Flags in Next.js: Step-by-Step Guide (App Router)
Add feature flags to your Next.js app in minutes. Toggle features, run A/B tests, and do gradual rollouts with Rollgate. No redeploy needed. Free to start.
13 min read