Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Built-in services

Built-in services

Feature flags

On this page

A feature flag lets you ship code switched off, then turn it on for some users, a share of users, or everyone, without a deploy. You declare every flag once, in the file flags.jadpo at the root of the project, with its type, its default, its owner and the date it expires. The /// comment above a flag is its description, as for every declaration.

example
// flags.jadpoflags {    /// The one-page checkout replacing the three-step form.    new_checkout: Bool {        default: false        owner: "payments"        expires: "2027-01-31"    }    /// Which layout the checkout uses while we compare two.    checkout_layout: CheckoutLayout {        default: CheckoutLayout.classic        owner: "payments"        expires: "2026-12-15"    }}

A flag is a Bool, or an enum when it has more than two variants. Code reads it as flags.new_checkout, in actions and in a route's run: arguments. Functions and queries can't read a flag, so you pass them its value as a parameter. As with clock.now, the value is fixed for the whole operation, so one request never sees a flag change halfway through.

example
// domains/ordering/actions/place_order.jadpoaction place_order(user_id: User.id, input: PlaceOrder) -> OrderView {    if (flags.new_checkout) {        return place_order_in_one_step(user_id, input)    }    ...}

Changing a flag

A flag's current state lives in your app's own database, in a table Jadpo owns. You change it while the app runs, with jadpo flags against an environment (Commands for services, calls, email and stored data). Each change is logged with who made it.

Each server holds the flag values in its own memory and refreshes them every few seconds, so a change applies within ten seconds. Flags are never held in the cache. If the database can't be read, each server keeps the last values it had, or the defaults, so reading a flag never fails a request.

Flags aren't a capability, and no outside flag service fills them.

text
jadpo flags set new_checkout --env production --users 0192... --percent 10jadpo flags set checkout_layout --env production --split classic=50,compact=50jadpo flags list --env production

Who gets which value

The first rule that matches wins:

  1. Named users get a fixed value.
  2. Named tenants get a fixed value. This rule comes once tenancy is settled for flags (Tenancy).
  3. A percentage of users, chosen by a stable hash of the flag's name and the user's id. The same user always gets the same answer, raising 10% to 20% keeps the first 10% in, and each flag picks a different 10%.
  4. Everyone else gets the flag's current value, at first its default.

The runtime knows the caller, as it does for policy, so you don't pass it. Schedules and subscribers have no caller (Background work), so they get the everyone value. An action that needs a user's variant later puts the value in the event it emits.

What the compiler checks

  • flags.new_chekout is a compile error, like any unknown name.
  • Every flag read needs a test for each value. A test chooses the value with flags: { new_checkout: true }, beside clock: and config: (Test settings). jadpo check fails until the tests that reach each read cover both true and false, or every variant.
  • A flag past its expires: date is an error naming its owner, and you can't decline it. jadpo check judges the date against today, and jadpo build against the commit's date, so rebuilding a merged revision is never blocked by a date passing. To keep a flag longer you edit the date, so keeping one is a reviewed choice.
  • Whether a flag has sat at 100% or 0% is runtime state the compiler can't see, so jadpo flags list marks those flags "ready to remove".

Removing a flag

jadpo flags remove new_checkout --keep true rewrites your source: each read becomes true, the branch that can no longer run goes, the declaration goes, and test overrides for the removed value are deleted. Jadpo can do this exactly because it knows every read.

Flags in browsers and apps

A flag with client: true is also returned, worked out for the caller, by a generated GET /flags route in the OpenAPI document.

What flags are not for

Flags are for releasing code. What a customer's plan includes is your app's data, guarded by policy. A setting that differs between environments is configuration (Settings: config.jadpo).

Markdown for agents: /docs/feature-flags.md.Built from Jadpo main at a707efb.