Skip to main content

Rule DSL Reference

This page documents the core rule-building DSL available in konditional-core.

Rule basics

object AppFeatures : Namespace("app") {
val darkMode by boolean<Context>(default = false) {
rule(true) { platforms(Platform.IOS) }
rule(true) { locales(AppLocale.UNITED_STATES) }
}
}

Boolean sugar

val darkMode by boolean<Context>(default = false) {
enable { ios() }
disable { android() }
}

Targeting primitives

Inside a rule block (RuleScope):

  • locales(...) targets locale ids
  • platforms(...) targets platform ids
  • versions { min(...); max(...) } targets version ranges
  • axis(...) targets custom axes
  • extension { ... } custom predicate
  • rampUp { ... } percentage rollout
  • allowlist(...) stable IDs that bypass ramp-up
  • note("...") attaches a human-readable note
  • always() / matchAll() mark a catch-all rule explicitly

Example

val checkout by string<Context>(default = "v1") {
rule("v2") {
platforms(Platform.IOS)
versions { min(3, 0, 0) }
rampUp { 25.0 }
note("iOS v2 rollout")
}
}

Custom predicates

extension { ... } receives the Context type for the feature.

data class EnterpriseContext(
override val locale: AppLocale,
override val platform: Platform,
override val appVersion: Version,
override val stableId: StableId,
val subscriptionTier: Tier,
) : Context

val enterpriseOnly by boolean<EnterpriseContext>(default = false) {
rule(true) { extension { subscriptionTier == Tier.ENTERPRISE } }
}
  • Guarantee: Custom predicates participate in specificity ordering.

  • Mechanism: Predicate implementations report their own specificity() which is added to the rule total.

  • Boundary: Konditional does not validate predicate correctness or determinism.

Ramp-up allowlists

allowlist(...) bypasses the ramp-up check after the rule matches by criteria.

  • Boundary: It does not override rule criteria, isActive, or the namespace kill-switch.

Next steps