--- title: "Architecting Low-Latency Decision Engines: One-Way vs. Two-Way Door Metrics in Software Architecture" description: "How to design low-latency software decision engines using Feature Flags, State Machines, and Bezos' reversible decision framework in TypeScript." tags: ["architecture", "typescript", "systemdesign", "devops"] canonical_url: "https://entscheidomat.com/ratgeber/entscheidung-treffen-wenn-zwei-optionen-gleich-gut-sind" target_keywords: ["entscheidungsfinder", "entscheidungsgenerator", "entweder oder generator", "feature flag architecture", "state machine typescript"] --- # Architecting Low-Latency Decision Engines: One-Way vs. Two-Way Door Metrics in Software Architecture In high-throughput distributed systems, decision-making happens millions of times per second. Whether an application is routing API traffic between microservices, executing A/B testing variations, evaluating user entitlements, or powering a lightweight decision tool like an [Entscheidungsgenerator](https://entscheidomat.com/entweder-oder), how decision engines are architected directly impacts **latency, system resilience, and deployment velocity**. Amazon founder Jeff Bezos famously divided all decisions into two distinct categories in his 1997 Shareholder Letter: * **Type 1 Decisions (One-Way Doors):** Irreversible, high-consequence architectural bets (e.g. primary database migration, multi-region cloud strategy). * **Type 2 Decisions (Two-Way Doors):** Reversible, low-risk operational choices (e.g. UI micro-interactions, algorithm tweaks, feature toggles). In this article, we will translate this decision framework into concrete software architecture patterns. We will build a production-grade, zero-dependency **Type 2 Decision Engine with Feature Flag Rollouts and Deterministic Finite State Machines (FSM)** in TypeScript. --- ## 1. Architectural Taxonomy: Type 1 vs. Type 2 Decisions in Code Software architecture mistakes occur when engineering organizations treat Type 2 decisions with Type 1 rigor (paralyzing release cycles) or treat Type 1 decisions as Type 2 shortcuts (causing catastrophic outage vulnerabilities). ```text ┌────────────────────────┐ │ Software Decision Flow │ └───────────┬────────────┘ │ Is the change easily reversible? │ ┌──────────────┴──────────────┐ ▼ ▼ YES (Type 2 / Two-Way) NO (Type 1 / One-Way) ┌───────────────────────────┐ ┌──────────────────────────┐ │ • Feature Toggles │ │ • DB Schema Migrations │ │ • Dynamic API Routing │ │ • Monolith to Serverless │ │ • Progressive Rollouts │ │ • Protocol Format Shifts │ └───────────────────────────┘ └──────────────────────────┘ ``` | Decision Metric | Type 1 (One-Way Door) | Type 2 (Two-Way Door) | | :--- | :--- | :--- | | **Reversibility Cost** | Extremely High ($O(\text{Weeks/Months})$) | Near Zero ($O(\text{Milliseconds})$) | | **Evaluation Speed Target** | Weeks (RFCs, Architecture Reviews) | $< 1 \text{ms}$ (Runtime Evaluator) | | **Verification Strategy** | Formal proofs, load testing | Feature flags, canary rollouts, A/B metrics | | **Code Implementation** | Immutable schema contracts | Dynamic state machine / Config evaluation | --- ## 2. Designing a Sub-Millisecond Type 2 Decision Engine Let's build a sub-millisecond, zero-dependency **Type 2 Feature Flag & Routing Engine** in TypeScript. This engine allows teams to make instant, reversible runtime decisions without re-deploying code. ```typescript export interface UserContext { id: string; email: string; country: string; isBetaTester: boolean; } export interface FeatureFlagRule { id: string; enabled: boolean; rolloutPercentage: number; // 0 to 100 allowedCountries?: string[]; requiresBeta?: boolean; } export class DecisionEngine { private rules: Map = new Map(); public registerRule(rule: FeatureFlagRule): void { this.rules.set(rule.id, rule); } /** * Deterministic hash calculation (MurmurHash3 variant) to ensure * a user consistently receives the same feature bucket. */ private hashUser(userId: string, flagId: string): number { const key = `${userId}:${flagId}`; let hash = 0x811c9dc5; // FNV-1a offset basis for (let i = 0; i < key.length; i++) { hash ^= key.charCodeAt(i); hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); } return (hash >>> 0) % 100; // Returns consistent integer 0..99 } /** * Evaluates a decision rule in under 0.1 milliseconds. */ public evaluate(flagId: string, context: UserContext): boolean { const rule = this.rules.get(flagId); if (!rule || !rule.enabled) return false; // Rule 1: Beta Check if (rule.requiresBeta && !context.isBetaTester) return false; // Rule 2: Geo-location Filter if (rule.allowedCountries && !rule.allowedCountries.includes(context.country)) { return false; } // Rule 3: Deterministic Percentage Rollout const userBucket = this.hashUser(context.id, flagId); return userBucket < rule.rolloutPercentage; } } ``` ### Performance & Determinism Test ```typescript const engine = new DecisionEngine(); engine.registerRule({ id: "new_checkout_flow", enabled: true, rolloutPercentage: 25, // 25% rollout allowedCountries: ["DE", "AT", "CH"] }); const user: UserContext = { id: "usr_94821", email: "dev@example.com", country: "DE", isBetaTester: false }; const startTime = performance.now(); const isEnabled = engine.evaluate("new_checkout_flow", user); const duration = performance.now() - startTime; console.log(`Decision Evaluation Result: ${isEnabled} (Executed in ${duration.toFixed(4)} ms)`); ``` --- ## 3. Reversible Decision Workflows via Finite State Machines (FSM) For complex multi-step application states (such as checkout flows, loan applications, or interactive decision tools like [Entscheidomat Entweder-Oder](https://entscheidomat.com/entweder-oder)), representing state transitions as a **Finite State Machine** ensures strict type safety and zero invalid transitions. ```typescript export type DecisionState = "IDLE" | "EVALUATING" | "RESOLVED" | "CANCELLED"; export type DecisionEvent = "SUBMIT" | "APPROVE" | "REJECT" | "RESET"; export class ReversibleDecisionFSM { private currentState: DecisionState = "IDLE"; private history: DecisionState[] = []; private readonly transitions: Record>> = { IDLE: { SUBMIT: "EVALUATING" }, EVALUATING: { APPROVE: "RESOLVED", REJECT: "CANCELLED", RESET: "IDLE" }, RESOLVED: { RESET: "IDLE" }, CANCELLED: { RESET: "IDLE" } }; public transition(event: DecisionEvent): DecisionState { const allowedNextState = this.transitions[this.currentState][event]; if (!allowedNextState) { throw new Error(`Invalid FSM transition: Cannot trigger '${event}' from state '${this.currentState}'`); } this.history.push(this.currentState); this.currentState = allowedNextState; return this.currentState; } /** * Reverts to previous state (Two-Way Door behavior) */ public rollback(): DecisionState { const previous = this.history.pop(); if (!previous) { throw new Error("No previous state to rollback to"); } this.currentState = previous; return this.currentState; } public getState(): DecisionState { return this.currentState; } } ``` --- ## 4. Benchmark & Architectural Checklist When designing modern web applications and decision utilities: | Principle | Technical Implementation | | :--- | :--- | | **Sub-Millisecond Evaluation** | Use in-memory FNV-1a hashing instead of remote DB network calls on every request. | | **Zero Deployment Rollbacks** | Wrap all Type 2 changes in feature flag evaluation blocks. | | **Reversibility (Two-Way)** | Implement FSM history stacks to allow single-click state rollbacks. | | **Stateless Scalability** | Derive user buckets deterministically using `hash(userId + flagId) % 100`. | --- ## Conclusion & Practical Takeaway 1. Software teams must explicitly tag changes as **Type 1 (One-Way)** or **Type 2 (Two-Way)** in RFCs and PR reviews. 2. Type 2 decisions should never require code deployments; evaluate them using in-memory feature flags and state machines. 3. For interactive consumer tools, use clean TypeScript state engines to keep execution fast, predictable, and reversible. Test an interactive decision engine live on [Entscheidomat Entweder-Oder](https://entscheidomat.com/entweder-oder). --- ## FAQ (Schema Structured Data) ```json { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What is Jeff Bezos' One-Way vs. Two-Way Door decision framework?", "acceptedAnswer": { "@type": "Answer", "text": "One-Way Door (Type 1) decisions are irreversible and high-stakes, requiring slow evaluation. Two-Way Door (Type 2) decisions are easily reversible and should be executed rapidly using feature flags and runtime evaluators." } }, { "@type": "Question", "name": "How fast should a feature flag decision engine evaluate?", "acceptedAnswer": { "@type": "Answer", "text": "A well-architected in-memory decision engine using deterministic hashing should evaluate in under 0.1 milliseconds per request without hitting external network databases." } } ] } ```