Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Automating Fallback Updates

This approach centralizes fallback values in a configuration file and updates them automatically during your build process. The automation queries LaunchDarkly for current flag definitions and determines the appropriate fallback value for each flag.

Centralize Fallback Management

Wrapper Functions

Create wrapper functions around variation() and variationDetail() that load fallback values from a centralized configuration:

Example:

// fallback-config.json
{
  "new-checkout-flow": false,
  "cache-optimization-v2": true,
  "experimental-feature": false
}

// wrapper.ts
import fallbackConfig from './fallback-config.json';

export function variationWithFallback(
  client: LDClient,
  flagKey: string,
  context: LDContext
): LDEvaluationDetail {
  let fallbackValue = fallbackConfig[flagKey];
  if (fallbackValue === undefined) {

    // you may want to make this an error in preproduction environments to catch missing fallback values early
    console.warn(`No fallback value defined for flag: ${flagKey}`);
    // you can use naming convention or other logic to determine a default fallback value
    // for example, release flags may default to failing closed
    if (flagKey.startsWith('release-')) {
      fallbackValue = false;
    }
  }
  return client.variationDetail(flagKey, context, fallbackValue);
}

Benefits:

  • Single source of truth for fallback values
  • Easier to automate fallback value updates
  • Logic can be shared across applications and services

Tradeoffs:

  • Difficult to implement dynamic fallback values (e.g., different fallback values for different users or applications)
  • Loss of locality: fallback values are no longer present in the variation call and require checking the fallback definition file

Process

  1. During build, query LaunchDarkly SDK polling endpoint for all flag definitions in an environment
  2. For each flag, intelligently determine the appropriate fallback value:
    • If flag is OFF → use off variation
    • If prerequisites exist and would not pass for all users → use off variation
    • If flag has multiple variations in targeting (rules, rollouts, etc.) → use off variation
    • If all targeting serves a single variation → use that variation
  3. Update fallback configuration files with determined values
  4. Validate that fallback values match expected types

Example Build Script

Fetch flag data from LaunchDarkly SDK polling endpoint:

#!/bin/bash
# Update fallback values from LaunchDarkly flags with intelligent fallback selection
# Uses the SDK polling endpoint which provides all flags for an environment

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

curl -s -H "Authorization: ${LD_SDK_KEY}" \
  "https://sdk.launchdarkly.com/sdk/latest-all" \
  | jq -f "${SCRIPT_DIR}/parse-fallbacks.jq" \
  > fallback-config.json

Parse fallback values using intelligent logic (handles prerequisites, rollouts, and targeting rules):

# Recursive function to get recommended fallback for a flag
def get_fallback($flag; $allFlags):
  # Helper function to get off variation value
  def get_off_variation_value:
    if $flag.offVariation != null then
      $flag.variations[$flag.offVariation]
    else
      (debug("WARNING: Flag '\($flag.key)' has no offVariation set, omitting from output")) | empty
    end;

  # Helper function to check if flag has multi-variation rollouts
  def has_multi_rollout:
    (
      # Check rules for multi-variation rollouts (with null safety)
      (($flag.rules // []) | map(
        if .rollout and .rollout.variations then
          (.rollout.variations | map(select(.weight > 0)) | length) > 1
        else
          false
        end
      ) | any) or

      # Check fallthrough for multi-variation rollout (with null safety)
      (if $flag.fallthrough.rollout and $flag.fallthrough.rollout.variations then
        ($flag.fallthrough.rollout.variations | map(select(.weight > 0)) | length) > 1
      else
        false
      end)
    );

  # Helper function to get all unique variations served
  def get_all_variations:
    (
      # Legacy target variations
      ([$flag.targets[]?.variation] // []) +

      # Context target variations
      ([$flag.contextTargets[]?.variation] // []) +

      # Rule variations (either direct or from single-variation rollout)
      (($flag.rules // []) | map(
        if .variation != null then
          .variation
        elif .rollout and .rollout.variations then
          (.rollout.variations[] | select(.weight > 0) | .variation)
        else
          empty
        end
      )) +

      # Fallthrough variation
      (if $flag.fallthrough.variation != null then
        [$flag.fallthrough.variation]
      elif $flag.fallthrough.rollout and $flag.fallthrough.rollout.variations then
        [$flag.fallthrough.rollout.variations[] | select(.weight > 0) | .variation]
      else
        []
      end)
    ) | unique;

  # Step 1: Check prerequisites first
  if ($flag.prerequisites and ($flag.prerequisites | length) > 0) then
    # Check each prerequisite
    ($flag.prerequisites | map(
      . as $prereq |
      # Find the prerequisite flag in all flags
      ($allFlags[] | select(.key == $prereq.key)) as $prereqFlag |

      # Get the fallback value for the prerequisite flag (recursive call)
      get_fallback($prereqFlag; $allFlags) as $prereqFallback |

      # Check if prerequisite passes:
      # 1. If prereq flag is OFF, prerequisite fails
      # 2. If prereq fallback does not match expected variation value, prerequisite fails
      if ($prereqFlag.on == false) then
        false
      elif ($prereqFallback != $prereqFlag.variations[$prereq.variation]) then
        false
      else
        true
      end
    ) | all) as $allPrereqsPass |

    # If any prerequisite fails, return off variation
    if $allPrereqsPass == false then
      get_off_variation_value
    else
      # All prerequisites pass, continue with normal evaluation
      if $flag.on == false then
        get_off_variation_value
      elif has_multi_rollout then
        get_off_variation_value
      else
        get_all_variations as $variations |
        if ($variations | length) == 1 then
          $flag.variations[$variations[0]]
        else
          get_off_variation_value
        end
      end
    end
  else
    # No prerequisites, use normal evaluation logic
    if $flag.on == false then
      get_off_variation_value
    elif has_multi_rollout then
      get_off_variation_value
    else
      get_all_variations as $variations |
      if ($variations | length) == 1 then
        $flag.variations[$variations[0]]
      else
        get_off_variation_value
      end
    end
  end;

# Main processing: convert flags object to array and process each one
.flags | to_entries | map(.value) as $allFlags |
[$allFlags[] |
  . as $flag |
  {
    name: .key,
    value: get_fallback($flag; $allFlags)
  }
] | from_entries

Benefits

  • Ensures fallback values match current flag definitions
  • Reduces manual maintenance overhead
  • Catches drift between code and flag definitions
  • Supports automated flag lifecycle management

Considerations

  • Uses the SDK polling endpoint (https://sdk.launchdarkly.com/sdk/latest-all) which provides all flags for an environment
  • Requires an SDK key (not an API key) to authenticate
  • The logic for determining fallback values is complex and handles prerequisites, rollouts, and targeting rules
  • Prerequisites are evaluated recursively to determine if they would pass for all users
  • Ensure your team is aware of how fallback values are generated from targeting state
  • The generated fallback values represent the “safest” value for each flag during an outage