You finished the feature. Tests pass. It works on your machine. You deploy.
Forty minutes later a user emails you: "The dashboard is blank." You check your test account -- fine. You check three others -- fine. Six customers are looking at a blank screen because the feature works unless they have more than 500 rows in their data table.
You just learned what feature flags are for.
LaunchDarkly starts at $20 per month for hobby use and climbs past $200 for a real team. It is a good tool. It is also three database columns you already have in your Neon DB.
The three patterns every SaaS actually needs:
You can build all three in an afternoon with the Next.js SaaS Boilerplate already in place.
Add a featureFlags table to your Drizzle schema:
export const featureFlagTable = pgTable('feature_flags', {
id: uuid('id').defaultRandom().primaryKey(),
name: varchar('name', { length: 100 }).notNull().unique(),
enabled: boolean('enabled').notNull().default(false),
rolloutPercent: integer('rollout_percent').notNull().default(0),
allowedUserIds: text('allowed_user_ids').array().notNull().default([]),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
One row per flag. enabled: false is the kill switch. rolloutPercent: 10 enables the flag for a stable 10% of your user base. allowedUserIds handles private betas where specific users get early access.
In your service layer, a single function decides whether a flag is active for a given user:
export async function isFlagEnabled(flagName: string, userId: string): Promise<boolean> {
const flag = await featureFlagRepo.findByName(flagName);
if (!flag || !flag.enabled) return false;
if (flag.allowedUserIds.includes(userId)) return true;
if (flag.rolloutPercent >= 100) return true;
if (flag.rolloutPercent === 0) return false;
const hash = userId.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
return (hash % 100) < flag.rolloutPercent;
}
The hash assigns each user to a stable bucket so they do not see the feature appear and disappear on each page load. A user in the 10% rollout stays in that group every time.
Because your data fetching lives in server components, the flag check is a single await:
const showNewDashboard = await isFlagEnabled('new_dashboard', user.id);
Pass the result as a prop to the component that needs it. No client-side requests, no layout shift, no flicker.
You can manage flags directly from Drizzle Studio (npm run db:studio) during development. For production, a flags tab inside your admin panel takes about 30 minutes to add -- a table listing flags with a toggle button per row.
Here is the sequence that works in practice:
enabled: true and rolloutPercent: 0allowedUserIdsrolloutPercent to 10 and clear allowedUserIdsStep 7 is the one founders skip. Flags that are never cleaned up become invisible tech debt -- you will not remember in six months what the new_dashboard_v3 flag controls. Keep a list and remove each one once its rollout reaches 100%.
Use flags for: schema changes that affect UI, new pricing experiments, features you are not ready to support publicly, anything that touched code paths you cannot easily test with your own account.
Skip flags for: bug fixes, security patches, cosmetic changes. A flag adds an extra code path that has to be tested and eventually removed. The risk of shipping a CSS tweak to everyone is lower than the ongoing cost of an unused flag.
Once this is in place you have a real deployment safety net. You can ship on a Friday afternoon because a broken feature affects 5% of users instead of all of them. You can give paying customers early access to features still in beta. You can turn something off the moment a user reports an issue without touching the codebase.
None of that requires a third-party service. It requires a table, a function, and the discipline to clean flags up after rollout.
The Next.js SaaS Boilerplate ships with Drizzle ORM, Neon DB, and the service-layer architecture that makes a check like isFlagEnabled a natural fit. Auth, payments, and email are already wired -- so adding feature flags is a migration and a function, not a week of integration work.
Ship your next feature to 5 users first. Get the boilerplate and launch your idea this weekend.