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
- During build, query LaunchDarkly SDK polling endpoint for all flag definitions in an environment
- 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
- Update fallback configuration files with determined values
- 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