Terug naar blog
saasdevelopmentfeature-flagsdevopsarchitectuur

Feature flags in je SaaS: sneller shippen zonder risico

Door SaaS Masters8 maart 20267 min leestijd
Feature flags in je SaaS: sneller shippen zonder risico

Feature flags (ook wel feature toggles genoemd) zijn een van de krachtigste tools in het arsenaal van een modern SaaS-team. Ze stellen je in staat om nieuwe features veilig uit te rollen, te experimenteren met verschillende gebruikersgroepen en snel terug te schakelen als er iets misgaat — zonder een nieuwe deployment.

In dit artikel duiken we diep in feature flags: hoe ze werken, wanneer je ze inzet, en hoe je ze implementeert in een productie-SaaS-omgeving.

Wat zijn feature flags?

Een feature flag is simpelweg een conditie in je code die bepaalt of een bepaald stuk functionaliteit actief is:

if (featureFlags.isEnabled('new-dashboard', { userId: user.id })) {
  return <NewDashboard />;
} else {
  return <LegacyDashboard />;
}

In plaats van code te deployen en meteen voor iedereen live te zetten, wrap je nieuwe functionaliteit in een flag. Die flag kun je vervolgens aan- en uitzetten via een configuratiepanel, zonder code te wijzigen of opnieuw te deployen.

Waarom feature flags essentieel zijn voor SaaS

1. Trunk-based development mogelijk maken

Zonder feature flags werken teams vaak met langlevende feature branches. Dit leidt tot merge-conflicten, integratieproblemen en trage release-cycles. Met feature flags kun je onafgemaakte code gewoon naar main pushen — de flag houdt het verborgen voor gebruikers.

2. Geleidelijke uitrol (progressive rollout)

In plaats van een feature in één keer voor al je 10.000 gebruikers te activeren, kun je geleidelijk opschalen:

  • 1% — intern testen
  • 5% — beta-gebruikers
  • 25% — early adopters
  • 100% — volledige uitrol

Als er op enig moment problemen opduiken, zet je de flag uit en is het opgelost.

3. A/B-testen ingebouwd

Feature flags zijn de basis voor A/B-testen. Je kunt twee varianten van een feature aanbieden aan verschillende gebruikersgroepen en meten welke beter presteert.

4. Plan-gebaseerde features

Voor SaaS met meerdere pricing tiers zijn feature flags ideaal:

const PLAN_FEATURES = {
  starter: ['basic-analytics', 'email-support'],
  professional: ['basic-analytics', 'email-support', 'advanced-reports', 'api-access'],
  enterprise: ['basic-analytics', 'email-support', 'advanced-reports', 'api-access', 'sso', 'audit-log', 'custom-branding'],
};

Feature flags zelf bouwen: een praktische implementatie

Laten we een lichtgewicht feature flag-systeem bouwen dat je in je eigen SaaS kunt gebruiken.

Database schema

CREATE TABLE feature_flags (
  id TEXT PRIMARY KEY,
  name TEXT UNIQUE NOT NULL,
  description TEXT,
  enabled BOOLEAN DEFAULT false,
  rollout_percentage INT DEFAULT 0,
  target_plans TEXT[] DEFAULT '{}',
  target_user_ids TEXT[] DEFAULT '{}',
  created_at TIMESTAMP DEFAULT now(),
  updated_at TIMESTAMP DEFAULT now()
);

CREATE TABLE feature_flag_overrides (
  id TEXT PRIMARY KEY,
  flag_name TEXT REFERENCES feature_flags(name),
  tenant_id TEXT NOT NULL,
  enabled BOOLEAN NOT NULL,
  created_at TIMESTAMP DEFAULT now(),
  UNIQUE(flag_name, tenant_id)
);

Server-side evaluatie

interface FlagContext {
  userId: string;
  tenantId: string;
  plan: string;
}

class FeatureFlagService {
  private cache: Map<string, FeatureFlag> = new Map();

  async isEnabled(flagName: string, context: FlagContext): Promise<boolean> {
    const flag = await this.getFlag(flagName);
    if (!flag) return false;

    // Check tenant-level override first
    const override = await this.getOverride(flagName, context.tenantId);
    if (override !== null) return override;

    // Global kill switch
    if (!flag.enabled) return false;

    // Plan-based targeting
    if (flag.targetPlans.length > 0 && !flag.targetPlans.includes(context.plan)) {
      return false;
    }

    // Specific user targeting (beta testers)
    if (flag.targetUserIds.includes(context.userId)) return true;

    // Percentage-based rollout (deterministic hash)
    if (flag.rolloutPercentage < 100) {
      const hash = this.hashUserId(context.userId, flagName);
      return hash < flag.rolloutPercentage;
    }

    return true;
  }

  private hashUserId(userId: string, flagName: string): number {
    const str = `${userId}:${flagName}`;
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      hash = ((hash << 5) - hash) + str.charCodeAt(i);
      hash = hash & hash;
    }
    return Math.abs(hash) % 100;
  }
}

React hook voor de frontend

import { createContext, useContext } from 'react';

const FeatureFlagContext = createContext<Record<string, boolean>>({});

export function useFeatureFlag(flagName: string): boolean {
  const flags = useContext(FeatureFlagContext);
  return flags[flagName] ?? false;
}

// Gebruik in componenten:
function PricingPage() {
  const hasNewPricing = useFeatureFlag('new-pricing-page');

  if (hasNewPricing) {
    return <NewPricingPage />;
  }
  return <CurrentPricingPage />;
}

Admin dashboard endpoint

// API route: PATCH /api/admin/feature-flags/:name
export async function updateFlag(req: Request) {
  const { name } = req.params;
  const { enabled, rolloutPercentage, targetPlans } = req.body;

  await db.featureFlags.update({
    where: { name },
    data: {
      enabled,
      rolloutPercentage,
      targetPlans,
      updatedAt: new Date(),
    },
  });

  // Invalidate cache across all instances
  await redis.publish('feature-flag-update', JSON.stringify({ name }));

  // Log the change for audit trail
  await db.auditLog.create({
    data: {
      action: 'feature_flag_updated',
      resource: name,
      changes: { enabled, rolloutPercentage, targetPlans },
      userId: req.user.id,
    },
  });

  return Response.json({ success: true });
}

Build vs buy: wanneer kies je een externe service?

Zelf bouwen als:

  • Je een eenvoudige setup nodig hebt (< 20 flags)
  • Je volledige controle wilt over de data
  • Je geen extra kosten wilt voor een externe service
  • Je flags vooral plan-gebaseerd zijn

Een service gebruiken als:

  • Je complexe targeting rules nodig hebt
  • Je A/B-testen met statistische significantie wilt
  • Je team groter wordt en je governance nodig hebt
  • Je real-time analytics per flag wilt

Populaire opties:

  • LaunchDarkly — de industriestandaard, krachtig maar prijzig
  • Unleash — open-source, self-hosted mogelijk
  • PostHog — combineert feature flags met product analytics
  • Flagsmith — open-source met goede free tier

Best practices voor feature flags in productie

1. Naamgeving en organisatie

Gebruik een consistente naamconventie:

release/new-dashboard        → tijdelijke release flag
experiment/pricing-v2        → A/B test
ops/maintenance-mode         → operationele toggle
permission/advanced-reports  → plan-gebaseerde feature

2. Ruim op na gebruik

Feature flags die permanent aan staan zijn technische schuld. Plan een opruimcyclus:

// Voeg een vervaldatum toe aan je flags
interface FeatureFlag {
  name: string;
  expiresAt?: Date; // Alert als deze datum verstreken is
  owner: string;    // Wie is verantwoordelijk?
}

// In je CI/CD pipeline: waarschuw bij verlopen flags
const expiredFlags = await db.featureFlags.findMany({
  where: {
    expiresAt: { lt: new Date() },
    enabled: true,
  },
});

if (expiredFlags.length > 0) {
  console.warn(`⚠️ ${expiredFlags.length} feature flags zijn verlopen en moeten opgeruimd worden`);
}

3. Monitoring en alerting

Koppel je feature flags aan je monitoring:

  • Track error rates per flag-variant
  • Monitor performance-impact van nieuwe features
  • Stel automatische rollback in bij verhoogde error rates

4. Documenteer je flags

Houd een overzicht bij van actieve flags, hun doel en eigenaar. Dit voorkomt dat flags verweesd raken in je codebase.

5. Test beide paden

Zorg dat je CI/CD pipeline beide varianten test — zowel met de flag aan als uit. Anders ontdek je bugs pas bij het uitschakelen van een flag.

Een real-world scenario

Stel je voor: je SaaS heeft een nieuw AI-powered zoeksysteem gebouwd. In plaats van een big-bang release:

  1. Week 1: Deploy met flag uit. Code zit in productie, maar niemand ziet het.
  2. Week 2: Zet de flag aan voor je interne team. Test in de echte productieomgeving.
  3. Week 3: Activeer voor 5% van je gebruikers. Monitor latency en relevantie.
  4. Week 4: Schaal op naar 25%. Verzamel feedback.
  5. Week 5: 100% rollout. Verwijder de oude zoekcode en de feature flag.

Als op enig moment de latency piekt of gebruikers klagen, draai je de flag terug. Geen hotfix, geen paniek, geen nieuwe deployment.

Conclusie

Feature flags zijn niet alleen een development tool — ze zijn een business tool. Ze geven je de controle om features op het juiste moment, voor de juiste gebruikers, op de juiste manier te lanceren.

Begin klein: implementeer een eenvoudig flag-systeem voor je volgende feature. Je zult merken dat het je hele release-proces transformeert. Sneller shippen, minder risico, meer controle.

Wil je hulp bij het implementeren van feature flags in je SaaS? Neem contact met ons op voor een vrijblijvend gesprek over je architectuur.