Skip to main content

Cascading Model

SuperTOML uses a cascading model for configuration overrides, inspired by CSS (Cascading Style Sheets). Just as CSS allows styles to cascade from general to specific selectors, SuperTOML allows configuration values to cascade from general to specific contexts.

The CSS Analogy

In CSS, styles cascade based on selector specificity:

/* General rule */
p {
color: black;
}

/* More specific rule */
p.highlight {
color: blue;
}

/* Most specific rule */
p#intro.highlight {
color: red;
}

A paragraph with id="intro" and class="highlight" gets color: red because that rule is most specific.

SuperTOML applies the same principle to configuration:

# General rule
[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

# More specific rule
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

# Most specific rule
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab", hour_of_day = 18 }
per_km_rate = 30.0

A cab ride in Bangalore at 6 PM gets per_km_rate = 30.0 because that context is most specific.

How Cascading Works

1. Start with Defaults

All resolution begins with the default configuration:

[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }
surge_factor = { value = 0.0, schema = { type = "number" } }

For any context, these are the base values.

2. Find Matching Contexts

When resolving configuration for a runtime context, find all overrides whose _context_ matches:

Runtime context:

{ "city": "Bangalore", "vehicle_type": "cab", "hour_of_day": 18 }

Matching overrides:

# ✅ Matches (vehicle_type matches)
[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

# ✅ Matches (city AND vehicle_type match)
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

# ✅ Matches (all three match)
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab", hour_of_day = 18 }
surge_factor = 5.0

# ❌ Does NOT match (city is different)
[[overrides]]
_context_ = { city = "Delhi", vehicle_type = "cab" }
per_km_rate = 28.0

# ❌ Does NOT match (hour_of_day is different)
[[overrides]]
_context_ = { hour_of_day = 6 }
surge_factor = 3.0

3. Sort by Priority

Matching contexts are sorted by priority. Higher priority contexts override lower priority ones.

Priority is calculated based on:

  • Number of dimensions - More dimensions = higher priority
  • Dimension positions - Higher positions contribute more weight
info

See Deterministic Resolution for the exact priority formula.

4. Apply Overrides in Order

Overrides are applied from lowest to highest priority. Later overrides replace earlier values:

Default:      per_km_rate = 20.0
Override 1: per_km_rate = 25.0 (vehicle_type = cab)
Override 2: per_km_rate = 22.0 (city = Bangalore, vehicle_type = cab)

Final: per_km_rate = 22.0

Visual Example: Ride-Hailing App

Consider a ride-hailing app with this configuration:

[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }
surge_factor = { value = 0.0, schema = { type = "number" } }

[dimensions]
city = { position = 4, schema = { type = "string", enum = ["Bangalore", "Delhi", "Chennai"] } }
vehicle_type = { position = 2, schema = { type = "string", enum = ["auto", "cab", "bike"] } }
hour_of_day = { position = 3, schema = { type = "integer", minimum = 0, maximum = 23 } }

[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

[[overrides]]
_context_ = { vehicle_type = "bike" }
per_km_rate = 15.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

[[overrides]]
_context_ = { city = "Delhi", vehicle_type = "cab", hour_of_day = 18 }
surge_factor = 5.0

Scenario 1: Bike in Any City

Context: { vehicle_type: "bike" }

OverrideContextMatches?Priority
Default--0
bike{ vehicle_type = "bike" }4
cab{ vehicle_type = "cab" }-
Bangalore cab{ city = "Bangalore", vehicle_type = "cab" }-

Result: per_km_rate = 15.0, surge_factor = 0.0

Scenario 2: Cab in Bangalore

Context: { city: "Bangalore", vehicle_type: "cab" }

OverrideContextMatches?Priority
Default--0
cab{ vehicle_type = "cab" }4
bike{ vehicle_type = "bike" }-
Bangalore cab{ city = "Bangalore", vehicle_type = "cab" }20

Result: per_km_rate = 22.0 (Bangalore cab override wins), surge_factor = 0.0

Scenario 3: Cab in Delhi at 6 PM

Context: { city: "Delhi", vehicle_type: "cab", hour_of_day: 18 }

OverrideContextMatches?Priority
Default--0
cab{ vehicle_type = "cab" }4
bike{ vehicle_type = "bike" }-
Bangalore cab{ city = "Bangalore", vehicle_type = "cab" }-
Delhi evening{ city = "Delhi", vehicle_type = "cab", hour_of_day = 18 }28

Result: per_km_rate = 25.0 (cab override), surge_factor = 5.0 (Delhi evening override)

Benefits of Cascading

1. Reduced Configuration Sprawl

Instead of duplicating configuration for every combination:

# ❌ Without cascading - lots of duplication
[cab_bangalore]
per_km_rate = 22.0
surge_factor = 0.0

[cab_delhi]
per_km_rate = 25.0
surge_factor = 0.0

[cab_chennai]
per_km_rate = 25.0
surge_factor = 0.0

[bike_bangalore]
per_km_rate = 15.0
surge_factor = 0.0

# ... and so on for every combination

You define defaults and only override what changes:

# ✅ With cascading - minimal duplication
[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }
surge_factor = { value = 0.0, schema = { type = "number" } }

[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

[[overrides]]
_context_ = { vehicle_type = "bike" }
per_km_rate = 15.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

2. Reasonable Defaults

Every configuration has a fallback:

# Default applies to any city not explicitly configured
[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }

# Only override for specific cities
[[overrides]]
_context_ = { city = "Bangalore" }
per_km_rate = 22.0

A new city automatically gets sensible defaults.

3. Incremental Specificity

Add specificity only when needed:

# Start with vehicle type override
[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

# Later, add city-specific override for Bangalore
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

# Even later, add time-specific override
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab", hour_of_day = 18 }
surge_factor = 5.0

4. Clear Override Hierarchy

The configuration file clearly shows what overrides what:

# General → Specific hierarchy is visible in the file
[[overrides]]
_context_ = { vehicle_type = "cab" } # Level 1: Vehicle type
per_km_rate = 25.0

[[overrides]]
_context_ = { city = "Bangalore" } # Level 1: City
per_km_rate = 21.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" } # Level 2: Combination
per_km_rate = 22.0

Anti-Patterns to Avoid

1. Overriding Everything

Don't override every combination:

# ❌ Bad: Overriding for every city-vehicle combination
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "auto" }
per_km_rate = 18.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "bike" }
per_km_rate = 15.0

[[overrides]]
_context_ = { city = "Delhi", vehicle_type = "auto" }
per_km_rate = 18.0

# ... many more

Instead, use sensible defaults and override only exceptions:

# ✅ Good: Default per vehicle type, exceptions per city
[[overrides]]
_context_ = { vehicle_type = "auto" }
per_km_rate = 18.0

[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

[[overrides]]
_context_ = { vehicle_type = "bike" }
per_km_rate = 15.0

# Only override where needed
[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0

2. Conflicting Overrides at Same Priority

Avoid overrides that conflict at the same priority level:

# ❌ Bad: Same priority, different values
[[overrides]]
_context_ = { city = "Bangalore" }
per_km_rate = 22.0

[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

# For Bangalore cab: which one wins?
# Both have same priority (one dimension each)

Resolution: Make one more specific:

# ✅ Good: Clear priority
[[overrides]]
_context_ = { vehicle_type = "cab" }
per_km_rate = 25.0

[[overrides]]
_context_ = { city = "Bangalore", vehicle_type = "cab" }
per_km_rate = 22.0 # This wins for Bangalore cabs

3. Missing Defaults

Always define defaults for all configs:

# ❌ Bad: No default for surge_factor
[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }

[[overrides]]
_context_ = { city = "Delhi" }
surge_factor = 5.0 # What about other cities?
# ✅ Good: Default for all configs
[default-configs]
per_km_rate = { value = 20.0, schema = { type = "number" } }
surge_factor = { value = 0.0, schema = { type = "number" } }

[[overrides]]
_context_ = { city = "Delhi" }
surge_factor = 5.0 # Other cities get 0.0

Comparison with Other Approaches

ApproachProsCons
SuperTOML CascadingDeterministic, declarative, no code logicLearning curve for priority rules
Environment FilesSimple, familiarDuplication, no type safety
Code-based LogicFlexibleHard to reason about, scattered logic
Database ConfigurationDynamicHard to version, no type safety
Feature FlagsGood for experimentsNot ideal for static configuration

SuperTOML cascading combines the best of these: declarative like env files, flexible like code, and type-safe like a database schema.