Key Mapping Reference¶
Parsing and formatting infrastructure for converting source system names into flag keys.
Module: flagzen-key-mapping
Overview¶
Key mapping solves the problem of translating source names (like environment variable names) into canonical flag keys. It consists of:
FlagKeyParser— parses a source name into segmentsFlagKeyParsers— factory methods for common parser implementationsFlagKeyFormat— formats segments into a flag key stringFlagKeyFormats— factory methods for common formatter implementationsConflictStrategy— handles conflicts when multiple source names map to the same flag key
FlagKeyParser¶
Parses a source name into lowercase key segments.
Interface¶
@FunctionalInterface
public interface FlagKeyParser {
Optional<List<String>> parse(String sourceName);
}
Contract¶
- Takes a raw source name (e.g., an environment variable name)
- Returns lowercase segments if the name matches the expected pattern
- Returns
Optional.empty()if the name does not match - Each segment is a single lowercase word
Built-In Parsers¶
FlagKeyParsers.screamingSnakeCase(String prefix)¶
Matches SCREAMING_SNAKE_CASE names with a given prefix.
Signature
Example
FlagKeyParser parser = FlagKeyParsers.screamingSnakeCase("FLAGZEN_");
parser.parse("FLAGZEN_CHECKOUT_FLOW");
// => Optional.of(["checkout", "flow"])
parser.parse("FLAGZEN_MAX_RETRIES");
// => Optional.of(["max", "retries"])
parser.parse("OTHER_PREFIX_VALUE");
// => Optional.empty() (prefix mismatch)
FlagKeyParsers.screamingSnakeCase()¶
Matches any SCREAMING_SNAKE_CASE name without a prefix.
Signature
Example
FlagKeyParser parser = FlagKeyParsers.screamingSnakeCase();
parser.parse("CHECKOUT_FLOW");
// => Optional.of(["checkout", "flow"])
parser.parse("MAX_RETRIES");
// => Optional.of(["max", "retries"])
parser.parse("lowercase_value");
// => Optional.of(["lowercase", "value"]) (uppercasing is not validated)
FlagKeyParsers.camelCase(String prefix)¶
Matches camelCase names with a given prefix, splitting on uppercase boundaries.
Signature
Example
FlagKeyParser parser = FlagKeyParsers.camelCase("myApp");
parser.parse("myAppCheckoutFlow");
// => Optional.of(["checkout", "flow"])
parser.parse("myAppMaxRetries");
// => Optional.of(["max", "retries"])
parser.parse("otherAppValue");
// => Optional.empty() (prefix mismatch)
FlagKeyParsers.camelCase()¶
Matches any camelCase name without a prefix.
Signature
Example
FlagKeyParser parser = FlagKeyParsers.camelCase();
parser.parse("checkoutFlow");
// => Optional.of(["checkout", "flow"])
parser.parse("maxRetries");
// => Optional.of(["max", "retries"])
parser.parse("x");
// => Optional.of(["x"]) (single segment)
FlagKeyFormat¶
Formats lowercase key segments into a flag key string.
Interface¶
Contract¶
- Takes a list of lowercase segments
- Returns a formatted flag key string
- All segments should be non-empty and lowercase
Built-In Formatters¶
FlagKeyFormats.kebabCase()¶
Joins segments with hyphens.
Example
FlagKeyFormat format = FlagKeyFormats.kebabCase();
format.format(["checkout", "flow"]);
// => "checkout-flow"
format.format(["max", "retries"]);
// => "max-retries"
FlagKeyFormats.snakeCase()¶
Joins segments with underscores.
Example
FlagKeyFormat format = FlagKeyFormats.snakeCase();
format.format(["checkout", "flow"]);
// => "checkout_flow"
format.format(["max", "retries"]);
// => "max_retries"
FlagKeyFormats.camelCase()¶
Joins segments in camelCase (first segment as-is, subsequent segments capitalized).
Example
FlagKeyFormat format = FlagKeyFormats.camelCase();
format.format(["checkout", "flow"]);
// => "checkoutFlow"
format.format(["max", "retries"]);
// => "maxRetries"
FlagKeyFormats.pascalCase()¶
Joins segments in PascalCase (all segments capitalized).
Example
FlagKeyFormat format = FlagKeyFormats.pascalCase();
format.format(["checkout", "flow"]);
// => "CheckoutFlow"
format.format(["max", "retries"]);
// => "MaxRetries"
FlagKeyFormats.dotCase()¶
Joins segments with dots.
Example
FlagKeyFormat format = FlagKeyFormats.dotCase();
format.format(["checkout", "flow"]);
// => "checkout.flow"
format.format(["max", "retries"]);
// => "max.retries"
FlagKeyFormats.colonCase()¶
Joins segments with colons.
Example
FlagKeyFormat format = FlagKeyFormats.colonCase();
format.format(["checkout", "flow"]);
// => "checkout:flow"
format.format(["max", "retries"]);
// => "max:retries"
ConflictStrategy¶
Strategy for handling conflicts when multiple source names map to the same flag key.
Enum¶
Values¶
| Value | Behavior |
|---|---|
WARN |
Log a warning at construction and on first access, keep the last mapping |
ERROR |
Throw IllegalStateException at construction, provider is not created |
Cardinality-Based Defaults¶
The default conflict strategy depends on the number of parsers and formatters:
| Parsers | Formatters | Default | Reasoning |
|---|---|---|---|
| 1 | 1 | WARN | Low collision risk (only env var name collision) |
| N | 1 | WARN | Medium risk (different parsers may overlap) |
| 1 | N | WARN | Medium risk (multiple formatters reduce collisions) |
| N | N | ERROR | High risk (cartesian product maximizes collisions) |
Example¶
// Low cardinality: WARN by default
EnvironmentVariableFlagProvider provider = EnvironmentVariableFlagProvider.builder()
.parser(FlagKeyParsers.screamingSnakeCase("FLAGZEN_"))
.format(FlagKeyFormats.kebabCase())
.build();
// If FLAGZEN_X and other sources map to same key, logs warning
// High cardinality: ERROR by default
EnvironmentVariableFlagProvider provider = EnvironmentVariableFlagProvider.builder()
.parser(FlagKeyParsers.screamingSnakeCase("FLAGZEN_"))
.parser(FlagKeyParsers.screamingSnakeCase("FF_"))
.format(FlagKeyFormats.kebabCase())
.format(FlagKeyFormats.snakeCase())
.build();
// Throws IllegalStateException if any conflict detected
// Override default
EnvironmentVariableFlagProvider provider = EnvironmentVariableFlagProvider.builder()
.parser(FlagKeyParsers.screamingSnakeCase("FLAGZEN_"))
.parser(FlagKeyParsers.screamingSnakeCase("FF_"))
.format(FlagKeyFormats.kebabCase())
.format(FlagKeyFormats.snakeCase())
.onConflict(ConflictStrategy.WARN)
.build();
// Logs warnings instead of throwing
Parse/Format Pipeline Example¶
Combining parsers and formatters to create a full mapping:
FlagKeyParser parser = FlagKeyParsers.screamingSnakeCase("FLAGZEN_");
FlagKeyFormat format = FlagKeyFormats.kebabCase();
// Environment variable: FLAGZEN_CHECKOUT_FLOW
// Step 1: Parse
Optional<List<String>> segments = parser.parse("FLAGZEN_CHECKOUT_FLOW");
// => Optional.of(["checkout", "flow"])
// Step 2: Format
String flagKey = segments.map(format::format).orElse(null);
// => "checkout-flow"
// Step 3: Resolve flag
FlagProvider provider = ...;
Optional<String> value = provider.getString("checkout-flow");
Best Practices¶
- Be explicit about prefix: Use
screamingSnakeCase("PREFIX_")rather thanscreamingSnakeCase()to avoid accidentally matching unrelated env vars. - Match your naming convention: Choose parsers/formatters that match your application's naming style:
- Env vars:
screamingSnakeCase("PREFIX_")parser →kebabCase()format - Spring properties:
camelCase()parser →kebabCase()format - Vault paths: custom parser →
colonCase()format - Watch for conflicts: If using multiple parsers/formatters, test with your actual env vars to catch conflicts. The
ERRORdefault for high-cardinality configs is there to catch these early. - Document mapping: In your configuration, document the parser/formatter combination so users understand the env var naming convention.
Related¶
EnvironmentVariableFlagProvider— uses key mapping to parse env var namesFlagProvider— the flag resolution interface