RFC 002: Light Feature Flag System for SaaS API Template

Summary

This RFC proposes the implementation of a light feature flag system within the Rust-based SaaS API template. The feature flag system will enable developers to toggle features on or off for individual users, workspaces, or globally across the application. This system provides flexibility for rolling out new features, conducting A/B testing, and managing feature access based on user segments or subscription tiers.

Motivation

Feature flag systems are essential for modern SaaS applications, allowing for controlled and gradual deployment of new features. By enabling or disabling features dynamically without requiring code changes or redeployment, developers can reduce risk, perform A/B testing, and offer differentiated experiences to users. Integrating a light feature flag system into the API template will enhance the flexibility and manageability of feature rollouts and help in maintaining a stable production environment.

Detailed Design

1. Overview

The feature flag system will be a lightweight mechanism to control the availability of specific features within the application. Features can be toggled on or off based on the scope—globally, per workspace, or per user. The system will rely on a simple database-backed implementation to store and manage feature flags.

2. Database Schema

A new feature_flags table will be introduced to manage the feature flags:

CREATE TABLE feature_flags (
    id SERIAL PRIMARY KEY,
    feature_name TEXT NOT NULL,
    is_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    scope TEXT NOT NULL,
    scope_id UUID NULL,
    UNIQUE (feature_name, scope, scope_id)
);
  • feature_name: A unique identifier for the feature (e.g., "new_dashboard_ui").
  • is_enabled: A boolean flag indicating whether the feature is enabled (TRUE) or disabled (FALSE).
  • scope: The scope of the feature flag. It can be global, user, or workspace.
  • scope_id: The UUID of the user or workspace if the scope is specific. For global flags, this will be NULL.

3. API Endpoints

New API routes will be introduced for managing feature flags:

  • GET /feature_flags: Retrieves a list of all feature flags.
  • POST /feature_flags: Creates or updates a feature flag.
    • Request Body:
      {
        "feature_name": "new_dashboard_ui",
        "is_enabled": true,
        "scope": "workspace",
        "scope_id": "workspace-uuid-1234"
      }
      
  • GET /feature_flags/:feature_name: Checks the status of a specific feature flag.
  • DELETE /feature_flags/:feature_name: Deletes a feature flag.

4. Checking Feature Flags in Code

Developers will use a middleware function to check if a feature is enabled before executing feature-specific code. This function will query the feature_flags table based on the provided scope and feature name.

Example function:

#![allow(unused)]
fn main() {
async fn is_feature_enabled(
    feature_name: &str,
    scope: &str,
    scope_id: Option<Uuid>,
    db: &PgPool
) -> Result<bool, sqlx::Error> {
    let query = match scope {
        "user" | "workspace" => sqlx::query!(
            "SELECT is_enabled FROM feature_flags WHERE feature_name = $1 AND scope = $2 AND scope_id = $3",
            feature_name, scope, scope_id
        ),
        "global" => sqlx::query!(
            "SELECT is_enabled FROM feature_flags WHERE feature_name = $1 AND scope = $2",
            feature_name, scope
        ),
        _ => return Ok(false),
    };
    let flag = query.fetch_optional(db).await?;
    Ok(flag.map(|f| f.is_enabled).unwrap_or(false))
}
}

5. Feature Flag Precedence

The feature flag system will prioritize feature availability in the following order:

  1. User-specific flag: Highest precedence.
  2. Workspace-specific flag: Applied if no user-specific flag is found.
  3. Global flag: Default if no specific flags exist for the user or workspace.

6. Example Use Cases

  • Beta Testing: Enable a new feature only for specific users to gather feedback before a full release.
  • Premium Features: Offer exclusive features to users with premium subscriptions by enabling the flag at the user level.
  • Gradual Rollout: Slowly enable a feature for one workspace at a time to monitor stability before a wider deployment.

Implementation Plan

  1. Phase 1: Implement the database schema and basic API routes for managing feature flags.
  2. Phase 2: Integrate the feature flag checking mechanism into existing and new feature implementations.
  3. Phase 3: Test and validate the feature flag system to ensure it behaves correctly under different scenarios.
  4. Phase 4: (Optional) Develop a simple admin UI for managing feature flags.