DEV Community

Cover image for Kiponos vs Flagsmith — Product Flags & Remote Config vs Live Ops Trees (Architecture)
Moshe Avdiel
Moshe Avdiel

Posted on • Originally published at github.com

Kiponos vs Flagsmith — Product Flags & Remote Config vs Live Ops Trees (Architecture)

Wednesday 11:08. The mobile squad ships checkout_redesign_v3 through Flagsmith — segment rules by plan_tier, remote config for banner copy, gradual percentage rollout. Same incident bridge, authorization is pegged: processor error rates climb, card-testing velocity spikes, and the JVM owner needs failure_rate_threshold at 32, fraud.block_score at 76, and limits/tenant_mega_corp/rpm raised for a launch partner — now, while pods still serve 12k TPS.

The product engineer suggests:

"Flagsmith has remote config — add failure_rate_threshold as a config value. One SDK, one dashboard."

The payments SRE answers:

"Flagsmith decides which users see which product variant. Our circuit breaker does not have a userId — it is system state every pod must read locally at wire speed. Remote config fetch semantics are not the same contract."

Flagsmith is a strong feature-flag and remote-config platform — SaaS or self-hosted OSS, identity traits, segments, multivariate flags, and remote config for product surfaces. Kiponos.io is a live operational config hub — nested trees, WebSocket deltas, and local get*() reads in Java Spring Boot 3 and Python on the money path. Mature teams keep Flagsmith for product; Kiponos for operational knobs that change during incidents.

The problem — remote config semantics on the authorization hot path

Correct Flagsmith usage for product:

// Product path — identity-bound flag evaluation
FlagsmithClient flagsmith = FlagsmithClient.newBuilder()
        .setEnvironmentKey(System.getenv("FLAGSMITH_ENV_KEY"))
        .build();

Flags flags = flagsmith.getIdentityFlags(customerId, Map.of(
        "plan_tier", planTier,
        "country", countryCode
));

boolean showRedesign = flags.isFeatureEnabled("checkout_redesign_v3");
String bannerCopy = flags.getFeatureValue("checkout_banner_copy", "Pay securely");
Enter fullscreen mode Exit fullscreen mode

The anti-pattern appears when ops keys land in remote config:

// Anti-pattern — system-bound float through identity remote config
Flags systemFlags = flagsmith.getIdentityFlags("system-circuit-breaker", Map.of());
int failureThreshold = Integer.parseInt(
        systemFlags.getFeatureValue("failure_rate_threshold", "45"));

if (rollingFailureRate > failureThreshold) {
    return CircuitDecision.open();
}
Enter fullscreen mode Exit fullscreen mode

Friction compounds:

  • Identity context for system knobs — fake system-* identities pollute the segment model
  • Remote config refresh model — optimized for product surfaces that tolerate seconds of staleness, not per-transaction circuit math at 12k TPS
  • Flat flag namespacesfraud.block_score, resilience.partner.wait_ms, and limits.tenant_x.rpm do not compose as a cross-service ops tree
  • Python fraud workers + Java APIs — duplicate remote config keys or custom sync layers
  • OSS self-host burden — Postgres, Django API, edge proxy justified for product flags; stuffing incident floats there clutters the flag catalog

Flagsmith remote config is legitimate for product-tunable parameters — default shipping option, onboarding step order, UI theme tokens. It is the wrong primitive for incident knobs on saturated authorization paths.

What teams believe vs production reality

Belief Production reality
"Remote config replaces an ops config hub" Built for product parameters per identity, not nested platform trees
"One Flagsmith SDK simplifies the stack" Product flags and fraud floats have different latency and audit models
"Self-hosted Flagsmith is free infrastructure" HA Postgres + upgrades are real ops cost — scope it to product
"Percentage rollouts fit every change" Circuit thresholds need instant global updates, not 15% of users
"We will use Redis for incident keys" Now Flagsmith + Redis + YAML for one platform team

The Aha

Flagsmith decides which identities see which product features and remote-config values. Kiponos decides how hard production runs when processors degrade and fraud spikes. Keep checkout_redesign_v3 and segment rules in Flagsmith. Move failure_rate_threshold, block_score, and tenant rpm to Kiponos — local reads, no per-transaction identity evaluation, no redeploy.

What Kiponos.io is for Flagsmith-heavy product orgs

Kiponos is a real-time configuration hub. Java and Python SDKs connect once via WebSocket to wss://kiponos.io/api/io-kiponos-sdk, hydrate a typed profile tree, and serve getInt(), getDouble(), and getBoolean() from in-process memory. Dashboard edits push single-key deltas — change fraud/thresholds/block_score from 85 to 76; every pod sees it without restart.

Profile path for this comparison:

['payments']['core']['prod']['live']
Enter fullscreen mode Exit fullscreen mode

Product flags and remote config stay in Flagsmith. Operational knobs live in Kiponos under payments_ops/ — the read contract is always local cache lookup, not identity flag evaluation with environment polling.

Architecture — Flagsmith product plane vs Kiponos ops plane

Architecture diagram

Hybrid is the norm: Flagsmith owns identity-bound product config; Kiponos owns system-bound thresholds both runtimes read.

Config tree — ops keys that do not belong in remote config

payments_ops/
  fraud/
    thresholds/
      block_score: 76
      review_score: 63
      velocity_per_hour: 22
      bin_attack_mode: true
  resilience/
    partner/
      failure_rate_threshold: 32
      wait_duration_open_ms: 18000
      half_open_permitted_calls: 7
    inventory/
      failure_rate_threshold: 41
      slow_call_threshold_ms: 3200
  limits/
    default/
      rpm: 1400
      burst: 250
    tenant_mega_corp/
      rpm: 11000
      burst: 1600
  flagsmith_bridge/
    # Document product keys that remain on Flagsmith
    checkout_redesign_v3: flagsmith_owned
    checkout_banner_copy: flagsmith_owned
Enter fullscreen mode Exit fullscreen mode

Java integration — Spring Boot 3 authorization path stays local

@Configuration
public class KiponosConfig {

    @Bean
    public Kiponos kiponos(
            @Value("${kiponos.team-id}") String teamId,
            @Value("${kiponos.access-key}") String accessKey,
            @Value("${kiponos.profile-path}") String profilePath) {
        return Kiponos.builder()
                .teamId(teamId)
                .accessKey(accessKey)
                .profilePath(profilePath)
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Store kiponos.* bootstrap in Kubernetes Secret — same as Flagsmith environment keys. Neither belongs in remote config.

@Service
public class FraudGateService {

    private final Kiponos kiponos;

    public FraudGateService(Kiponos kiponos) {
        this.kiponos = kiponos;
    }

    public Decision evaluate(Transaction txn, int riskScore, int velocityLastHour) {
        var fraud = kiponos.path("payments_ops", "fraud", "thresholds");
        int blockScore = fraud.getInt("block_score");
        int velocityLimit = fraud.getInt("velocity_per_hour");

        if (riskScore >= blockScore) {
            return Decision.block("score");
        }
        if (velocityLastHour > velocityLimit) {
            return Decision.block("velocity");
        }
        return Decision.allow();
    }
}
Enter fullscreen mode Exit fullscreen mode

Product flag — keep Flagsmith on checkout where segments matter:

public boolean renderCheckoutRedesign(String customerId, String planTier) {
    Flags flags = flagsmith.getIdentityFlags(customerId, Map.of("plan_tier", planTier));
    return flags.isFeatureEnabled("checkout_redesign_v3");
    // Do not route fraud.block_score through this SDK
}
Enter fullscreen mode Exit fullscreen mode

React to resilience deltas without per-request overhead:

@Component
public class LiveCircuitBinder {

    private final Kiponos kiponos;
    private final CircuitBreakerRegistry breakers;

    public LiveCircuitBinder(Kiponos kiponos, CircuitBreakerRegistry breakers) {
        this.kiponos = kiponos;
        this.breakers = breakers;
        kiponos.afterValueChanged(this::onChange);
    }

    private void onChange(ValueChange change) {
        if (change.path().startsWith("payments_ops/resilience/partner")) {
            breakers.circuitBreaker("partner").reset();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Python integration — velocity worker shares the ops tree

import os
from kiponos import Kiponos

os.environ["KIPONOS_PROFILE"] = "['payments']['core']['prod']['live']"
kiponos = Kiponos.create_for_current_team()

def velocity_limit() -> int:
    return kiponos.path("payments_ops", "fraud", "thresholds").get_int(
        "velocity_per_hour", 18
    )

def evaluate_card_testing_ring(txn_count_last_hour: int) -> str:
    limit = velocity_limit()
    if txn_count_last_hour > limit:
        return "escalate"
    return "normal"
Enter fullscreen mode Exit fullscreen mode

Java authorization and Python velocity scoring read the same tree — Flagsmith remote config has no clean polyglot ops story for both.

Real scenarios

Event Flagsmith alone Flagsmith + Kiponos
Roll out checkout_redesign_v3 to premium segment Native segments + remote config Keep Flagsmith; unchanged
Processor brownout — tighten circuit Remote config hack + fake system identity resilience/partner/failure_rate_threshold live
BIN attack — lower block score Wrong tool / awkward config value fraud/thresholds/block_score in seconds
Launch partner — raise tenant RPM Not identity-scoped limits/tenant_mega_corp/rpm immediate
Python velocity worker must match Java Duplicate remote config keys One profile path, two SDKs
OSS self-hosted flag uptime Flagsmith strength Scope Flagsmith to product; ops in Kiponos

Performance — hot path economics on authorization

  • Flagsmith identity evaluation — trait resolution, segment matching — right for product paths at human scale
  • Flagsmith remote config on auth — environment/identity fetch semantics; not designed for 12k bare floats/sec
  • Kiponos getInt() — in-memory tree lookup; no network on read path
  • Delta updates — incident changes one key; no full remote-config document refresh per pod
  • One WebSocket per JVM/worker — background sync; hot path never blocks on Flagsmith API RTT
  • Polyglot parity — Java Spring Boot 3 and Python workers share one profile; Flagsmith SDK coverage varies by service role

Honest comparison table

Criterion Flagsmith Kiponos Honest verdict
Identity-segment feature flags Excellent App-side bucketing possible Flagsmith wins product flags
Remote config for product UX Core strength Ops trees, not copy variants Flagsmith for UI parameters
Multivariate / percentage rollouts Native Global ops change, not per-user % Flagsmith for gradual product
Numeric incident knobs (fraud, circuits) Awkward fit First-class Kiponos on money path
Nested cross-service ops trees Flat flag namespaces Hierarchical paths Kiponos for platform ops
Hot-path read at 12k TPS Evaluation + cache model Local SDK cache Kiponos on authorization
Java + Python same hub Partial / role-dependent Both SDKs Kiponos for polyglot ops
Self-hosted OSS option Strong Managed hub or private deploy Flagsmith for OSS product flags
Pricing model MAU / request oriented Team/hub pricing Model product vs ops split

When not to use Kiponos

Use case Better tool
Per-identity feature flags with segment rules Flagsmith
Remote config for onboarding copy and UI tokens Flagsmith
Gradual percentage rollout to user cohorts Flagsmith
Bootstrap secrets and API keys Vault / cloud secret manager
Infrastructure desired state GitOps / Terraform

Getting started (15 minutes) — split product from ops

  1. Inventory every live key: mark product flag / remote config vs operational knob (fraud, circuit, limit).
  2. TeamPro at kiponos.io — profile ['payments']['core']['prod']['live'].
  3. Migrate three ops keys off Flagsmith remote config: block_score, one failure_rate_threshold, one tenant rpm.
  4. Wire Java FraudGateService and Python velocity worker to the same profile.
  5. Document RFC: "Flagsmith owns identity-bound product flags and remote config; Kiponos owns ops floats on hot paths."

Further reading


Kiponos.io — Flagsmith for which users see the product variant. Live hub for how hard production runs during the incident.

Top comments (0)