FeatureSignals
Guides

Migrating from LaunchDarkly: A Technical Guide with Zero Downtime

A complete migration playbook covering the full 5-step process: discover providers, validate credentials, dry-run analysis, execute import, and monitor progress. We walk through operator mapping (13 operators, negation support), segment migration, environment handling, IaC export for GitOps, and post-migration verification — with real API calls and configuration snippets.

FS
FeatureSignals Solutions Team
·March 2026·12 min read

Why Teams Migrate

We've helped dozens of teams migrate from LaunchDarkly to FeatureSignals. The reasons are consistent: pricing that scales unpredictably with MAU (monthly active users), a desire for self-hosted infrastructure to satisfy data sovereignty requirements, frustration with proprietary SDK lock-in, and a preference for open-source tooling with community-driven development. One team we worked with was paying $72,000/year for LaunchDarkly and migrated to a self-hosted FeatureSignals instance running on a $200/month cloud server with better evaluation latency.

Migration sounds daunting — flags, segments, environments, SDK integrations, CI/CD pipelines — but with the right process, it's a methodical operation that can be completed without any application downtime, any broken flags, or any user impact. This guide walks through exactly how to do it.

Pre-Migration Checklist

Before you start the migration, complete this checklist. Missing items here are the most common source of migration issues:

  • Inventory your flags: Export a complete list from LaunchDarkly. Note which flags are active, which are deprecated but still referenced in code, and which can be archived.
  • Map your environments: LaunchDarkly environments → FeatureSignals environments. The names don't need to match, but the mapping must be documented. Most teams use 1:1 mapping (production→production, staging→staging).
  • Audit your SDK usage: Which languages? Which versions? Are you using LaunchDarkly-specific features like prerequisite flags, percentage rollouts with custom attributes, or experimentation?
  • Identify custom integrations: Webhooks, audit log streaming, Slack notifications, Datadog metrics. These need to be reconfigured post-migration.
  • Set up your FeatureSignals instance: Deploy the server (self-hosted or cloud). Create your organization, project, and environments. Generate API keys.
  • Run the migration in staging first: Never migrate production first. Use staging to validate the process, then repeat for production.

The 5-Step Migration Process

We've refined the migration process into five discrete steps. Each step is independently verifiable, so you can pause, validate, and resume without starting over.

  1. Discover: The migration tool connects to your LaunchDarkly account via API key and enumerates all flags, segments, environments, and targeting rules. Nothing is modified at this stage — it's a read-only inventory.
  2. Validate: The tool checks every flag and rule for compatibility with FeatureSignals. It flags unsupported operators, identifies flags that need manual attention, and generates a compatibility report.
  3. Dry-Run: The tool simulates the migration, creating a preview of what the FeatureSignals configuration will look like. No data is written. Review the preview carefully.
  4. Execute: The tool creates flags, segments, and targeting rules in FeatureSignals. This is idempotent — you can run it multiple times and it won't create duplicates.
  5. Monitor: After migration, run both systems in parallel (dual-read) for 48–72 hours. Compare evaluation results between LaunchDarkly and FeatureSignals to catch any discrepancies before cutting over.

Operator Mapping: LD → FeatureSignals

FeatureSignals supports all standard LaunchDarkly operators plus several extensions. Here's the complete mapping:

text
Operator Mapping Reference

  LD Operator             FS Operator          Notes
  ─────────────────────────────────────────────────────────
  in                      in                    Direct equivalent
  endsWith               endsWith              Direct equivalent
  startsWith             startsWith            Direct equivalent
  matches                matches               Regex matching
  contains               contains              Substring matching
  lessThan               lessThan              Numeric comparison
  lessThanOrEqual        lessThanOrEqual       Numeric comparison
  greaterThan            greaterThan           Numeric comparison
  greaterThanOrEqual     greaterThanOrEqual    Numeric comparison
  before                 before                Date comparison
  after                  after                 Date comparison
  segmentMatch           segmentMatch          Segment membership
  semVerEqual            semVerEqual           SemVer comparison (FS extension)
  semVerLessThan         semVerLessThan        SemVer comparison (FS extension)
  semVerGreaterThan      semVerGreaterThan     SemVer comparison (FS extension)
  percentage             percentage            MurmurHash3-based rollout

Percentage Rollout Conversion

Percentage rollouts work identically in both systems — hash the user key, modulo 100, compare to the rollout percentage. However, LaunchDarkly uses a different hashing algorithm than FeatureSignals (one based on a CRC-like function vs. our MurmurHash3). This means the same user may fall into different buckets across the two systems during the dual-read phase. This is expected and not a problem — the distribution is still uniform at scale. What matters is that both systems serve the correct variation for the bucket the user lands in, not that the buckets are identical.

Infrastructure as Code: Terraform Export

For teams practicing GitOps, the migration tool can export your entire FeatureSignals configuration as Terraform HCL. This gives you version-controlled, reviewable flag configuration that integrates with your existing CI/CD pipeline:

hcl
# Generated by FeatureSignals Migration Tool v2.4.0
# Source: LaunchDarkly project "acme-platform" (production)

resource "featuresignals_flag" "new_checkout" {
  project_key = "acme-platform"
  env_key     = "production"
  key         = "new-checkout"
  name        = "New Checkout Flow"
  description = "Migrated from LaunchDarkly — rollout completed 2026-02-15"
  type        = "boolean"

  default_variation = "off"

  variations = [
    { name = "on",  value = true },
    { name = "off", value = false },
  ]

  rules {
    description = "Internal users"
    priority    = 1
    variation   = "on"

    conditions {
      attribute = "email"
      operator  = "endsWith"
      value     = "@acme-corp.com"
    }
  }

  rules {
    description = "10% gradual rollout"
    priority    = 2
    variation   = "on"

    conditions {
      attribute = "key"
      operator  = "percentage"
      value     = "10"
    }
  }
}

Dual-Read Verification Pattern

The safest migration pattern is dual-read: run both LaunchDarkly and FeatureSignals in parallel and compare results. Here's the pattern in Go:

go
// DualRead evaluates a flag against both the existing LD client
// and the new FeatureSignals client, logs any mismatches, and
// returns the LaunchDarkly result (safe: no behavior change).
func DualRead(
    ldClient *ldclient.LDClient,
    fsClient openfeature.IClient,
    flagKey string,
    user lduser.User,
) bool {
    ctx := context.Background()

    // Evaluate against both systems concurrently
    var ldResult, fsResult bool
    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        ldResult, _ = ldClient.BoolVariation(flagKey, user, false)
    }()

    go func() {
        defer wg.Done()
        fsResult, _ = fsClient.BooleanValue(ctx, flagKey, false,
            openfeature.NewEvaluationContext(
                user.GetKey(),
                map[string]interface{}{
                    "email": user.GetEmail(),
                    "name":  user.GetName(),
                },
            ),
        )
    }()

    wg.Wait()

    // Log mismatches for investigation
    if ldResult != fsResult {
        slog.Warn("dual-read mismatch",
            "flag", flagKey,
            "user", user.GetKey(),
            "ld_result", ldResult,
            "fs_result", fsResult,
        )
    }

    // Return the existing behavior — FeatureSignals is shadowing only
    return ldResult
}

Run the dual-read pattern for 48–72 hours. Monitor the mismatch rate. A mismatch rate under 1% is typical during migration (caused by percentage rollout hash differences, timing differences in flag updates, and edge cases in operator semantics). If the mismatch rate is higher, investigate before cutting over.

Post-Migration: SDK Swap and Cleanup

Once you've validated the dual-read results and are confident in the migration, swap the SDK and decommission LaunchDarkly:

  1. Swap the provider: Replace the LaunchDarkly provider with the FeatureSignals OpenFeature provider in your application initialization. If you're not using OpenFeature yet, this is the time to adopt it.
  2. Remove dual-read code: Delete the shadow evaluation logic and use FeatureSignals as the sole source of truth.
  3. Decommission LaunchDarkly: Export your final audit log. Archive the project. Cancel the subscription.
  4. Monitor: Watch your FeatureSignals dashboards for the first 24 hours. Check evaluation latency, error rates, and flag resolution accuracy.
  5. Celebrate: You're now running on open-source, sub-millisecond feature flags with no vendor lock-in.
💡

Tip

Keep your LaunchDarkly account active for 30 days post-migration as a safety net. If something goes wrong, you can revert the provider swap and be back on LaunchDarkly in minutes. We've never seen a team need this, but it's cheap insurance.