<?xml version="1.0" encoding="UTF-8" ?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
    <title><![CDATA[Chahid Arid | FinTech &amp; AI Engineering — English]]></title>
    <description><![CDATA[FinTech, AI, payments, and software architecture—practical essays with original diagrams.]]></description>
    <link>https://chahidarid.online/en/</link>
    <atom:link href="https://chahidarid.online/rss/" rel="self" type="application/rss+xml" />
    <language>en</language>
    <lastBuildDate>Sun, 02 Aug 2026 01:17:38 +0400</lastBuildDate>
    <ttl>60</ttl>
        <item>
            <title><![CDATA[Loop Engineering for Governed Agent Systems]]></title>
            <description><![CDATA[Loop engineering designs the recurring system around an AI agent: triggers, evidence, tools, verification, budgets, stop conditions, and accountable authority.]]></description>
            <link>https://chahidarid.online/en/loop-engineering-governed-agent-systems/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/loop-engineering-governed-agent-systems/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Thu, 23 Jul 2026 23:36:35 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/loop-engineering-governed-agent-systems.png?v&#x3D;20260724-v2" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>Loop engineering is the emerging practice of designing the system that repeatedly prompts, equips, checks, and stops an AI agent. The important shift is not from one clever prompt to a longer prompt. It is from supervising every turn manually to engineering a controlled execution loop.</p><p>I find the term useful because it moves the architecture discussion to the right level. The question is no longer, “Can the model complete this task?” It becomes:</p><blockquote>What evidence must one iteration produce before the system is allowed to continue, stop, escalate, or create a real-world effect?</blockquote><p>That is the difference between an agent that keeps trying and an operating system for agent work.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/loop-engineering-governed-agent-systems.png?v=20260724-v2" class="kg-image" alt="A governed loop engineering lifecycle in which a signal becomes an evidence bundle, a proposed action, independent verification, an authority gate, committed state, and versioned learning, with a safe stop path" loading="lazy" width="1600" height="975"><figcaption>Figure 36. The inner loop observes, proposes, and verifies. The outer loop owns authority, budgets, terminal states, and the consequences of acting.</figcaption></figure><h2 id="loop-engineering-is-one-layer-above-the-agent-run">Loop engineering is one layer above the agent run</h2><p><a href="https://www.ibm.com/think/topics/loop-engineering?ref=chahidarid.online">IBM’s definition of loop engineering</a> describes an iterative workflow organized around a goal, an action, an observation, and an adjustment. <a href="https://openai.com/index/unrolling-the-codex-agent-loop/?ref=chahidarid.online">OpenAI’s description of the Codex agent loop</a> shows the lower-level mechanism: a harness orchestrates the user, the model, and the tools the model invokes.</p><p>Those views are compatible, but they describe different design scopes.</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Layer</th>
<th>Design question</th>
<th>Typical artifact</th>
<th>Failure when missing</th>
</tr>
</thead>
<tbody>
<tr>
<td>Prompt engineering</td>
<td>What should the model do now?</td>
<td>Instruction and examples</td>
<td>Ambiguous or inconsistent response</td>
</tr>
<tr>
<td>Context engineering</td>
<td>What should the model know now?</td>
<td>Retrieved facts, files, summaries</td>
<td>Irrelevant, stale, or overwhelming context</td>
</tr>
<tr>
<td>Harness engineering</td>
<td>What can one agent run access and change?</td>
<td>Tools, permissions, sandbox, recovery</td>
<td>Unsafe or ineffective execution</td>
</tr>
<tr>
<td>Loop engineering</td>
<td>What should trigger the next run, prove progress, and stop recurrence?</td>
<td>State machine, evaluator, budget, memory, human gate</td>
<td>Endless motion, false completion, or uncontrolled effects</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>Loop engineering does not replace the other three. A loop simply compounds their quality. A poor prompt, polluted context, or overpowered harness becomes more dangerous when it can run repeatedly.</p><h2 id="the-smallest-useful-loop-has-a-contract">The smallest useful loop has a contract</h2><p>I would not start with multiple agents or a sophisticated orchestrator. I would begin with a contract that can be inspected without reading model reasoning.</p><pre><code class="language-yaml">goal:
  outcome: classify one reconciliation exception
  done_when: evidence bundle passes deterministic checks

scope:
  read: [ledger, processor_report, settlement_report]
  write: [case_note, proposed_correction]
  forbidden: [post_ledger_entry, move_money, close_case]

limits:
  iterations: 4
  wall_time: 8m
  model_cost: 1.50_USD

evidence:
  required:
    - source_completeness
    - amount_and_currency_match
    - identifier_lineage
    - verifier_result

terminal_states:
  - proposed
  - no_action
  - needs_human
  - control_failed
</code></pre><p>This is not a universal syntax. It is a design checklist expressed as configuration. A credible loop needs six things.</p><h3 id="1-a-recursive-goal-with-a-verifiable-end">1. A recursive goal with a verifiable end</h3><p>“Investigate the exception” is an activity. “Produce a complete evidence bundle, a classified cause, and a proposal that passes these four checks” is an outcome.</p><p>The goal must be re-evaluated on every iteration. Otherwise the agent can optimize for whatever intermediate signal is easiest: more notes, more tool calls, a plausible explanation, or a green evaluator score that does not represent the business result.</p><h3 id="2-a-bounded-trigger">2. A bounded trigger</h3><p>The loop needs a reason to start: an event, schedule, queue item, failed control, or human request. The trigger should carry an identity, priority, owner, and deduplication key.</p><p>Without an idempotent trigger, one real-world issue can create several active loops. That is inconvenient for code maintenance and dangerous for financial operations.</p><h3 id="3-prepared-context-not-accumulated-conversation">3. Prepared context, not accumulated conversation</h3><p>Each cycle should receive a deliberately assembled evidence bundle: current records, relevant policy, tool results, prior decisions, and a compact summary of earlier attempts.</p><p>Appending the full history is not memory design. It is context inflation. The loop should preserve durable facts and decisions while discarding conversational residue.</p><h3 id="4-tools-with-authority-proportional-to-the-task">4. Tools with authority proportional to the task</h3><p><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/?ref=chahidarid.online">OpenAI’s practical guide to agents</a> recommends assessing tool risk based on factors such as whether access is read-only or write-enabled, reversibility, permissions, and financial impact. That is especially important inside a loop because tool authority is exercised repeatedly.</p><p>I use three tool classes:</p><ul><li><strong>Observe:</strong> query records, retrieve policy, inspect telemetry.</li><li><strong>Prepare:</strong> create a draft, proposal, test fixture, or case note.</li><li><strong>Effect:</strong> modify a customer state, deploy software, post a ledger entry, or move money.</li></ul><p>An observe tool may be automatic. A prepare tool usually needs validation. An effect tool needs explicit authorization based on impact and reversibility.</p><p>If tools are exposed through MCP, transport authorization is only part of the design. The <a href="https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization?ref=chahidarid.online">MCP authorization specification</a> defines protections such as OAuth-based authorization, secure token handling, HTTPS, and scopes. The business layer must still decide whether this agent, for this case, at this point in the loop, may invoke the tool.</p><h3 id="5-evidence-that-is-stronger-than-model-confidence">5. Evidence that is stronger than model confidence</h3><p>A model saying “the task is complete” is a claim. The loop needs evidence that another component can inspect.</p><p>Useful evidence includes:</p><ul><li>test results bound to the current source version;</li><li>schema and invariant checks;</li><li>before-and-after snapshots;</li><li>signed or immutable tool receipts;</li><li>independent record counts and control totals;</li><li>deterministic policy outcomes;</li><li>a model-based critique with a stable rubric;</li><li>human approval for high-impact exceptions.</li></ul><p><a href="https://www.anthropic.com/engineering/building-effective-agents?ref=chahidarid.online">Anthropic’s evaluator-optimizer pattern</a> is valuable when success criteria are clear enough for iterative feedback to improve the result. But a second model is not automatically an independent control. Two models can share missing context, incentives, or blind spots. The evaluator should add evidence, not ceremonial agreement.</p><h3 id="6-terminal-states-that-include-failure">6. Terminal states that include failure</h3><p>A loop without a negative terminal state is an infinite retry policy.</p><p>At minimum, I want:</p><ul><li><strong>done:</strong> the requested outcome is proven;</li><li><strong>no action:</strong> the evidence shows that a change is unnecessary;</li><li><strong>needs human:</strong> ambiguity or impact exceeds delegated authority;</li><li><strong>control failed:</strong> a required source, check, or safeguard is unavailable;</li><li><strong>budget exhausted:</strong> the loop reached its time, iteration, or cost limit.</li></ul><p>Stopping safely is a product feature. It prevents an uncertain agent from converting uncertainty into repeated action.</p><h2 id="the-maker-checker-pattern-needs-three-kinds-of-verification">The maker-checker pattern needs three kinds of verification</h2><p>Loop engineering discussions often reduce verification to a maker agent and a checker agent. I prefer a three-part model.</p><h3 id="deterministic-verification">Deterministic verification</h3><p>Use code for properties that code can decide: type checks, balances, thresholds, allowed transitions, completeness, signatures, uniqueness, and policy rules.</p><p>Deterministic checks are cheap, repeatable, and explainable. They should not be delegated to a model merely because a model is already present.</p><h3 id="model-based-evaluation">Model-based evaluation</h3><p>Use a model when the property requires interpretation: whether an explanation is supported by the evidence, whether a proposed plan addresses the stated goal, or whether the output omits a material risk.</p><p><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents?ref=chahidarid.online">Anthropic’s guidance on agent evals</a> emphasizes that multi-turn agents must be evaluated across tool use, state changes, and intermediate results—not only the final answer. I would therefore score both the trajectory and the terminal artifact.</p><h3 id="accountable-authority">Accountable authority</h3><p>Use a named human or formally delegated policy for the consequences the system is allowed to create.</p><p>The <a href="https://airc.nist.gov/airmf-resources/airmf/5-sec-core/?ref=chahidarid.online">NIST AI RMF Core</a> calls for documented scope, responsibilities, human oversight, testing, independent assessment, and production monitoring. In practice, that means the architecture should show who owns the outer loop: risk tolerance, tool authority, exception policy, evaluation changes, and the decision to expand autonomy.</p><h2 id="a-worked-use-case-a-reconciliation-exception-loop">A worked use case: a reconciliation exception loop</h2><p>Consider a synthetic payment operation with three independently produced records:</p><ol><li>an internal ledger entry;</li><li>a processor or scheme report;</li><li>a settlement or bank statement.</li></ol><p>The reconciliation engine finds a mismatch. A traditional workflow puts the case into an analyst queue. A poorly designed agent workflow gives an AI assistant broad access and asks it to “resolve the exception.”</p><p>A loop-engineered version is narrower.</p><h3 id="iteration-1-assemble-facts">Iteration 1: assemble facts</h3><p>The loop retrieves the three records, verifies source completeness, normalizes identifiers, and builds control totals. It cannot update any financial record.</p><p>If a source is missing, the terminal state is <code>control_failed</code>. The agent does not infer settlement from a success message.</p><h3 id="iteration-2-classify-and-propose">Iteration 2: classify and propose</h3><p>The maker classifies the mismatch:</p><ul><li>timing difference;</li><li>duplicate report;</li><li>identifier mapping failure;</li><li>fee or foreign-exchange variance;</li><li>missing ledger posting;</li><li>unknown.</li></ul><p>It then proposes either no action, delayed review, a data correction, or a compensating ledger entry. The proposal includes the expected effect and the records that support it.</p><h3 id="iteration-3-verify-independently">Iteration 3: verify independently</h3><p>Deterministic controls recompute totals, validate currency and amount, check that referenced records exist, and confirm that the proposed transition is permitted.</p><p>A separate evaluator checks whether the narrative is supported by the evidence and whether a plausible alternative cause was ignored. The evaluator sees the evidence bundle and rubric, not the maker’s hidden reasoning.</p><h3 id="outer-loop-decision-authorize-or-stop">Outer-loop decision: authorize or stop</h3><p>No model may post a ledger entry. Low-risk data enrichment can be approved by policy if it is reversible. A financial correction requires the authorized operating role.</p><p>After approval, the system creates a new compensating entry; it never edits financial history. Reconciliation then runs again against the new state. Only that fresh result can close the case.</p><p>This is where the FinTech example clarifies the broader concept: the inner loop can accelerate investigation, but the outer loop owns financial truth.</p><p>The <a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Basel Committee’s operational-resilience principles</a> emphasize the ability to respond, recover, and learn while sustaining critical operations. Loop engineering should reinforce that discipline. It should not create a fast but opaque bypass around it.</p><h2 id="observe-the-loop-as-a-production-system">Observe the loop as a production system</h2><p>An agent trace is not only a transcript. The system needs correlated telemetry for:</p><ul><li>trigger and case identity;</li><li>context and policy versions;</li><li>model, prompt, and evaluator versions;</li><li>tool calls and authorization results;</li><li>iteration count, latency, and cost;</li><li>evidence checks and failures;</li><li>human decisions and reason codes;</li><li>terminal state and later business outcome.</li></ul><p><a href="https://opentelemetry.io/docs/concepts/signals/?ref=chahidarid.online">OpenTelemetry’s signal model</a> provides a useful implementation vocabulary: traces show the path, metrics show aggregate behavior, logs record events, and baggage carries controlled correlation context.</p><p>The metrics I would review are not “tokens used” alone.</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Measure</th>
<th>What it reveals</th>
<th>Dangerous interpretation</th>
</tr>
</thead>
<tbody>
<tr>
<td>Proven completion rate</td>
<td>Outcomes with all required evidence</td>
<td>Treating model-declared completion as proof</td>
</tr>
<tr>
<td>Escalation precision</td>
<td>Whether escalations identify genuine ambiguity or risk</td>
<td>Driving escalation toward zero</td>
</tr>
<tr>
<td>Repeat-exception rate</td>
<td>Whether the loop repairs causes or only clears cases</td>
<td>Counting closed tickets as recovery</td>
</tr>
<tr>
<td>Evidence freshness</td>
<td>Whether decisions use current records and policies</td>
<td>Reusing a previously green result</td>
</tr>
<tr>
<td>Cost per proven outcome</td>
<td>Economic efficiency of useful work</td>
<td>Optimizing token cost before quality</td>
</tr>
<tr>
<td>Human override rate</td>
<td>Misalignment between loop verdict and accountable judgment</td>
<td>Blaming reviewers for “slowing automation”</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>Every repeated stop is also design information. If the loop repeatedly lacks one identifier, that is a data-contract problem. If reviewers repeatedly reject the same proposal class, that is an evaluation or authority problem. If the same exception returns after correction, the loop is treating symptoms rather than causes.</p><h2 id="where-loop-engineering-is-the-wrong-answer">Where loop engineering is the wrong answer</h2><p>The term is new; the temptation to apply it everywhere will be strong.</p><p>Use deterministic automation when:</p><ul><li>the states and transitions are known;</li><li>the inputs are structured;</li><li>the decision is already encoded reliably;</li><li>an agent would only imitate a rules engine;</li><li>uncertainty adds no business value.</li></ul><p>Use a human-led workflow when:</p><ul><li>the goal itself is disputed;</li><li>the evidence cannot be made available safely;</li><li>accountability cannot be delegated;</li><li>the action is irreversible and rare;</li><li>learning from the decision matters more than throughput.</li></ul><p>Use an agent loop when the work combines interpretation, tool use, multiple steps, recoverable actions, and verifiable intermediate progress.</p><p>The test is not whether the task is difficult. It is whether the task can be <strong>bounded and proven</strong>.</p><h2 id="a-practical-adoption-sequence">A practical adoption sequence</h2><h3 id="stage-1-%E2%80%94-shadow">Stage 1 — Shadow</h3><p>Run the loop against historical or duplicated cases. Give it read-only tools. Compare its classifications and evidence bundles with known outcomes.</p><h3 id="stage-2-%E2%80%94-recommend">Stage 2 — Recommend</h3><p>Let the loop prepare proposals for live work while humans make every consequential decision. Measure missing evidence, overrides, failure modes, cost, and latency.</p><h3 id="stage-3-%E2%80%94-delegate-reversible-actions">Stage 3 — Delegate reversible actions</h3><p>Allow narrowly scoped, reversible updates when deterministic checks pass. Keep high-impact actions behind human authority.</p><h3 id="stage-4-%E2%80%94-expand-by-evidence-class">Stage 4 — Expand by evidence class</h3><p>Increase autonomy only for the classes of work that have stable inputs, clear controls, acceptable error rates, and reliable recovery—not because the average score improved.</p><p>At every stage, version the loop itself: goal, context assembly, tool contracts, evaluator rubric, budgets, and stop conditions. Otherwise a changed prompt or tool silently changes the operating control.</p><h2 id="my-design-rule">My design rule</h2><p>Do not optimize a loop for the number of iterations it can complete without a human. Optimize it for the quality of evidence it produces before the system changes state.</p><p>A good loop has the confidence to continue when progress is proven, the discipline to stop when controls fail, and an accountable owner for the consequences it creates.</p><p>That is loop engineering: not endless autonomy, but engineered recurrence.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-observability/">Observability for AI: From Tokens to Business Outcomes</a></li><li><a href="https://chahidarid.online/en/responsible-ai-governance/">Responsible AI Governance as an Engineering System</a></li><li><a href="https://chahidarid.online/en/reconciliation-by-design/">Reconciliation by Design, Not by Spreadsheet</a></li><li><a href="https://chahidarid.online/en/build-mcp-server-for-your-api/">How to Build an MCP Server for Your API</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.ibm.com/think/topics/loop-engineering?ref=chahidarid.online">IBM — What Is Loop Engineering?</a></li><li><a href="https://openai.com/index/unrolling-the-codex-agent-loop/?ref=chahidarid.online">OpenAI — Unrolling the Codex Agent Loop</a></li><li><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/?ref=chahidarid.online">OpenAI — A Practical Guide to Building AI Agents</a></li><li><a href="https://www.anthropic.com/engineering/building-effective-agents?ref=chahidarid.online">Anthropic — Building Effective Agents</a></li><li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents?ref=chahidarid.online">Anthropic — Demystifying Evals for AI Agents</a></li><li><a href="https://airc.nist.gov/airmf-resources/airmf/5-sec-core/?ref=chahidarid.online">NIST — AI Risk Management Framework Core</a></li><li><a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Basel Committee — Principles for Operational Resilience</a></li><li><a href="https://opentelemetry.io/docs/concepts/signals/?ref=chahidarid.online">OpenTelemetry — Signals</a></li><li><a href="https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization?ref=chahidarid.online">Model Context Protocol — Authorization</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Good Teams Still Produce Messy Architecture]]></title>
            <description><![CDATA[Why capable teams still create fragmented systems—and how ownership, incentives, executable standards, and system feedback restore coherence.]]></description>
            <link>https://chahidarid.online/en/why-good-teams-produce-messy-architecture/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/why-good-teams-produce-messy-architecture/</guid>
            <category><![CDATA[Architecture]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Thu, 23 Jul 2026 13:16:24 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/why-good-teams-produce-messy-architecture.png?v&#x3D;20260723-v1" medium="image" />
            <content:encoded><![CDATA[<p>Architecture</p><p>Good engineers can produce a difficult system without making an obviously bad decision. Each team responds rationally to its own deadline, service boundary, budget, and incident queue. The architecture becomes messy when those local decisions accumulate without a mechanism that protects the end-to-end system.</p><p>That is why I do not begin an architecture review by asking, “Who designed this?” I begin with a different question: <strong>What environment made this design the easiest reasonable choice?</strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/why-good-teams-produce-messy-architecture.png?v=20260723-v1" class="kg-image" alt="Five environmental forces that cause good teams to produce messy architecture: local goals, delivery pressure, ownership gaps, unusable standards, and missing system feedback" loading="lazy" width="1600" height="975"><figcaption>Figure 35. Messy architecture is rarely one reckless decision. It is the compound result of locally sensible choices made without shared ownership, executable standards, or system-level feedback.</figcaption></figure><h2 id="architecture-is-shaped-by-the-environment-around-the-team">Architecture is shaped by the environment around the team</h2><p>Melvin Conway’s original paper, <a href="https://www.melconway.com/Home/Committees_Paper.html?ref=chahidarid.online">“How Do Committees Invent?”</a>, connected a system’s structure to the communication structure of the organization that designed it. The point is deeper than drawing team boxes around services. Communication paths, delegation, incentives, and ownership boundaries constrain which designs are practical.</p><p><a href="https://dora.dev/capabilities/loosely-coupled-teams/?ref=chahidarid.online">DORA’s guidance on loosely coupled teams</a> makes the operating consequence explicit: teams perform better when they can make, test, and deploy changes independently. Independence, however, does not mean isolation. A team can deploy independently and still create hidden coupling through shared data, duplicated rules, inconsistent identities, or an undocumented operational dependency.</p><p>The distinction I use is:</p><ul><li><strong>Autonomy</strong> means a team can make a bounded decision and carry its consequences.</li><li><strong>Isolation</strong> means a team cannot see, influence, or measure the consequences outside its boundary.</li></ul><p>Healthy architecture needs the first and actively prevents the second.</p><h2 id="five-forces-that-turn-reasonable-decisions-into-system-debt">Five forces that turn reasonable decisions into system debt</h2><h3 id="1-local-optimization-rewards-the-visible-target">1. Local optimization rewards the visible target</h3><p>A team is usually measured on the work it can directly control: roadmap delivery, service availability, sprint commitments, cloud cost, or ticket closure. Those measures are useful, but they are incomplete.</p><p>Imagine that an onboarding team is measured on conversion and lead time. Reusing the enterprise customer profile requires coordination with another domain and a migration the roadmap did not fund. The team creates a local profile store. The decision improves the local target and may even be the correct short-term choice. At system level, the organization now has two customer identities, two data-retention paths, and a reconciliation problem.</p><p>The architecture followed the scorecard. If leaders measure only local throughput, they should expect local designs.</p><h3 id="2-delivery-pressure-changes-the-option-set">2. Delivery pressure changes the option set</h3><p>Deadlines do not merely make teams work faster. They remove options that require discovery, coordination, migration, or platform work.</p><p>Under pressure, temporary solutions are attractive:</p><ul><li>copy a validation rule instead of publishing a reusable contract;</li><li>query another team’s database instead of waiting for an API;</li><li>add a second queue because the shared event contract is difficult to change;</li><li>keep a manual approval because automating the control does not fit the release;</li><li>bypass a platform capability because onboarding takes longer than the feature.</li></ul><p>The danger is not the workaround itself. The danger is a workaround with no owner, expiry condition, observable cost, or funded removal plan. “Temporary” is not a lifecycle state.</p><h3 id="3-unclear-ownership-hides-at-the-seams">3. Unclear ownership hides at the seams</h3><p>Most organization charts assign applications and teams. They do not always assign the relationship between them.</p><p>Who owns the meaning of “customer active” across onboarding, compliance, core banking, cards, and payments? Who owns replay after an event contract changes? Who decides whether a failed downstream posting can move a payment back to processing? Who explains the customer outcome when every individual service met its SLO?</p><p>If the answer is “all of us,” the operational answer is often “nobody.” Microsoft’s current <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/govern/build-cloud-governance-team?ref=chahidarid.online">cloud governance guidance</a> similarly emphasizes explicit function, authority, scope, and interaction across teams. The useful lesson is not to create another committee. It is to make decision rights and escalation paths executable.</p><p>I assign four things at every important boundary:</p><ol><li>the owner of the business meaning;</li><li>the owner of the technical contract;</li><li>the owner of the end-to-end outcome;</li><li>the authority that resolves a conflict.</li></ol><p>Without those, a dependency can be documented and still remain unowned.</p><h3 id="4-standards-fail-when-the-compliant-path-is-harder">4. Standards fail when the compliant path is harder</h3><p>An architecture standard can be correct and still be ineffective. If it is abstract, hard to discover, slow to approve, or unsupported in the delivery toolchain, teams will route around it.</p><p>“Use event-driven integration” is not an executable standard. A usable standard answers:</p><ul><li>Which event types and ownership rules apply?</li><li>How are schema compatibility and sensitive data checked?</li><li>What are the retry, ordering, and idempotency expectations?</li><li>Which supported libraries, templates, and observability conventions exist?</li><li>How does a team request a justified exception?</li><li>Who upgrades the shared capability?</li></ul><p>The strongest standards are delivered as paved roads: templates, APIs, tests, policy feedback, examples, and supported runtime capabilities. The <a href="https://teamtopologies.com/s/Organization-Dynamics-with-Team-Topologies-Mini-book-MB80.pdf?ref=chahidarid.online">Team Topologies material on organization dynamics</a> frames cognitive load as an important design constraint. A standard that transfers all integration and operational complexity to the consuming team has not reduced system complexity; it has only moved it.</p><h3 id="5-missing-system-feedback-allows-drift-to-look-successful">5. Missing system feedback allows drift to look successful</h3><p>A local release can be green while the system becomes harder to change.</p><p>Traditional dashboards show latency, errors, availability, and resource use by service. System-level architecture needs additional signals:</p><ul><li>duplicated capabilities and business rules;</li><li>cross-domain data ownership violations;</li><li>synchronous dependency depth;</li><li>change lead time across team boundaries;</li><li>exception age and repeated exception patterns;</li><li>contract-breaking changes and consumer recovery time;</li><li>incidents where every component met its SLO but the journey failed;</li><li>cost of changing one business rule across the estate.</li></ul><p>Without these signals, architectural drift remains an opinion. By the time it appears as a major transformation programme, the organization has already paid for it through slower delivery, reconciliation, incidents, and coordination.</p><h2 id="a-synthetic-example-the-%E2%80%9Csimple%E2%80%9D-eligibility-rule">A synthetic example: the “simple” eligibility rule</h2><p>Consider a synthetic eligibility rule used by customer onboarding, payments, lending, and card issuance. The business wants to change one threshold in six weeks.</p><p>Each domain originally implemented the rule locally because its first release had a different deadline. Every implementation is tested and owned. None is careless. Over time:</p><ol><li>onboarding stores the rule in a workflow engine;</li><li>payments copies it into an API validator;</li><li>lending enriches it with a risk attribute;</li><li>cards evaluates it overnight in a batch;</li><li>reporting reconstructs the outcome from downstream data.</li></ol><p>Now a single policy change requires five delivery plans, different effective dates, customer-state reconciliation, and a decision about historical treatment. The architecture problem is not duplicated code alone. It is duplicated authority.</p><p>I would not immediately replace every implementation with one central service. That can turn local duplication into runtime coupling. I would first establish a canonical policy owner, a versioned decision contract, common evidence, explicit latency and availability needs, and a migration sequence. Some domains may evaluate locally using the same signed policy version; others may call a shared capability. The system design follows the required authority and failure model—not a slogan about reuse.</p><h2 id="diagnose-the-environment-before-prescribing-a-target-architecture">Diagnose the environment before prescribing a target architecture</h2>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Visible symptom</th>
<th>Likely environmental cause</th>
<th>Evidence to collect</th>
</tr>
</thead>
<tbody>
<tr>
<td>Duplicate services or rules</td>
<td>Local roadmaps and no reusable capability owner</td>
<td>Change history, funding boundaries, repeated implementation effort</td>
</tr>
<tr>
<td>Direct database access</td>
<td>Missing or unreliable domain contract</td>
<td>API lead time, incident history, data consumers, unsupported queries</td>
</tr>
<tr>
<td>Too many synchronous calls</td>
<td>Teams optimize their step, not journey resilience</td>
<td>Dependency depth, timeout budget, failure propagation, ownership gaps</td>
</tr>
<tr>
<td>Standards ignored</td>
<td>Compliant route is slower or poorly supported</td>
<td>Onboarding time, exception volume, support tickets, failed controls</td>
</tr>
<tr>
<td>Architecture review happens late</td>
<td>Governance is an approval gate, not delivery feedback</td>
<td>Decision timing, rework, waiting time, unresolved ADRs</td>
</tr>
<tr>
<td>“Temporary” components remain</td>
<td>No expiry, owner, or funded removal condition</td>
<td>Age, usage, operating cost, business dependency</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>This table prevents a common failure: treating a structural problem as a training problem. Training helps when people do not know the practice. It does not repair a scorecard, funding model, or ownership gap that rewards the opposite behaviour.</p><h2 id="the-operating-system-i-want-around-good-teams">The operating system I want around good teams</h2><h3 id="make-ownership-end-to-end">Make ownership end to end</h3><p>Give stream-aligned teams a bounded domain and a customer or business outcome, not only a set of components. Publish the contracts they own, the decisions they may take independently, and the situations that require cross-domain resolution.</p><p>Use a named owner for journeys that cross teams. This role does not implement every component. It owns the coherence of the outcome, evidence, and unresolved trade-offs.</p><h3 id="turn-principles-into-executable-fitness-functions">Turn principles into executable fitness functions</h3><p>“Services must be resilient” is too vague to govern. An architecture fitness function checks a property continuously: dependency policy, schema compatibility, recovery objective, encryption, ownership metadata, deployment independence, or an allowed data flow.</p><p>Use automation for rules that can be tested. Keep human review for trade-offs that genuinely require judgement. Martin Fowler’s explanation of <a href="https://martinfowler.com/bliki/ArchitectureDecisionRecord.html?ref=chahidarid.online">architecture decision records</a> reinforces the complementary discipline: record the context, decision, alternatives, and consequences, then supersede the record when evidence changes rather than rewriting history.</p><h3 id="fund-the-shared-work">Fund the shared work</h3><p>Cross-team architecture work cannot survive as spare-time goodwill. Platform capabilities, migrations, contract improvements, and removal of high-cost exceptions need explicit capacity and owners.</p><p>I use an architecture investment backlog with evidence: recurring incidents, repeated exceptions, duplicated cost, long change paths, or control gaps. The backlog competes on business and operational impact, not architectural elegance.</p><h3 id="govern-exceptions-as-information">Govern exceptions as information</h3><p>An exception should record the unmet need, risk owner, compensating control, support model, expiry, and review trigger. Repeated exceptions are product discovery for the platform or evidence that the standard is wrong.</p><p>The goal is not zero exceptions. It is zero invisible exceptions.</p><h3 id="review-outcomes-not-diagrams">Review outcomes, not diagrams</h3><p>Architecture reviews should ask whether the environment produces the intended properties:</p><ul><li>Can teams deploy without coordinating routine changes?</li><li>Can an incident owner explain an end-to-end failure?</li><li>Can one business rule change without a programme?</li><li>Can a team adopt the standard path without waiting for a specialist?</li><li>Are system risks visible before they become transformation projects?</li><li>Can an accepted exception be found, measured, and retired?</li></ul><p>The diagram is a hypothesis. Delivery and runtime evidence decide whether it is true.</p><h2 id="a-30-day-intervention">A 30-day intervention</h2><p>I would start with one painful customer or operational journey, not an enterprise reorganization.</p><p><strong>Week 1 — Map the evidence.</strong> Trace one recent change or incident across teams. Record waiting, decisions, data ownership, workarounds, and unresolved authority.</p><p><strong>Week 2 — Name the seams.</strong> Assign business meaning, contract ownership, journey ownership, and escalation authority for the two or three boundaries creating most friction.</p><p><strong>Week 3 — Make one standard executable.</strong> Replace one policy document with a template, automated check, working example, and exception route.</p><p><strong>Week 4 — Change one feedback loop.</strong> Add a system-level measure—cross-team change lead time, exception age, duplicated rule count, or journey failure—and put one funded improvement on the roadmap.</p><p>This will not “fix the architecture” in a month. It will prove whether the organization can change the environment that keeps reproducing the same architecture.</p><h2 id="my-design-rule">My design rule</h2><p>Good teams do not need more slogans. They need a technical environment where the locally easy decision and the systemically healthy decision are usually the same decision.</p><p>When they are not, the organization must make the trade-off visible: name the owner, preserve the rationale, measure the consequence, fund the repair, and learn from the exception. Architecture becomes coherent when the operating model produces coherence repeatedly—not when one architect draws a cleaner target state.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/architecture-decision-records/">Architecture Decision Records That Teams Keep Using</a></li><li><a href="https://chahidarid.online/en/platform-golden-paths/">Platform Engineering Golden Paths Without Golden Cages</a></li><li><a href="https://chahidarid.online/en/core-banking-modernization/">Modernizing Core Banking Without a Big-Bang Rewrite</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.melconway.com/Home/Committees_Paper.html?ref=chahidarid.online">Melvin Conway — How Do Committees Invent?</a></li><li><a href="https://dora.dev/capabilities/loosely-coupled-teams/?ref=chahidarid.online">DORA — Loosely coupled teams</a></li><li><a href="https://teamtopologies.com/s/Organization-Dynamics-with-Team-Topologies-Mini-book-MB80.pdf?ref=chahidarid.online">Team Topologies — Organization Dynamics mini-book</a></li><li><a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/govern/build-cloud-governance-team?ref=chahidarid.online">Microsoft Cloud Adoption Framework — Build a cloud governance team</a></li><li><a href="https://martinfowler.com/bliki/ArchitectureDecisionRecord.html?ref=chahidarid.online">Martin Fowler — Architecture Decision Record</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The AI-Enabled Digital Bank Is Not a Layer Cake]]></title>
            <description><![CDATA[A target-state digital bank becomes executable when authority, financial truth, evidence, resilience, and AI decision boundaries are explicit.]]></description>
            <link>https://chahidarid.online/en/ai-enabled-digital-bank-architecture/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/ai-enabled-digital-bank-architecture/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Wed, 22 Jul 2026 13:14:38 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/ai-enabled-digital-bank-architecture.png?v&#x3D;20260722-motion-v2" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>A familiar target-state diagram puts channels at the top, APIs and orchestration in the middle, core banking and payments underneath, then data, AI, governance, and observability across the bottom. It is a useful inventory. It shows that a digital bank needs more than a mobile app and a core ledger.</p><p>But it is not yet an architecture a team can execute.</p><p>My test is simple: point at a customer action and ask who is authorised to decide, which system creates the financial fact, what happens after a timeout, which evidence proves the outcome, and how the bank recovers the critical service. If the diagram cannot answer those questions, its layers are categories—not contracts.</p><p>An AI-enabled bank needs two connected paths. The first turns customer intent into an authorised, durable outcome. The second turns observed outcomes into governed data and model proposals. AI may improve the decision, but it must not blur the boundary between a proposal and a permitted financial effect.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/ai-enabled-digital-bank-architecture.png?v=20260722-motion-v2" class="kg-image" alt="Detailed target-state architecture for an AI-enabled digital bank showing customer intent, authority, model proposal, policy evaluation, domain execution, durable financial records, event evidence, observability, governed data, outcome evaluation, controlled model release, resilience, and financial assurance" loading="lazy" width="1600" height="1900"><figcaption>Figure 37. Eleven explicit steps connect the live financial path to the evidence and model lifecycle. Runtime trust, resilience, data governance, AI governance, reconciliation, and audit remain visible control contracts rather than generic foundation layers.</figcaption></figure><h2 id="start-with-the-operation-not-the-platform">Start with the operation, not the platform</h2><p>“Real-time, intelligent, scalable, secure, resilient” sounds right because no one wants the opposite. It is still too vague to guide design.</p><p>The <a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Basel Committee’s operational-resilience principles</a> start from critical operations and a bank’s ability to withstand disruptive events. That changes the architecture discussion. Instead of asking whether Kafka, Kubernetes, or a service mesh is highly available, I ask:</p><ul><li>Which customer or market outcome must continue?</li><li>What level of disruption can the bank tolerate?</li><li>Which people, processes, technology, data, facilities, and third parties support it?</li><li>How does the bank detect that the operation is outside tolerance?</li><li>What degraded mode is safe?</li></ul><p>Take a card-limit increase requested through a conversational assistant. The critical outcome is not “the chatbot replies.” It is that an authenticated customer receives a correct, authorised decision; any approved limit is recorded once; the change is visible to fraud, servicing, statements, and audit; and the bank can explain or reverse the effect under the applicable rules.</p><p>That one operation crosses the channel, identity, product, decision, core-record, event, data, AI, and control concerns shown in a layer diagram. Designing each layer independently will not guarantee the outcome.</p><h2 id="replace-boxes-with-six-enforceable-contracts">Replace boxes with six enforceable contracts</h2><p>I use six contracts to turn the capability inventory into a delivery architecture.</p><h3 id="1-the-authority-contract">1. The authority contract</h3><p>The authority contract answers who or what may request an action, for which resource, under which purpose, scope, limit, and time window.</p><p>In the UAE, the <a href="https://rulebook.centralbank.ae/en/rulebook/introduction-and-scope-2?ref=chahidarid.online">CBUAE Open Finance Regulation</a> makes the separation tangible. The framework includes an API Hub and Trust Framework; data sharing and service initiation are subject to explicit user consent, appropriate authentication, and secure communication. These are not decorative features of an API gateway. They are part of the business authority for an action.</p><p>The same reasoning applies inside the bank. <a href="https://www.nist.gov/publications/zero-trust-architecture?ref=chahidarid.online">NIST’s Zero Trust Architecture</a> rejects implicit trust based only on network location. A request from an “internal” service is not authorised merely because it crossed the service mesh. User identity, workload identity, protected resource, requested operation, policy, and current context still matter.</p><p>An API gateway can validate a token and rate limit traffic. It cannot decide every product entitlement, financial limit, segregation-of-duties rule, or exceptional approval. The target state must show where those decisions live and how their evidence travels with the command.</p><h3 id="2-the-capability-ownership-contract">2. The capability-ownership contract</h3><p>A box labelled “microservices” says nothing about ownership. A banking capability needs a bounded vocabulary, commands it owns, invariants it enforces, records it controls, and service outcomes for which one team is accountable.</p><p><a href="https://bian.org/?ref=chahidarid.online">BIAN’s banking service landscape</a> is useful here because it gives teams a banking-specific vocabulary for decomposing capabilities and service domains. It should inform the map, not become a reason to generate hundreds of services. The deployable boundary should follow a stable business responsibility and its consistency needs—not one rectangle per reference-model term.</p><p>For the limit-increase example, the assistant does not own the limit. A product or credit capability owns the command, validates the account state, applies the policy decision, enforces idempotency, and records the approved change. The model may help interpret the request or propose an amount. Ownership remains with the domain.</p><h3 id="3-the-financial-truth-contract">3. The financial-truth contract</h3><p>Layer diagrams often use “single source of truth” beside a lake, warehouse, or customer-360 platform. In banking, that phrase becomes dangerous when it collapses different kinds of truth.</p><p>The ledger records balanced financial facts. A product system records contractual state. A payment rail supplies external settlement evidence. An event log records that a producer announced something. A warehouse provides governed analytical views. None is a universal replacement for the others.</p><p>The <a href="https://www.bis.org/publ/bcbs239.htm?ref=chahidarid.online">BCBS 239 principles</a> require more than central storage: integrated taxonomies and architecture, metadata, identifiers, ownership, quality, and the ability to support risk-data aggregation during stress. A data lake icon does not satisfy those responsibilities.</p><p>For every important number, the architecture should identify its authoritative producer, business meaning, effective time, correction mechanism, lineage, and reconciliation control. “Customer balance” alone is not precise enough. Available balance, ledger balance, settled balance, and an analytical daily snapshot can be correct at the same time while answering different questions.</p><h3 id="4-the-event-contract">4. The event contract</h3><p>An event-streaming layer can decouple producers from consumers, but it can also become a shared ambiguity bus.</p><p>An event needs a named owner, versioned schema, business key, ordering assumption, event time, publication guarantee, retention policy, privacy classification, and a replay contract. Consumers need to know whether the event reports an accepted command, a committed domain fact, or an observation that may later be corrected.</p><p>I avoid using an event as the only proof of a financial outcome. The domain first commits its durable state and an outbox record within the appropriate transaction boundary. Publication can then be retried. Consumers remain idempotent. Reconciliation compares the source record, emitted event, downstream record, and—where relevant—external rail evidence.</p><p>This is the difference between “event driven” as a transport preference and event-driven architecture as an explicit consistency model.</p><h3 id="5-the-ai-decision-contract">5. The AI-decision contract</h3><p>Putting an “AI/ML layer” below the data platform suggests every model is a shared utility. The risk and latency profile actually depend on the decision.</p><p>Fraud scoring before authorisation, document classification during onboarding, a next-best-action recommendation, and a generative servicing assistant do not have the same authority, evidence, or failure mode. Each needs a decision contract:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Decision concern</th>
<th>Question the architecture must answer</th>
</tr>
</thead>
<tbody>
<tr>
<td>Purpose</td>
<td>Which customer or operational outcome is the model allowed to support?</td>
</tr>
<tr>
<td>Inputs</td>
<td>Which governed data, features, and retrieval evidence were used?</td>
</tr>
<tr>
<td>Identity</td>
<td>Which model, prompt, policy, feature, and knowledge versions ran?</td>
</tr>
<tr>
<td>Authority</td>
<td>Is the result advice, a proposal, or an action within delegated limits?</td>
</tr>
<tr>
<td>Evaluation</td>
<td>Which quality, bias, safety, and business-outcome tests gate release?</td>
</tr>
<tr>
<td>Runtime control</td>
<td>Which confidence rule, deterministic policy, approval, or kill switch constrains it?</td>
</tr>
<tr>
<td>Evidence</td>
<td>Can the bank reconstruct the proposal, decision, action, and outcome?</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>The <a href="https://www.nist.gov/itl/ai-risk-management-framework?ref=chahidarid.online">NIST AI Risk Management Framework</a> treats trustworthiness as a lifecycle concern spanning design, development, use, and evaluation. The Financial Stability Board’s June 2026 report on <a href="https://www.fsb.org/2026/06/sound-practices-for-responsible-adoption-of-artificial-intelligence-ai-consultation-report/?ref=chahidarid.online">sound practices for responsible AI adoption</a> similarly proposes organisation-wide governance across the AI lifecycle. That FSB document is a consultation, not a final binding rule, but its framing is useful: adding a model endpoint is not the same as governing an AI-enabled capability.</p><p>My default boundary is therefore: <strong>the model proposes; deterministic controls and authorised domain capabilities create effects</strong>. A low-risk use case may automate within narrow delegated limits. A material or uncertain case may require human approval. Both paths should record the same decision evidence.</p><h3 id="6-the-evidence-and-resilience-contract">6. The evidence-and-resilience contract</h3><p>Governance and observability should not be drawn only as foundations at the bottom. They must intersect the live path.</p><p>For one request, I want to correlate customer intent, consent, authentication, policy decision, domain command, idempotency key, model and prompt version, retrieval evidence, financial or business record, emitted events, downstream processing, and customer-visible outcome.</p><p><a href="https://opentelemetry.io/docs/concepts/observability-primer/?ref=chahidarid.online">OpenTelemetry’s observability model</a> describes traces, metrics, and logs as signals used to understand internal system state from its outputs. In a bank, technical telemetry needs business context without leaking sensitive data. A trace that proves an HTTP call completed is useful; a control record that proves which authorised command created a financial effect is different and equally necessary.</p><p>The contract also defines failure behavior. If the model is unavailable, does the bank fall back to deterministic rules, route to a person, or stop the operation? If an event stream is delayed, can the source capability continue safely? If the customer loses the connection after submission, can the channel retrieve the durable outcome without creating the command twice?</p><h2 id="walk-the-request-from-intent-to-evidence">Walk the request from intent to evidence</h2><p>Return to the limit-increase example.</p><ol><li>The channel captures the customer’s intent and presents the required disclosure.</li><li>Identity, consent, session risk, and product scope establish what may be requested.</li><li>The assistant extracts a structured proposal. It does not call a database or mutate an account directly.</li><li>The decision service evaluates governed data and records the model, feature, rule, and evidence versions.</li><li>Deterministic policy checks eligibility, exposure, delegated limits, and whether human approval is required.</li><li>The product capability accepts one idempotent command, enforces its invariant, and records the result.</li><li>Events publish the committed outcome for fraud, servicing, notification, analytics, and regulatory processes.</li><li>Telemetry and control evidence show the end-to-end result; later outcomes return to a governed data product for evaluation and improvement.</li></ol><p>Notice what is not in this sequence: an LLM with broad credentials choosing arbitrary tools. Tool access should be narrow, typed, policy-controlled, and auditable. The AI context should not contain reusable credentials or final authority.</p><h2 id="a-target-state-also-needs-a-transition-state">A target state also needs a transition state</h2><p>A beautifully layered end state can hide the hardest question: how does the bank move without breaking a critical operation?</p><p>I would not begin with a programme to “build the API layer,” “move to microservices,” or “create the AI platform.” I would choose one bounded operation with measurable pain and build a vertical slice.</p><p>For example:</p><ol><li>Map the current limit-change journey, systems of record, approvals, failure modes, and control evidence.</li><li>Define the future authority, capability, truth, event, AI, and resilience contracts for that operation.</li><li>Put a stable API and idempotent command boundary around the current product system.</li><li>Add end-to-end identity, policy, telemetry, and reconciliation before changing the implementation behind the boundary.</li><li>Introduce the AI proposal path in shadow mode and compare it with approved outcomes.</li><li>Automate only the cases that meet evaluation and delegated-authority thresholds.</li><li>Prove recovery and degraded operation, then expand to the next capability.</li></ol><p>This approach produces reusable platform capabilities, but each one is earned by a real journey. It also exposes whether the supposed target architecture supports delivery or merely reorganises the technology catalogue.</p><h2 id="the-architecture-review-i-would-run">The architecture review I would run</h2><p>I would ask every team to complete this scorecard for one critical operation:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Area</th>
<th>Evidence required before approval</th>
</tr>
</thead>
<tbody>
<tr>
<td>Outcome</td>
<td>Named customer or market outcome, owner, and disruption tolerance</td>
</tr>
<tr>
<td>Authority</td>
<td>Identities, consent, policy, limits, approvals, and revocation path</td>
</tr>
<tr>
<td>Capability</td>
<td>Owning team, commands, invariants, records, APIs, and service objectives</td>
</tr>
<tr>
<td>Financial truth</td>
<td>Authoritative records, settlement evidence, corrections, and reconciliation</td>
</tr>
<tr>
<td>Events</td>
<td>Owners, schemas, guarantees, ordering, replay, privacy, and consumer idempotency</td>
</tr>
<tr>
<td>Data</td>
<td>Purpose, lineage, quality controls, identifiers, retention, and access</td>
</tr>
<tr>
<td>AI</td>
<td>Model purpose, evaluation set, version evidence, runtime guardrails, and fallback</td>
</tr>
<tr>
<td>Operations</td>
<td>Telemetry, dependency map, incident ownership, degraded mode, recovery test</td>
</tr>
<tr>
<td>Change</td>
<td>First migration slice, compatibility boundary, rollback, and acceptance measures</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>If a row is answered with only a product name, the design is not finished.</p><h2 id="the-layer-cake-still-has-a-place">The layer cake still has a place</h2><p>Layered reference diagrams remain useful. They create a shared vocabulary, expose missing capabilities, and help explain investment areas. A central API, data, or AI platform can also reduce duplicated controls and improve consistency.</p><p>The trade-off is concentration. A universal orchestration layer can become the owner of no business outcome and the failure domain of every outcome. A customer-360 platform can become a stale copy mistaken for a system of record. A shared model gateway can standardise access while hiding use-case-specific evaluation and authority.</p><p>The answer is not to reject platforms. It is to make their contracts explicit and keep domain accountability visible.</p><h2 id="my-decision-rule">My decision rule</h2><p>An AI-enabled digital bank is not defined by the presence of an LLM box, an event bus, or a data lake. It is defined by how safely and observably it turns intent into a permitted, durable outcome—and how it learns from that outcome without surrendering authority or financial truth.</p><p>Use the layer diagram as the capability map. Use critical operations, contracts, sequences, failure modes, and evidence to create the architecture.</p><p>The model may propose. The bank must still authorise, execute, record, reconcile, explain, and recover.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/responsible-ai-governance/">Responsible AI Governance as an Engineering System</a></li><li><a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger That Auditors Can Trust</a></li><li><a href="https://chahidarid.online/en/platform-golden-paths/">Platform Engineering Golden Paths Without Golden Cages</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://rulebook.centralbank.ae/en/rulebook/introduction-and-scope-2?ref=chahidarid.online">CBUAE — Open Finance Regulation: Introduction and Scope</a></li><li><a href="https://rulebook.centralbank.ae/en/rulebook/open-finance-regulation?ref=chahidarid.online">CBUAE — Open Finance Regulation</a></li><li><a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Basel Committee — Principles for operational resilience</a></li><li><a href="https://www.bis.org/publ/bcbs239.htm?ref=chahidarid.online">Basel Committee — Principles for effective risk data aggregation and risk reporting</a></li><li><a href="https://www.nist.gov/publications/zero-trust-architecture?ref=chahidarid.online">NIST — Zero Trust Architecture, SP 800-207</a></li><li><a href="https://www.nist.gov/itl/ai-risk-management-framework?ref=chahidarid.online">NIST — AI Risk Management Framework</a></li><li><a href="https://www.fsb.org/2026/06/sound-practices-for-responsible-adoption-of-artificial-intelligence-ai-consultation-report/?ref=chahidarid.online">Financial Stability Board — Sound Practices for Responsible Adoption of AI, consultation report</a></li><li><a href="https://bian.org/?ref=chahidarid.online">BIAN — Banking Industry Architecture Network</a></li><li><a href="https://opentelemetry.io/docs/concepts/observability-primer/?ref=chahidarid.online">OpenTelemetry — Observability primer</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Build an MCP Server for Your API with Ollama and Kimi]]></title>
            <description><![CDATA[Build and cost a secure MCP adapter for an existing API, then connect the same tested tool contracts to Codex, Ollama, or open Kimi models.]]></description>
            <link>https://chahidarid.online/en/build-mcp-server-for-your-api/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/build-mcp-server-for-your-api/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Tue, 21 Jul 2026 12:09:33 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/build-mcp-server-for-your-api.png?v&#x3D;20260721-mcp-api-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>An API becomes useful to an AI system when the model can discover a small capability, supply valid arguments, and receive a result it can reason about. That does <strong>not</strong> mean handing an OpenAPI document and an administrator token to the model. I prefer a narrow MCP adapter that preserves the API's existing authorization and domain rules.</p><p>This guide builds that adapter with the official TypeScript MCP SDK, connects it to Codex or another MCP host, and shows the tool loop needed when Ollama or an open Kimi model is the model layer. The example API is synthetic, but the boundary is the one I use for real systems: the model proposes; the application validates and decides.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/build-mcp-server-for-your-api.png?v=20260721-motion-v2" class="kg-image" alt="Sequence from a model host through an MCP server and tool plugin to an existing REST API, with schema and policy validation, sanitized results, and audit evidence" loading="lazy" width="1600" height="1280"><figcaption>Figure 33. MCP standardizes tool discovery and invocation. Credentials, authorization, domain effects, output minimization, and evidence remain application responsibilities.</figcaption></figure><h2 id="start-with-the-boundary-not-the-sdk">Start with the boundary, not the SDK</h2><p>MCP defines three server capabilities: resources, tools, and prompts. A resource is readable context, a tool performs an action or query, and a prompt is a reusable interaction template. For an existing API, I normally begin with tools because they give the model a named operation with an explicit input schema.</p><p>The first design exercise is not “How do I convert every endpoint?” It is “Which business questions should the model be allowed to ask?” An internal API with 180 operations may need only three initial tools:</p><ul><li><code>orders.get_status</code> reads one authoritative order;</li><li><code>orders.search</code> searches within the caller's tenant and a fixed time window;</li><li><code>orders.prepare_cancellation</code> validates a cancellation proposal without executing it.</li></ul><p>I do not expose raw URLs, arbitrary HTTP methods, SQL, access tokens, or a generic <code>call_api</code> tool. A narrow tool gives me a stable contract, a smaller authorization surface, better descriptions for the model, and meaningful audit events.</p><h2 id="scaffold-the-mcp-server">Scaffold the MCP server</h2><p>The current official TypeScript SDK uses separate server and client packages. A local stdio server needs Node.js 20 or later:</p><pre><code class="language-bash">mkdir api-mcp &amp;&amp; cd api-mcp
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server zod tsx
mkdir -p src/plugins
</code></pre><p>Create <code>src/index.ts</code>. The API URL and token come from the process environment; they are never arguments the model can choose.</p><pre><code class="language-ts">import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const API_BASE = new URL(process.env.API_BASE ?? 'https://api.example.com');
const API_TOKEN = process.env.API_TOKEN;

if (!API_TOKEN) throw new Error('API_TOKEN is required');

const Order = z.object({
  id: z.string(),
  status: z.enum(['pending', 'confirmed', 'cancelled']),
  updatedAt: z.string()
});

function createServer(): McpServer {
  const server = new McpServer({ name: 'orders-api', version: '1.0.0' });

  server.registerTool(
    'orders.get_status',
    {
      title: 'Get order status',
      description: 'Read the authoritative status of one order visible to the caller.',
      inputSchema: z.object({
        orderId: z.string().regex(/^ord_[a-zA-Z0-9]+$/)
      }),
      outputSchema: Order,
      annotations: { readOnlyHint: true, idempotentHint: true }
    },
    async ({ orderId }) =&gt; {
      const controller = new AbortController();
      const timeout = setTimeout(() =&gt; controller.abort(), 5000);

      try {
        const url = new URL(`/v1/orders/${encodeURIComponent(orderId)}`, API_BASE);
        const response = await fetch(url, {
          headers: { Authorization: `Bearer ${API_TOKEN}` },
          signal: controller.signal
        });

        if (response.status === 404) {
          return { content: [{ type: 'text', text: 'Order not found.' }], isError: true };
        }
        if (!response.ok) {
          return {
            content: [{ type: 'text', text: `Upstream API failed with HTTP ${response.status}.` }],
            isError: true
          };
        }

        const order = Order.parse(await response.json());
        return {
          content: [{ type: 'text', text: JSON.stringify(order) }],
          structuredContent: order
        };
      } finally {
        clearTimeout(timeout);
      }
    }
  );

  return server;
}

void serveStdio(createServer);
console.error('orders-api MCP server running on stdio');
</code></pre><p>One Zod schema produces the JSON Schema advertised to clients and validates arguments before the handler executes. The output schema gives clients a machine-readable contract as well. I still parse the upstream response because an API returning malformed data should fail at the adapter boundary, not become confident model context.</p><p>The <code>readOnlyHint</code> and <code>idempotentHint</code> annotations improve client presentation; they are not security controls. The server and the upstream API must enforce identity, tenant scope, role, current state, rate limits, and domain invariants.</p><h2 id="add-tools-as-explicit-plugins">Add tools as explicit plugins</h2><p>As the server grows, I keep each business capability in a small module. “Plugin” here means application code registered deliberately at startup—not arbitrary packages downloaded and executed because a model requested them.</p><pre><code class="language-ts">// src/plugins/types.ts
import type { McpServer } from '@modelcontextprotocol/server';

export type ToolPlugin = {
  name: string;
  register(server: McpServer): void;
};
</code></pre><pre><code class="language-ts">// src/plugins/index.ts
import { orderStatusPlugin } from './order-status.js';
import { customerSummaryPlugin } from './customer-summary.js';

export const plugins = [orderStatusPlugin, customerSummaryPlugin];
</code></pre><pre><code class="language-ts">const server = new McpServer({ name: 'operations-api', version: '1.0.0' });
for (const plugin of plugins) plugin.register(server);
</code></pre><p>This layout gives every plugin an owner, tests, schemas, endpoint allowlist, authorization policy, and version. A plugin registry can load configuration, but I avoid scanning an untrusted directory and importing whatever file appears there.</p><h2 id="test-before-connecting-a-model">Test before connecting a model</h2><p>The official MCP Inspector runs through <code>npx</code> and lets you list tools, inspect schemas, and call handlers directly:</p><pre><code class="language-bash">API_BASE=https://sandbox.example.com \
API_TOKEN=replace-locally \
npx @modelcontextprotocol/inspector npx tsx src/index.ts
</code></pre><p>Test valid calls, malformed identifiers, unauthorized records, timeouts, unexpected upstream JSON, and concurrent requests. Verify that logs contain trace identifiers and decisions but never raw bearer tokens or excessive personal data.</p><p>For stdio, stdout belongs to JSON-RPC. A single <code>console.log</code> can corrupt the protocol stream, so operational logging goes to stderr or a separate sink.</p><h2 id="register-it-in-codex-or-another-host">Register it in Codex or another host</h2><p>Codex can register the same local command and pass environment variables to the child process:</p><pre><code class="language-bash">codex mcp add orders-api \
  --env API_BASE=https://sandbox.example.com \
  --env API_TOKEN=replace-locally \
  -- npx tsx /absolute/path/api-mcp/src/index.ts
</code></pre><p>Do not commit the real token or place it in an article, shell history, or model prompt. In production, prefer a short-lived workload identity or secret manager and bind authorization to the authenticated user rather than one shared super-token.</p><p>VS Code uses the same command in <code>.vscode/mcp.json</code>:</p><pre><code class="language-json">{
  "servers": {
    "orders-api": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "src/index.ts"]
    }
  }
}
</code></pre><p>The host performs <code>tools/list</code>, gives the names, descriptions, and schemas to the model, then sends the selected <code>tools/call</code> to the MCP server. The model does not call the REST API directly.</p><h2 id="where-ollama-fits">Where Ollama fits</h2><p>Ollama exposes a local chat API at <code>http://localhost:11434/api/chat</code> and supports tool definitions and tool-call responses. Ollama is the <strong>model runtime</strong>, not the MCP server. A host or a small bridge must translate MCP tools into the function schema sent to Ollama, execute selected calls through the MCP client, append tool results to the conversation, and ask the model to continue.</p><p>The core loop looks like this:</p><pre><code class="language-ts">import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';

const mcp = new Client({ name: 'ollama-host', version: '1.0.0' });
await mcp.connect(new StdioClientTransport({
  command: 'npx', args: ['tsx', 'src/index.ts']
}));

const { tools } = await mcp.listTools();
const ollamaTools = tools.map(tool =&gt; ({
  type: 'function',
  function: {
    name: tool.name,
    description: tool.description,
    parameters: tool.inputSchema
  }
}));

const response = await fetch('http://localhost:11434/api/chat', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    model: process.env.OLLAMA_MODEL ?? 'qwen3:8b',
    messages: [{ role: 'user', content: 'What is the status of ord_1042?' }],
    tools: ollamaTools,
    stream: false
  })
});

const turn = await response.json();
for (const call of turn.message.tool_calls ?? []) {
  const result = await mcp.callTool({
    name: call.function.name,
    arguments: call.function.arguments
  });
  // Append the assistant turn and this tool result, then call Ollama again.
}
</code></pre><p>A production host adds the complete message loop, cancellation, maximum tool-call depth, per-tool approval, result-size limits, telemetry, and a hard allowlist. Never dispatch <code>call.function.name</code> into <code>eval</code>, a shell, or a dynamic import.</p><h2 id="where-open-kimi-fits">Where open Kimi fits</h2><p>Moonshot AI publishes Kimi model code and weights under a Modified MIT licence. Kimi K2 is designed for tool use, but its published architecture has one trillion total parameters with 32 billion activated parameters. Open weights do not mean laptop-scale. Running the full model locally requires serious inference infrastructure; Moonshot documents engines such as vLLM and SGLang.</p><p>If an Ollama installation exposes a Kimi model with tool support, the bridge above changes only <code>OLLAMA_MODEL</code>. If Kimi is served through an OpenAI-compatible endpoint, the same MCP client can sit behind an OpenAI-compatible host loop. The MCP server itself does not change because model selection belongs above the tool boundary.</p><p>For a genuinely local developer workstation, start with a smaller Ollama model marked as tool-capable. Use Kimi when its serving footprint and operating model fit the environment. Do not market a cloud-routed model as locally hosted simply because the command starts with <code>ollama</code>.</p><h2 id="what-this-architecture-actually-costs">What this architecture actually costs</h2><p>MCP itself does not add a licence fee. The official SDK is open source, and the adapter in this guide is a small process. The material costs sit around it: remote compute, model inference, logs, secrets, identity, testing, and the engineering time needed to keep tool contracts aligned with the API.</p><p>The following prices were checked on <strong>21 July 2026</strong> and are reference points, not quotations. They exclude tax, storage, observability, managed identity, network charges, support, and staff time.</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Deployment choice</th>
<th style="text-align:right">Published entry price</th>
<th>What the number includes</th>
<th>What it does not include</th>
</tr>
</thead>
<tbody>
<tr>
<td>Local stdio MCP + local Ollama</td>
<td style="text-align:right">$0 incremental software cost</td>
<td>MCP process and Ollama Free on hardware you already own</td>
<td>Hardware purchase, electricity, backups, developer time, or adequate RAM/GPU capacity</td>
</tr>
<tr>
<td>Ollama cloud-assisted use</td>
<td style="text-align:right">$0 Free; $20/month Pro; $100/month Max</td>
<td>Usage allowance and concurrency vary by plan</td>
<td>Your MCP hosting, API platform, logs, and production support</td>
</tr>
<tr>
<td>Small remote MCP VM</td>
<td style="text-align:right">DigitalOcean Droplets start at $4/month</td>
<td>A basic general-purpose VM; suitable as a cost floor</td>
<td>Production redundancy, database, load balancer, backups, monitoring, or model inference</td>
</tr>
<tr>
<td>Serverless remote adapter</td>
<td style="text-align:right">Cloudflare Workers Free, or $5/month minimum for Paid</td>
<td>Worker execution and the plan's included usage</td>
<td>Model tokens, external API charges, persistent services, and engineering controls</td>
</tr>
<tr>
<td>Self-hosted open Kimi</td>
<td style="text-align:right">Model licence may be $0; infrastructure is not</td>
<td>You control model serving and data path</td>
<td>GPU capacity, idle headroom, replicas, model loading, power, operations, and incident response</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>The choice is therefore not “free versus paid.” It is <strong>where the cost and operational responsibility land</strong>. A local Ollama prototype can have no new invoice and still consume a workstation. A $4 VM can run a lightweight adapter, but not a large open-weight model. A remote MCP endpoint also needs TLS, workload identity, secret rotation, rate limiting, telemetry, patching, and an availability design.</p><h3 id="a-cost-model-i-can-defend">A cost model I can defend</h3><p>For a production estimate, I separate fixed platform cost from usage-driven model cost:</p><pre><code class="language-text">monthly cost = adapter compute
             + identity, secrets, logs, and storage
             + (input tokens / 1,000,000 × input rate)
             + (output tokens / 1,000,000 × output rate)
             + operational ownership
</code></pre><p>Consider an illustrative workload of <strong>50,000 tool-assisted turns per month</strong>, averaging <strong>2,000 input tokens</strong> and <strong>500 output tokens</strong> per turn. That is 100 million input tokens and 25 million output tokens. At hypothetical rates of $0.60 per million input tokens and $2.50 per million output tokens, inference would be:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Cost component</th>
<th style="text-align:right">Calculation</th>
<th style="text-align:right">Illustrative monthly cost</th>
</tr>
</thead>
<tbody>
<tr>
<td>Input tokens</td>
<td style="text-align:right">100 × $0.60</td>
<td style="text-align:right">$60.00</td>
</tr>
<tr>
<td>Output tokens</td>
<td style="text-align:right">25 × $2.50</td>
<td style="text-align:right">$62.50</td>
</tr>
<tr>
<td>Model subtotal</td>
<td style="text-align:right">$60.00 + $62.50</td>
<td style="text-align:right"><strong>$122.50</strong></td>
</tr>
<tr>
<td>Adapter and controls</td>
<td style="text-align:right">Add the chosen hosting, telemetry, secrets, and support</td>
<td style="text-align:right">Variable</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>Those token prices are deliberately illustrative so the method remains valid when providers change their rates. Replace them with the current rate for the exact model and region, then measure real prompts rather than trusting a demo average. Tool descriptions, schemas, conversation history, retries, and repeated agent loops all consume tokens. Cache discounts and batch rates should be modelled separately, not assumed.</p><p>For self-hosted inference I use a different denominator: <strong>cost per successful, policy-compliant task</strong>, not cost per token. I include amortized hardware or GPU rental, electricity, average and p95 latency, concurrency, utilization, failed tool loops, on-call work, and the spare capacity required during maintenance. Open weights remove a usage invoice; they do not remove capacity planning.</p><h3 id="measure-value-and-failure-together">Measure value and failure together</h3><p>A research-grade pilot needs a baseline and a holdout. I compare the MCP-assisted workflow with the current API or manual workflow on the same task set and record:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Measure</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody>
<tr>
<td>Successful tasks / attempted tasks</td>
<td>Prevents a cheap but unreliable model from looking efficient</td>
</tr>
<tr>
<td>Cost / successful task</td>
<td>Joins model, adapter, and retry cost to an outcome</td>
</tr>
<tr>
<td>p50 and p95 completion time</td>
<td>Shows whether the tail makes the workflow unusable</td>
</tr>
<tr>
<td>Tool calls and retries / task</td>
<td>Reveals loops, weak descriptions, and schema mismatch</td>
</tr>
<tr>
<td>Unauthorized or over-broad attempts</td>
<td>Tests the capability boundary, not only answer quality</td>
</tr>
<tr>
<td>Human corrections / task</td>
<td>Measures the operational burden hidden by a polished demo</td>
</tr>
<tr>
<td>Trace completeness</td>
<td>Confirms that a result can be reconstructed and challenged</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>I would not approve a production rollout from answer-quality scores alone. The evidence package should include the task set, model and prompt versions, tool-schema versions, raw outcome categories, cost assumptions, excluded costs, failure examples, and the date on which provider prices were retrieved.</p><h2 id="production-controls-i-would-not-skip">Production controls I would not skip</h2><p>Before exposing a real API, I require:</p><ol><li><strong>A capability allowlist.</strong> No generic URL, method, SQL, shell, or unrestricted search tool.</li><li><strong>Two validation layers.</strong> MCP schema validation at the adapter and domain validation in the API.</li><li><strong>Caller-bound authorization.</strong> Tenant, role, legal entity, record scope, and purpose remain explicit.</li><li><strong>Separate read and write tools.</strong> A read tool cannot become a write through a hidden query parameter.</li><li><strong>Preparation before material effects.</strong> High-impact changes produce a reviewable proposal before execution.</li><li><strong>Idempotency and state checks.</strong> Retries cannot create duplicate effects, and stale proposals fail closed.</li><li><strong>Output minimization.</strong> Return only fields needed for the model's task; redact secrets and unnecessary personal data.</li><li><strong>Bounded execution.</strong> Deadlines, concurrency limits, response-size limits, and maximum tool-call depth.</li><li><strong>Evidence.</strong> Record authenticated subject, tool, schema version, decision, trace ID, result class, and timing.</li><li><strong>Versioned contracts.</strong> A breaking domain change creates a new tool or contract version.</li></ol><p>The patterns in <a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech</a>, <a href="https://chahidarid.online/en/responsible-ai-governance/">Responsible AI Governance as an Engineering System</a>, and <a href="https://chahidarid.online/en/resilient-payment-apis/">Resilient Payment APIs</a> apply even when the example is not financial. MCP gives the model a clean interface; it does not absolve the system from engineering discipline.</p><h2 id="the-design-test">The design test</h2><p>I consider the adapter ready when I can replace Ollama with Kimi—or replace both with another host—without changing authorization, endpoint mapping, domain rules, or audit evidence. That is the practical value of MCP: the model layer can evolve while the capability boundary stays deliberate.</p><h2 id="references">References</h2><ul><li><a href="https://modelcontextprotocol.io/docs/getting-started/intro?ref=chahidarid.online">Model Context Protocol — Introduction</a></li><li><a href="https://modelcontextprotocol.io/docs/develop/build-server?ref=chahidarid.online">Model Context Protocol — Build an MCP Server</a></li><li><a href="https://github.com/modelcontextprotocol/typescript-sdk?ref=chahidarid.online">Official MCP TypeScript SDK</a></li><li><a href="https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/servers/tools.md?ref=chahidarid.online">MCP TypeScript SDK — Tool Contracts</a></li><li><a href="https://modelcontextprotocol.io/docs/tools/inspector?ref=chahidarid.online">Model Context Protocol — Inspector</a></li><li><a href="https://github.com/ollama/ollama/blob/main/docs/api.md?ref=chahidarid.online">Ollama API — Tool Calling</a></li><li><a href="https://www.ollama.com/pricing?ref=chahidarid.online">Ollama — Pricing</a></li><li><a href="https://github.com/MoonshotAI/Kimi-K2?ref=chahidarid.online">Moonshot AI — Kimi K2 Repository</a></li><li><a href="https://github.com/MoonshotAI/Kimi-K2/blob/main/docs/tool_call_guidance.md?ref=chahidarid.online">Moonshot AI — Kimi K2 Tool-Calling Guidance</a></li><li><a href="https://www.digitalocean.com/pricing/droplets?ref=chahidarid.online">DigitalOcean — Droplet Pricing</a></li><li><a href="https://developers.cloudflare.com/workers/platform/pricing/?ref=chahidarid.online">Cloudflare Workers — Pricing</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UAEFTS Explained: Engineering Real-Time Settlement]]></title>
            <description><![CDATA[How UAEFTS settles AED transfers—and how engineers should design liquidity, idempotency, reconciliation, and recovery around it.]]></description>
            <link>https://chahidarid.online/en/uaefts-real-time-settlement/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/uaefts-real-time-settlement/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Tue, 21 Jul 2026 10:30:37 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/uaefts-real-time-settlement.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>UAEFTS is easy to describe as “the UAE’s RTGS.” That description is correct, but it is not enough to design a production integration. The engineering problem begins when a bank must turn a customer or treasury instruction into an individually settled transfer, manage liquidity while the instruction is in flight, distinguish settlement from beneficiary posting, and prove the outcome later.</p><p>My rule is simple: never let one generic <code>SUCCESS</code> hide the boundaries between instruction acceptance, queueing, settlement in central-bank money, participant confirmation, and account posting.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/uaefts-real-time-settlement.png?v=20260721-motion-v2" class="kg-image" alt="UAEFTS sequence showing participant validation, SFTP or TEP submission, queueing, individual settlement across CBUAE accounts, confirmation, beneficiary posting, and reconciliation" loading="lazy" width="1600" height="1320"><figcaption>Figure 31. UAEFTS settlement is the central financial boundary. Participant acceptance, liquidity queueing, settlement confirmation, beneficiary posting, and reconciliation must remain separately observable.</figcaption></figure><h2 id="uaefts-is-settlement-infrastructure-not-a-payment-api">UAEFTS is settlement infrastructure, not a payment API</h2><p>The <a href="https://www.centralbank.ae/en/our-operations/payments-and-settlements/uae-fund-transfer-system-uaefts/?ref=chahidarid.online">Central Bank of the UAE</a> describes UAEFTS as the country’s Real Time Gross Settlement system, owned, hosted, and managed by CBUAE. It has operated since 2001 and transfers funds between participating banks and financial institutions through their accounts at the central bank.</p><p>“Real time” means instructions are processed continuously during the system’s operating window. “Gross” means transfers are settled individually rather than offsetting debits and credits into a net position. Those properties define a financial-market infrastructure. They do not mean every channel, compliance decision, core-banking posting, or beneficiary notification completes in one synchronous request.</p><p>CBUAE classifies UAEFTS as a <a href="https://www.centralbank.ae/en/our-operations/payments-and-settlements?ref=chahidarid.online">Large-Value Payment System</a>, while its other infrastructures serve retail-payment purposes. Yet UAEFTS activity is not limited to a small set of exceptional corporate payments. The <a href="https://www.centralbank.ae/media/pjimtxfq/annual-report-2025-en.pdf?ref=chahidarid.online">CBUAE Annual Report 2025</a> reports 114.9 million retail transfers worth approximately AED 9.9 trillion and 865,708 institutional transfers worth approximately AED 14.5 trillion through UAEFTS in 2025. At that scale, operational correctness matters as much as raw throughput.</p><h2 id="follow-one-transfer-through-the-boundaries">Follow one transfer through the boundaries</h2><p>I model an outbound UAEFTS transfer as a sequence of explicit proofs.</p><h3 id="1-the-participant-accepts-the-instruction">1. The participant accepts the instruction</h3><p>The originating institution authenticates the caller, validates the account and mandate, applies sanctions and financial-crime controls, checks product and participant rules, and reserves a durable internal operation identifier. Customer acceptance proves that the institution has taken responsibility for processing the request. It does not prove that UAEFTS has received or settled it.</p><p>The operation should become immutable enough to retry safely: amount, currency, debtor, creditor, value date, purpose and permitted remittance data, initiating channel, approvals, and the relevant external references. Corrections should create a controlled replacement or cancellation path rather than silently changing a submitted instruction.</p><h3 id="2-the-gateway-submits-through-the-certified-channel">2. The gateway submits through the certified channel</h3><p>CBUAE states that UAEFTS supports straight-through processing using prescribed formats over SFTP. Participants can also enter transactions through the restricted Transactions and Enquiry Portal, or TEP. I therefore keep the payment domain separate from the transport adapter.</p><p>The adapter serializes the approved instruction into the required market format, signs or packages it as required, sends it through the certified connection, and records a content hash, transmission time, file or message reference, and technical acknowledgement. A successful upload is transport evidence—not settlement evidence.</p><h3 id="3-uaefts-validates-orders-or-queues-the-transfer">3. UAEFTS validates, orders, or queues the transfer</h3><p>The rail may reject an invalid request or queue an otherwise valid instruction for processing. The public CBUAE description says participant requests are queued for immediate action and settled continuously according to the governing rules. The bank’s state model should therefore preserve at least <code>submitted</code>, <code>technically_acknowledged</code>, <code>accepted_for_processing</code>, <code>queued</code>, <code>settled</code>, <code>rejected</code>, and <code>unknown</code> as distinct concepts, even if the exact external codes come from the applicable UAEFTS specification.</p><p>Queueing deserves special treatment because an RTGS system consumes liquidity transfer by transfer. It is not a harmless synonym for “pending.” A growing queue can indicate a participant liquidity problem, a counterparty pattern, an operational incident, or an approaching cut-off risk.</p><h3 id="4-settlement-moves-balances-at-cbuae">4. Settlement moves balances at CBUAE</h3><p>The decisive interbank event is the individual debit and credit across participant accounts held with CBUAE. Once the rail reports settlement under its rules, the originating institution must stop treating the instruction as a retryable outbound command. A second submission is a second financial instruction unless the scheme explicitly identifies it as the same operation.</p><p>That is why idempotency must span the business command, the generated UAEFTS instruction, the outbound artefact, and the returned evidence. A timeout after submission is an unknown outcome. The correct response is to query or reconcile the original reference—not create a replacement payment because the caller did not receive an answer. The same principle appears in my article on <a href="https://chahidarid.online/en/resilient-payment-apis/">resilient payment APIs</a>.</p><h3 id="5-confirmation-and-beneficiary-posting-complete-different-obligations">5. Confirmation and beneficiary posting complete different obligations</h3><p>CBUAE says the system sends confirmation to affected participants after processing. The receiving institution still has its own work: correlate the incoming transfer, run the controls required for that context, post the beneficiary account, notify the customer, and expose the entry through statements or transaction history.</p><p>Interbank settlement and beneficiary credit should therefore have separate timestamps and references. Customer support needs to know whether funds settled to the receiving institution but remain unmatched or unposted. Finance needs the same distinction to reconcile settlement-account movement against customer-account entries.</p><h2 id="liquidity-is-part-of-the-application-design">Liquidity is part of the application design</h2><p>RTGS integrations fail when teams treat liquidity as somebody else’s dashboard. Each individually settled debit changes the participant’s available position. A payment service needs visibility into queued value, settlement-account capacity, urgent-payment priority, expected incoming funds, cut-off exposure, and aged instructions.</p><p>CBUAE’s <a href="https://www.centralbank.ae/en/our-operations/monetary-policy-and-domestic-markets/domestic-market-operations/?ref=chahidarid.online">Intraday Liquidity Facility</a> gives eligible UAEFTS participants access to collateralised dirham funding to manage unforeseen disruption or gridlock. That facility is a liquidity-management tool, not an automatic application retry. The bank still needs an operating decision: which queued instructions are urgent, which collateral and approvals are available, who may invoke a facility, and how every decision is recorded.</p><p>I expose liquidity as an explicit dependency to orchestration. The payment engine may request priority, hold a lower-priority instruction, or escalate an aged queue item, but it must not manufacture settlement capacity or hide the reason for delay.</p><h2 id="build-an-evidence-ledger-beside-the-money-ledger">Build an evidence ledger beside the money ledger</h2><p>For every UAEFTS instruction, I retain an append-only evidence chain:</p><ul><li>the approved business instruction and its payload fingerprint;</li><li>the market-format artefact actually transmitted;</li><li>technical acknowledgements and delivery attempts;</li><li>UAEFTS processing status with the raw code and interpretation version;</li><li>settlement reference, timestamp, amount, currency, and affected participant accounts;</li><li>receiving-side posting or return evidence when available;</li><li>operator actions, investigations, and corrections.</li></ul><p>The money ledger records financial postings. The evidence ledger explains why those postings exist and which external event justified each one. They are linked, but one should not overwrite the other.</p><p>This makes reconciliation deterministic. At minimum, I reconcile four populations: approved outbound instructions, artefacts sent, UAEFTS outcomes, and settlement-account entries. On the inbound side, I reconcile rail confirmations to beneficiary postings and exceptions. A perfectly matched set can still be incomplete, so control totals must cover count, value, currency, business date, source sequence, and delivery completeness. This is the same discipline described in <a href="https://chahidarid.online/en/reconciliation-by-design/">reconciliation by design</a>.</p><h2 id="treat-iso-20022-as-a-controlled-migration">Treat ISO 20022 as a controlled migration</h2><p>The CBUAE Rulebook includes an in-force <a href="https://rulebook.centralbank.ae/en/rulebook/annex-3-transition-swift-iso-20022-payment-standard?ref=chahidarid.online">annex on the transition from SWIFT to the ISO 20022 payment standard</a>, effective 7 November 2025, and places UAEFTS within the UAE payment-system landscape affected by the change.</p><p>That does not justify replacing one file parser and declaring the platform “ISO 20022 ready.” The canonical model must preserve party identities, account data, agent roles, purpose, remittance information, identifiers, and status reasons without truncating them into a legacy schema. The adapter must then apply the exact UAE market profile and version in force.</p><p>I version three things independently:</p><ol><li>the internal payment model;</li><li>the UAEFTS usage profile and validation rules;</li><li>the transport and security configuration.</li></ol><p>That separation lets a bank introduce a new message version without rewriting its ledger or customer journey. It also prevents the common mistake of treating syntactically valid XML as a valid, authorised, fundable payment. <a href="https://chahidarid.online/en/iso-20022/">ISO 20022 is a domain model</a>, but the scheme profile remains the executable contract.</p><h2 id="controls-i-would-require-before-production">Controls I would require before production</h2><ul><li>Every submission has a stable business idempotency key and a unique scheme instruction identity.</li><li>Transport acknowledgement, processing acceptance, settlement, and beneficiary posting are stored separately.</li><li>A timeout becomes <code>unknown</code>, followed by enquiry and reconciliation—not blind resubmission.</li><li>Queue age and value are monitored by priority, participant, and cut-off exposure.</li><li>Operators cannot edit a settled instruction; corrections use governed return or adjustment flows.</li><li>Settlement-account movements reconcile to UAEFTS evidence and internal ledger entries.</li><li>Manual TEP activity is imported into the same control population as straight-through traffic.</li><li>Message-profile versions and mapping changes are tested with golden files and negative cases.</li><li>Production access satisfies CBUAE licensing, network, application-certification, and participant-rule requirements.</li></ul><h2 id="the-architecture-decision">The architecture decision</h2><p>I would not hide UAEFTS behind a method called <code>sendMoney()</code> that returns a boolean. I would build a payment state machine around durable evidence and place settlement at its own boundary.</p><p>The originating instruction, secure delivery, rail acceptance, liquidity queue, CBUAE settlement, receiving-bank posting, and reconciliation result each answer a different question. When those facts remain separate, the platform can recover from timeouts, manage liquidity, explain delays, prevent duplicates, and prove exactly where the money is.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/iso-20022-payment-lifecycle/">ISO 20022 Payment Lifecycle: From pain.001 to camt.054</a></li><li><a href="https://chahidarid.online/en/iso-20022-payment-lifecycle/">How a Payment Moves Through ISO 20022</a></li><li><a href="https://chahidarid.online/en/reconciliation-by-design/">Reconciliation by Design, Not by Spreadsheet</a></li><li><a href="https://chahidarid.online/en/resilient-payment-apis/">Resilient Payment APIs: Timeouts, Retries, and Unknown Outcomes</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.centralbank.ae/en/our-operations/payments-and-settlements/uae-fund-transfer-system-uaefts/?ref=chahidarid.online">Central Bank of the UAE — UAE Fund Transfer System</a></li><li><a href="https://www.centralbank.ae/en/our-operations/payments-and-settlements?ref=chahidarid.online">Central Bank of the UAE — Payments and Settlements</a></li><li><a href="https://www.centralbank.ae/media/pjimtxfq/annual-report-2025-en.pdf?ref=chahidarid.online">Central Bank of the UAE — Annual Report 2025</a></li><li><a href="https://www.centralbank.ae/en/our-operations/monetary-policy-and-domestic-markets/domestic-market-operations/?ref=chahidarid.online">Central Bank of the UAE — Domestic Market Operations and Intraday Liquidity Facility</a></li><li><a href="https://rulebook.centralbank.ae/en/rulebook/annex-3-transition-swift-iso-20022-payment-standard?ref=chahidarid.online">CBUAE Rulebook — Transition from SWIFT to ISO 20022</a></li><li><a href="https://rulebook.centralbank.ae/en/rulebook/313-large-value-and-retail-payment-systems-regulations?ref=chahidarid.online">CBUAE Rulebook — Large Value and Retail Payment Systems Regulations</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[pacs.008 vs pacs.009: Customer, FI, and Cover Transfers]]></title>
            <description><![CDATA[Choose pacs.008, pacs.009, or pacs.009 COV by business function, then operate serial, FI, and cover flows without losing correlation.]]></description>
            <link>https://chahidarid.online/en/pacs-008-vs-pacs-009/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/pacs-008-vs-pacs-009/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Mon, 20 Jul 2026 18:52:15 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/pacs-008-vs-pacs-009.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>When I compare pacs.008 with pacs.009, I do not start with their XML. I start with the financial reason for moving value. A pacs.008 carries a customer credit-transfer instruction between financial institutions. An ordinary pacs.009 carries a credit transfer in which the debtor and creditor are financial institutions. A pacs.009 COV is a specific cover use of pacs.009: it moves funds for one customer transfer already sent separately in pacs.008.</p><p>That distinction matters when a platform must choose a route, screen the right parties, correlate two message legs, or explain why an instruction arrived before its cover funds. A wrong choice changes the represented business function and can create routing, compliance, liquidity, and reconciliation failures.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/pacs-008-vs-pacs-009.png?v=20260721-motion-v2" class="kg-image" alt="Comparison of a serial pacs.008 customer transfer, a pacs.009 financial-institution transfer, and a linked pacs.008 plus pacs.009 COV cover flow" loading="lazy" width="1600" height="1120"><figcaption>Figure 26. Choose the message from the business purpose: customer instructions use pacs.008, FI transfers use pacs.009, and cover settlement links a pacs.008 Direct leg to one pacs.009 COV funds leg.</figcaption></figure><h2 id="the-short-answer">The short answer</h2><p>I use this decision table before discussing fields:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Business movement</th>
<th>Message model</th>
<th>What travels</th>
</tr>
</thead>
<tbody>
<tr>
<td>A customer pays another customer through a serial correspondent chain</td>
<td>pacs.008</td>
<td>Customer instruction and settlement path advance through the same chain</td>
</tr>
<tr>
<td>One financial institution transfers its own funds to another</td>
<td>pacs.009</td>
<td>FI-to-FI transfer, such as an eligible treasury or liquidity movement</td>
</tr>
<tr>
<td>A customer instruction is sent directly while correspondents provide settlement</td>
<td>pacs.008 plus pacs.009 COV</td>
<td>pacs.008 carries the Direct customer instruction; pacs.009 COV carries linked cover funds</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>The official <a href="https://www.iso20022.org/iso-20022-message-definitions?ref=chahidarid.online">ISO 20022 message catalogue</a> names pacs.008 <code>FIToFICustomerCreditTransfer</code> and pacs.009 <code>FinancialInstitutionalCreditTransfer</code>. The <a href="https://www.bis.org/cpmi/publ/d230.htm?ref=chahidarid.online">CPMI harmonised requirements</a> include both in the core cross-border credit-transfer set and make “use the appropriate message for a particular business function” the first fundamental requirement.</p><p>The rule is therefore not “customer-facing channel means pacs.008” or “bank network means pacs.009.” Both messages travel between financial institutions. The difference is whose underlying credit transfer the message represents and, for cover, whether the message is the customer instruction or the funding leg.</p><h2 id="name-the-version-and-profile">Name the version and profile</h2><p>“We support pacs.008” is incomplete. As of this review, the global catalogue lists pacs.008.001.14 and pacs.009.001.13. The February 2026 <a href="https://www.bis.org/cpmi/publ/d230_annex.pdf?ref=chahidarid.online">CPMI technical annex</a> models pacs.008.001.13 and pacs.009.001.12. The April 2026 PMPG cover guidance discusses the CBPR+ pacs.009.001.08 COV usage guideline.</p><p>These are different layers and release cycles: the global message definition, a harmonised cross-border data model, and a network market-practice profile. The <a href="https://www.iso20022.org/faq?ref=chahidarid.online">ISO 20022 implementation FAQ</a> notes that implementation depends heavily on the adopting community, so schemes can constrain the same base message differently.</p><p>For every integration, I record:</p><ul><li>message identifier and namespace;</li><li>market-practice or scheme usage guideline and release;</li><li>permitted settlement methods and agents;</li><li>validation rules beyond XSD;</li><li>supported identifiers, code-list snapshot, and character rules;</li><li>bilateral restrictions and activation date.</li></ul><p>Schema validity proves structure against one XSD. It does not prove that the message is valid for the selected route or profile. The deeper implementation model is covered in <a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model</a>; here the important point is that routing must select a versioned business contract, not only a serializer.</p><h2 id="serial-pacs008-follows-the-customer-payment-chain">Serial: pacs.008 follows the customer-payment chain</h2><p>Consider a synthetic EUR 250,000 supplier payment. A debtor instructs Bank A to credit a supplier at Bank C. Bank A has no direct settlement relationship with Bank C for the route, so Bank B acts as an intermediary.</p><p>In the serial model, Bank A sends pacs.008 to Bank B, and Bank B forwards the customer credit transfer to Bank C according to the applicable profile and account relationships. The customer parties, agents, amount, remittance, purpose, and transaction identifiers remain part of the customer-payment instruction as it advances. The <a href="https://www.swift.com/es/node/309141?ref=chahidarid.online">Swift CBPR+ customer-payment material</a> explicitly separates this serial pacs.008 method from the cover method.</p><p>I represent each hop as part of one end-to-end payment but not as one database update. Each hop can be received, validated, held, rejected, settled, or forwarded at a different time. The platform keeps the end-to-end identity and the current instructed/instructing agents visible while preserving prior-agent evidence required by its profile.</p><p>Serial flow is simpler to correlate because instruction and settlement follow the same chain, but it is not automatically faster or safer. Every intermediary adds a boundary for cut-off times, liquidity, screening, fees, transformation, and exceptions.</p><h2 id="fi-to-fi-ordinary-pacs009-represents-a-bank-transfer">FI-to-FI: ordinary pacs.009 represents a bank transfer</h2><p>Now remove the supplier. Bank A wants to transfer its own funds to Bank C for an eligible treasury purpose. The debtor and creditor are financial institutions; there is no underlying customer credit transfer to fund. This is the ordinary pacs.009 business function.</p><p>A pacs.009 can pass through intermediaries and its settlement can fail, but it does not become customer payment data because an operator attaches a customer reference. Conversely, a pacs.008 is not the right treasury message merely because its XML can carry agents and amounts.</p><p>This boundary matters in the canonical model. I keep <code>customer_credit_transfer</code> and <code>financial_institution_credit_transfer</code> as separate domain concepts with separate allowed parties, purposes, routing policies, and validations. A generic <code>credit_transfer</code> table can share infrastructure, but it must not erase the business type.</p><h2 id="cover-one-payment-two-linked-paths">Cover: one payment, two linked paths</h2><p>Return to the supplier payment. Bank A can send the pacs.008 customer instruction directly toward Bank C while using reimbursement agents to move the funds. The customer instruction and settlement now travel on different paths:</p><ol><li>The <strong>Direct leg</strong> is the pacs.008 from the debtor-agent side toward the creditor agent.</li><li>The <strong>cover leg</strong> is one pacs.009 COV through the correspondent or reimbursement chain.</li></ol><p>The <a href="https://www.swift.com/swift-resource/251967/download?ref=chahidarid.online">Swift ISO 20022 Programme User Handbook</a> documents the pacs.008 settlement method and uses <code>COVE</code> to indicate settlement through a covering pacs.009. The current <a href="https://www.swift.com/swift-resource/252248/download?ref=chahidarid.online">PMPG Cover Payments guideline</a> is stricter about purpose: pacs.009 COV covers one separately sent pacs.008 customer credit transfer. It is not an alternative to pacs.008, and an ordinary pacs.009 must not be used to disguise that customer-cover function.</p><p>The COV usage is identified by the underlying customer credit-transfer component. That component copies selected information from the pacs.008, including the customer parties required by the profile, and the guidance says those elements must remain unchanged through the cover chain. Intermediaries need that underlying information for screening; replacing it with placeholders or unrelated data defeats the transparency the variant was designed to provide.</p><p>The Direct and cover legs need deterministic correlation. PMPG guidance describes the UETR as linking pacs.008 and pacs.009 COV and says the related camt.054 credit notification should reference it. I also correlate message identifiers, amount, currency, agents, value date, and the immutable customer-data fingerprint. The UETR is evidence, not permission to ignore contradictory fields.</p><h2 id="model-two-states-then-derive-the-customer-outcome">Model two states, then derive the customer outcome</h2><p>A cover payment is where weak state models become dangerous. Receiving the Direct pacs.008 does not mean the cover funds have settled. Receiving a pacs.009 COV does not prove that the beneficiary was credited. A transport acknowledgement is not a business acceptance, and a credit advice is not the same thing as end-of-day reconciliation.</p><p>I keep at least two linked state machines:</p><ul><li><strong>instruction state:</strong> received, validated, held, accepted, rejected, cancelled, or completed under the adopted profile;</li><li><strong>cover state:</strong> expected, received, screening, settled, delayed, rejected, returned, or unmatched.</li></ul><p>The customer-facing state is derived from policy and evidence across both. Whether Bank C credits before cover receipt is not a universal ISO 20022 rule; it depends on legal interpretation, scheme rules, account relationships, credit policy, and the selected service. The platform must record that decision instead of burying it inside an adapter.</p><p>PMPG guidance calls for delay or rejection of the cover leg to be communicated quickly and identifies pacs.002, tracking updates, cancellation, return, and investigation paths depending on the situation. For the API side, I apply the same uncertainty rule described in <a href="https://chahidarid.online/en/resilient-payment-apis/">Resilient Payment APIs</a>: a timeout means the caller does not know, not that money did not move.</p><h2 id="failure-modes-i-test-before-launch">Failure modes I test before launch</h2><p>The happy path is easy to draw. These are the cases that reveal whether the model is correct:</p><ol><li><strong>Ordinary pacs.009 used as cover.</strong> The funds arrive without the required underlying customer context. Stop and investigate; do not infer a COV variant from an operator note.</li><li><strong>pacs.009 COV has no matching pacs.008.</strong> Hold it as unmatched evidence, alert operations, and follow the applicable status or investigation process.</li><li><strong>Direct and cover data disagree.</strong> Compare UETR, amount, currency, agents, value date, and the underlying customer fingerprint. Never silently overwrite one leg with the other.</li><li><strong>Direct arrives before cover.</strong> Represent “instruction received, settlement pending” explicitly. Apply the institution's approved credit policy.</li><li><strong>Cover settles after Direct rejection or cancellation.</strong> Route the case into governed exception handling; do not post it as a new unrelated FI transfer.</li><li><strong>A retry creates duplicate legs.</strong> Deduplicate each message in its defined scope and enforce the one-pacs.008-to-one-pacs.009-COV relationship for this use case.</li><li><strong>A router switches serial to cover after submission.</strong> Reject the mutation. A different settlement model creates new instructions and evidence; it is not an invisible adapter fallback.</li></ol><p>Ledger postings should follow confirmed financial events, and corrections should be new entries rather than edits to history. <a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger</a> explains that accounting boundary in more detail.</p><h2 id="my-implementation-checklist">My implementation checklist</h2><p>Before enabling a route, I ask:</p><ul><li>Is the underlying movement a customer transfer or an FI's own transfer?</li><li>If customer-related, are instruction and settlement serial or separated into Direct and cover paths?</li><li>Which exact message version and usage guideline govern the route?</li><li>Does pacs.008 declare the permitted settlement method for that profile?</li><li>If cover is used, is there exactly one corresponding pacs.009 COV with preserved underlying customer data?</li><li>Which identifiers and financial fields must match across both legs?</li><li>Which system owns instruction state, cover state, beneficiary-credit policy, and reconciliation evidence?</li><li>What happens when either leg is delayed, rejected, duplicated, returned, or unmatched?</li><li>Can operations explain the payment without reading raw XML across several systems?</li></ul><p>The durable design rule is simple: choose by business function, then make the route a first-class domain decision. pacs.008 is the customer instruction. pacs.009 is the FI transfer. pacs.009 COV is the linked cover leg for one separately sent pacs.008. Once those meanings are explicit, mapping, status processing, reconciliation, and incident handling become testable instead of interpretive.</p><h2 id="references">References</h2><ul><li><a href="https://www.bis.org/cpmi/publ/d230.htm?ref=chahidarid.online">CPMI — Harmonised ISO 20022 data requirements, updated report</a></li><li><a href="https://www.bis.org/cpmi/publ/d230_annex.pdf?ref=chahidarid.online">CPMI — Harmonised ISO 20022 data models, technical annex</a></li><li><a href="https://www.iso20022.org/iso-20022-message-definitions?ref=chahidarid.online">ISO 20022 — Message Definitions catalogue</a></li><li><a href="https://www.iso20022.org/faq?ref=chahidarid.online">ISO 20022 — Frequently asked questions</a></li><li><a href="https://www.swift.com/swift-resource/252248/download?ref=chahidarid.online">PMPG — Cover Payments Market Practice Guideline, version 4.1</a></li><li><a href="https://www.swift.com/es/node/309141?ref=chahidarid.online">Swift — CBPR+ Customer Payments</a></li><li><a href="https://www.swift.com/swift-resource/251967/download?ref=chahidarid.online">Swift — ISO 20022 Programme User Handbook</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How a Payment Moves Through ISO 20022]]></title>
            <description><![CDATA[Follow a payment from pain.001 initiation through pacs.008, status, settlement, beneficiary credit, camt.054 reporting, and reconciliation.]]></description>
            <link>https://chahidarid.online/en/iso-20022-payment-lifecycle/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/iso-20022-payment-lifecycle/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Mon, 20 Jul 2026 18:52:11 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/iso-20022-payment-lifecycle.png?v&#x3D;20260723-evidence-ledger-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>When a corporate payment reaches a bank, the dangerous question is, “Has it been processed?” That word can mean the file arrived, the instruction passed validation, the debtor account was reserved, the interbank message was accepted, settlement completed, the creditor posted the beneficiary account, or the customer received a notification. Those are different events, owned by different systems.</p><p>I prefer to follow one synthetic payment end to end. A treasury team sends a supplier payment to its bank. The bank validates it, creates an interbank instruction, sends it through a clearing or RTGS service, and the creditor agent posts the beneficiary account. Reporting then returns enough evidence to reconcile the original intent with the financial outcome.</p><p>The familiar shorthand is <code>pain.001 → pacs.008 → camt.054</code>. It is useful, but it is not a universal recipe. The adopted scheme, market infrastructure, message version, and usage guideline determine the actual sequence and rules. My design rule is stricter: <strong>every transition needs its own proof, authority, and timestamp</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/iso-20022-payment-lifecycle.png?v=20260723-evidence-ledger-v1" class="kg-image" alt="ISO 20022 payment lifecycle showing seven separate proofs from pain.001 receipt to reconciliation" loading="lazy" width="1600" height="1320"><figcaption>Figure 25. One customer outcome is derived from seven separately owned proofs. Message acceptance, settlement, beneficiary posting, and reconciliation are related; they are not interchangeable.</figcaption></figure><h2 id="the-lifecycle-is-an-evidence-ledger">The lifecycle is an evidence ledger</h2><p>I do not let one mutable <code>payment_status</code> column carry this journey. I keep an append-only evidence record and derive the customer-facing state from policy. That distinction is what prevents a late callback from turning “settled” back into “processing,” or a transport acknowledgement from becoming “paid.”</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Proof</th>
<th>Typical evidence</th>
<th>What it proves</th>
<th>What it does <strong>not</strong> prove</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. Receipt</td>
<td>Channel ID, payload hash, authenticated principal</td>
<td>The bank received a specific instruction</td>
<td>Business acceptance or debit</td>
</tr>
<tr>
<td>2. Validation</td>
<td>Rule-set version, decision, reason, <code>pain.002</code> where used</td>
<td>The instruction passed or failed the debtor agent’s controls</td>
<td>Interbank acceptance or settlement</td>
</tr>
<tr>
<td>3. Interbank processing</td>
<td><code>pacs.008</code>, business message ID, rail acknowledgement, <code>pacs.002</code> where used</td>
<td>The adopted route accepted, rejected, or held an instruction</td>
<td>Movement of settlement funds unless the profile says so</td>
</tr>
<tr>
<td>4. Settlement</td>
<td>Rail or account-system reference, amount, value date, finality timestamp</td>
<td>The relevant participant-account movement occurred</td>
<td>Beneficiary-account posting</td>
</tr>
<tr>
<td>5. Beneficiary posting</td>
<td>Creditor ledger entry and immutable posting reference</td>
<td>The beneficiary account was credited or rejected</td>
<td>Customer notification or end-to-end reconciliation</td>
</tr>
<tr>
<td>6. Reporting</td>
<td><code>camt.054</code>, statement event, delivery record</td>
<td>A debit or credit entry was reported</td>
<td>That every upstream reference matches</td>
</tr>
<tr>
<td>7. Reconciliation</td>
<td>Matched instruction, rail, ledger, and reporting identities</td>
<td>The expected financial evidence agrees</td>
<td>That history may now be overwritten</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>The table is deliberately scheme-neutral. A usage guideline may use a different message, status vocabulary, or event order. The proof boundary remains useful because it asks the same operational question at every step: <strong>which system is authoritative for this claim?</strong></p><h2 id="iso-20022-names-contracts-not-one-payment-rail">ISO 20022 names contracts, not one payment rail</h2><p>The <a href="https://www.iso20022.org/iso-20022-message-definitions?ref=chahidarid.online">ISO 20022 catalogue</a> separates Payments Initiation, Payments Clearing and Settlement, and Cash Management. That structure is important. <code>pain.001</code> is a customer-to-bank initiation message. <code>pacs.008</code> is an FI-to-FI customer credit-transfer instruction. <code>pacs.002</code> reports processing status. <code>camt.054</code> is a bank-to-customer debit or credit notification.</p><p>These messages describe business communication. They do not create one global settlement system. The <a href="https://www.iso20022.org/faq?ref=chahidarid.online">ISO implementation FAQ</a> explains that an adopting community defines how it uses the standard. A domestic instant-payment scheme, an RTGS service, and a cross-border correspondent route can therefore constrain the same message family differently.</p><p>Before building an adapter, I write down the executable contract:</p><ul><li>exact message identifier and namespace;</li><li>scheme or market-practice release;</li><li>mandatory and forbidden elements beyond the XSD;</li><li>code-list and character-set rules;</li><li>duplicate scope and identifier lifetime;</li><li>accepted status transitions;</li><li>cut-off, settlement, return, and investigation rules.</li></ul><p>That is why <a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model</a> matters more than a library that serializes XML. A valid document can still be invalid for the selected route.</p><h2 id="step-1-pain001-captures-customer-intent">Step 1: pain.001 captures customer intent</h2><p>In the synthetic case, a corporate treasury submits a payment initiation to its debtor agent. The <code>pain.001</code> carries the requested execution, debtor and creditor information, amount, currency, remittance data, and references permitted by the bank’s channel profile.</p><p>Receipt is the first proof, not the outcome. The channel should assign an immutable intake identifier, retain the original payload or its governed evidence, authenticate the sender, and make retries idempotent. If a client times out after submission, the system must let it query the original request instead of encouraging a second payment.</p><p>The bank then validates more than XML. It checks entitlement, mandate, account state, funds or limits, party data, sanctions and compliance controls, cut-off policy, and route availability. Some profiles return customer-payment status through a <code>pain.002</code>; others expose equivalent status through an API or channel workflow. Whatever the transport, I model the result as <code>received</code>, <code>accepted</code>, <code>held</code>, or <code>rejected</code> with reason and timestamp.</p><h2 id="step-2-acceptance-creates-an-interbank-obligation">Step 2: acceptance creates an interbank obligation</h2><p>After acceptance, the debtor agent maps the customer instruction into its canonical payment model and selects a route. For a customer credit transfer between financial institutions, that route can produce a <code>pacs.008</code> under the adopted clearing or network profile.</p><p>This mapping is not a field-copy exercise. The service must preserve end-to-end references, party roles, amount and currency meaning, charge arrangement, remittance, purpose, and agent chain. It must also record which source value produced each outbound value. That lineage is what makes an exception explainable later.</p><p>The <a href="https://www.bis.org/cpmi/publ/d230.htm?ref=chahidarid.online">CPMI’s updated 2026 harmonisation requirements</a> address interbank payments, clearing and settlement, returns, and investigations, while preserving the need for consistent implementation across communities. I take the same approach internally: one payment aggregate can connect the journey, but every message and financial event keeps its own identity, version, and provenance.</p><h2 id="step-3-the-rail-validates-orders-and-settles">Step 3: the rail validates, orders, and settles</h2><p>The clearing or RTGS service validates the message against its own usage guideline. It can reject the instruction, accept it for later processing, queue it for liquidity, order it, or settle it according to its rules. A <code>pacs.002</code> or rail-specific event may report part of this progression.</p><p>The key engineering rule is simple: a processing status is evidence of exactly the state it names. It is not automatically proof that central-bank money moved, that correspondent accounts were posted, that the beneficiary was credited, or that the customer statement reconciles.</p><p>I store the rail message identifier, business status, reason, settlement reference, value date, timestamp, source system, and usage-guideline version separately. Then I derive a payment-level state from an explicit policy. This avoids the common anti-pattern where the last arriving callback overwrites a more authoritative financial event.</p><p>A compact implementation can model the evidence like this:</p><pre><code class="language-sql">create table payment_evidence (
  payment_id          uuid        not null,
  evidence_type       text        not null,
  source_system       text        not null,
  source_reference    text        not null,
  business_status     text        not null,
  occurred_at         timestamptz not null,
  recorded_at         timestamptz not null default now(),
  payload_hash        text        not null,
  profile_version     text        not null,
  primary key (source_system, source_reference, evidence_type)
);
</code></pre><p>The primary key is illustrative, not universal. The adopted route defines the real duplicate scope and identifier lifetime. The important property is that a retry either finds the same evidence or adds a new, separately identifiable event; it never silently edits financial history.</p><p>Payment infrastructure documentation makes this profile boundary concrete. The <a href="https://www.frbservices.org/resources/financial-services/wires/faq/iso-20022/format-technical-documentation-mystandards?ref=chahidarid.online">Fedwire ISO 20022 technical FAQ</a>, for example, directs participants to the service-specific format and validation documentation. Supporting “ISO 20022” without naming the market profile is not a testable claim.</p><h2 id="step-4-beneficiary-credit-is-another-financial-event">Step 4: beneficiary credit is another financial event</h2><p>Once the creditor agent receives the instruction and the settlement evidence required by its operating model, it validates the beneficiary account and applies its posting policy. The beneficiary-credit timestamp can differ from the interbank-settlement timestamp. Legal finality, availability of funds, screening, account restrictions, and local operating rules all matter.</p><p>The platform therefore needs separate facts for:</p><ol><li>instruction accepted by the debtor agent;</li><li>interbank instruction accepted by the rail;</li><li>settlement completed under the rail or account model;</li><li>creditor agent received the instruction;</li><li>beneficiary account posted or rejected;</li><li>customer notification produced;</li><li>reconciliation completed.</li></ol><p>Collapsing these into <code>SUCCESS</code> is convenient until the first dispute. After that, the missing distinctions become an operational and accounting problem.</p><h2 id="step-5-camt054-reports-a-debit-or-credit">Step 5: camt.054 reports a debit or credit</h2><p>A <code>camt.054</code> can notify a customer of a debit or credit entry. In this example, the creditor agent uses it to report the beneficiary credit, while a debtor-side notification can report the debit under the applicable service.</p><p>The <a href="https://www.europeanpaymentscouncil.eu/document-library/guidance-documents/epc-recommendation-iso-20022-customer-reporting-scts-oct-inst?ref=chahidarid.online">European Payments Council recommendation on customer reporting</a> emphasizes ISO 20022 cash-management reporting and reference continuity for reconciliation. That is the real value of structured reporting: the notification should carry enough stable identity to match the booked entry to the instruction and interbank evidence.</p><p><code>camt.054</code> is not a magic close signal. A notification can be duplicated, delayed, corrected, or unmatched. The reconciliation service checks identifiers, account, amount, currency, debit or credit indicator, booking and value dates, and any scheme-specific references. It closes the payment only when the expected financial evidence agrees.</p><p>The broader control pattern is explained in <a href="https://chahidarid.online/en/reconciliation-by-design/">Reconciliation by Design</a>: exceptions must remain visible, and corrections must produce new evidence rather than rewrite history.</p><h2 id="failure-paths-i-test-before-the-happy-path">Failure paths I test before the happy path</h2><p>I design these cases before launch:</p><ul><li><strong>Duplicate pain.001:</strong> return the existing payment identity within the idempotency scope; do not create another obligation.</li><li><strong>Channel timeout after acceptance:</strong> expose queryable state and do not tell the client to resend blindly. The uncertainty model in <a href="https://chahidarid.online/en/resilient-payment-apis/">Resilient Payment APIs</a> applies here.</li><li><strong>Business rejection after transport acceptance:</strong> retain both events. The transport worked; the payment did not pass the business rule.</li><li><strong>pacs.008 accepted but settlement delayed:</strong> show <code>accepted / settlement pending</code>, not <code>paid</code>.</li><li><strong>Settlement recorded but beneficiary posting fails:</strong> open a governed exception with the creditor-side reason and ownership.</li><li><strong>camt.054 arrives twice:</strong> deduplicate the notification without suppressing a genuine later correction.</li><li><strong>References disagree:</strong> stop automatic closure and investigate; never force a match using amount alone.</li></ul><p>Each failure has an owner, a deadline, a safe retry rule, and a customer-facing explanation. A payment platform is not reliable because every message succeeds. It is reliable because uncertainty cannot silently become a second debit or a false completion.</p><h2 id="my-implementation-checklist">My implementation checklist</h2><p>Before enabling a payment route, I ask:</p><ul><li>Which business event does each message represent?</li><li>Which exact usage guideline and version apply?</li><li>What proof authorizes the next transition?</li><li>Which system owns receipt, validation, settlement, posting, notification, and reconciliation?</li><li>Which identifiers survive every transformation?</li><li>How long does each idempotency and duplicate key remain valid?</li><li>Can a late event move state backward or overwrite stronger evidence?</li><li>What does the customer see during a timeout, hold, or partial completion?</li><li>Can operations explain the payment without reconstructing it from raw XML?</li></ul><p>The durable design is not a straight line of acronyms. It is a chain of explicit business contracts and evidence. <code>pain.001</code> captures intent, <code>pacs.008</code> carries the interbank customer transfer, status messages report defined processing outcomes, the settlement system records its financial result, the creditor agent records beneficiary posting, and <code>camt.054</code> supports customer reporting. Reconciliation joins those facts. Only then should the platform say the payment is complete.</p><h2 id="references">References</h2><ul><li><a href="https://www.iso20022.org/iso-20022-message-definitions?ref=chahidarid.online">ISO 20022 — Message Definitions</a></li><li><a href="https://www.iso20022.org/faq?ref=chahidarid.online">ISO 20022 — Frequently asked questions</a></li><li><a href="https://www.bis.org/cpmi/publ/d230.htm?ref=chahidarid.online">CPMI — Harmonised ISO 20022 data requirements, updated 2026</a></li><li><a href="https://www.europeanpaymentscouncil.eu/document-library/guidance-documents/epc-recommendation-iso-20022-customer-reporting-scts-oct-inst?ref=chahidarid.online">European Payments Council — Customer-reporting recommendation</a></li><li><a href="https://www.frbservices.org/resources/financial-services/wires/faq/iso-20022/format-technical-documentation-mystandards?ref=chahidarid.online">Federal Reserve Financial Services — Fedwire ISO 20022 technical FAQs</a></li><li><a href="https://www.swift.com/standards/iso-20022/iso-20022-financial-institutions-focus-payments-instructions?tl=en&ref=chahidarid.online">Swift — ISO 20022 for payment instructions</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Platform Engineering Golden Paths Without Golden Cages]]></title>
            <description><![CDATA[A golden path should make the safe, observable route easiest while preserving a documented escape hatch.]]></description>
            <link>https://chahidarid.online/en/platform-golden-paths/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/platform-golden-paths/</guid>
            <category><![CDATA[Architecture]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:31:27 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/platform-golden-paths.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>Architecture</p><p>I define a golden path as a supported, observable route through common engineering work. It succeeds when teams choose it because it removes friction—not because an internal platform makes every alternative impossible.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/platform-golden-paths.png?v=20260721-motion-v2" class="kg-image" alt="Platform golden path showing developer journeys through self-service templates, reusable capabilities, delivery guardrails, runtime operations, feedback, and an exception route" loading="lazy" width="1600" height="1370"><figcaption>Figure 20. The path composes templates, platform capabilities, and runtime guardrails; telemetry and governed exceptions feed the platform product backlog.</figcaption></figure><h2 id="start-with-a-journey-not-a-portal">Start with a journey, not a portal</h2><p>Teams do not wake up wanting a developer portal. They want to create a service, ship a change, rotate a secret, expose an API, diagnose an incident, or retire a workload. A golden path should make one of those journeys faster and safer from request to operation.</p><p>The <a href="https://tag-app-delivery.cncf.io/whitepapers/platforms/?ref=chahidarid.online">CNCF Platforms White Paper</a> frames a platform as a product that curates capabilities for internal users. That product framing changes the first question from “Which tools should we centralize?” to “Which recurring user problem should we solve, for whom, and how will we know?” The white paper is living guidance; teams should record when they adopt a particular version or interpretation.</p><p>I start with user interviews and observation of the work. I measure current lead time, handoffs, failure/rework, cognitive load, and support demand, then pick a frequent, painful journey with enough commonality to standardize. A rare specialist workload is a poor first candidate.</p><h2 id="separate-the-path-into-contracts">Separate the path into contracts</h2><p>A maintainable path combines several layers without hiding them:</p><ul><li><strong>Experience:</strong> portal, CLI, documentation, examples, and discoverable ownership.</li><li><strong>Scaffolding:</strong> a template that creates a repository, metadata, tests, deployment definitions, and an initial service record.</li><li><strong>Capabilities:</strong> identity, secrets, build, deployment, networking, observability, data, and policy exposed through stable interfaces.</li><li><strong>Guardrails:</strong> automated checks and secure defaults with actionable feedback.</li><li><strong>Operations:</strong> runtime service levels, on-call ownership, upgrades, incident support, and decommissioning.</li></ul><p><a href="https://backstage.io/docs/features/software-templates/?ref=chahidarid.online">Backstage Software Templates</a> provide one official example of controlled inputs and actions that scaffold and publish standardized project skeletons. A template is only the entry point. If generated services cannot receive platform updates, diagnose failures, or exit safely, the organization has created a one-time code generator rather than a supported path.</p><p>Version capability contracts independently from templates. Prefer composition: a team may use the standard build and observability capability while choosing a justified data store. Publish compatibility and deprecation windows. Test migrations on representative services before changing defaults.</p><h2 id="make-safety-the-easiest-choice">Make safety the easiest choice</h2><p>Good defaults remove decisions that add little product value: encrypted transport, baseline telemetry, ownership metadata, dependency scanning, deployment health checks, resource limits, and rollback hooks. Guardrails should explain the risk, show the failing evidence, and provide a repair path.</p><p>Do not confuse safety with uniformity. A single runtime, repository shape, or release process can become a constraint when workloads differ. The <a href="https://tag-app-delivery.cncf.io/whitepapers/platform-eng-maturity-model/?ref=chahidarid.online">CNCF Platform Engineering Maturity Model</a> describes progression toward self-service and standardized interfaces while warning that early approaches may offer too few customization options. Maturity is not the number of mandatory tools; it is the platform’s ability to meet user needs reliably through product practices.</p><p>Define a documented exception route. Ask for the use case, unmet requirement, risk owner, alternative controls, duration, support model, and review date. Time-box temporary exceptions. Feed repeated exceptions into the roadmap: they may reveal a missing capability or a path whose assumptions are too narrow.</p><p>An escape hatch without ownership becomes unmanaged risk. A path without an escape hatch becomes a golden cage.</p><h2 id="measure-outcomes-not-enrolment">Measure outcomes, not enrolment</h2><p>Portal visits and template executions are activity metrics. A platform can have high enrolment because it is mandatory while users remain slow and frustrated.</p><p>Use a balanced scorecard:</p><ul><li>journey lead time and waiting time;</li><li>change failure and recovery performance;</li><li>reliability and security-control coverage;</li><li>developer satisfaction and task success;</li><li>support tickets and time to resolve;</li><li>adoption by eligible teams, not all teams;</li><li>exception frequency, age, and common causes;</li><li>upgrade completion and exit cost.</li></ul><p>Segment the results. A path may work for stateless APIs and fail for data-intensive or regulated workloads. Combine telemetry with interviews; instrumentation shows where users leave, while research explains why.</p><p>Set a hypothesis before launch: “For a standard API, the path will reduce median production onboarding from ten working days to two while preserving required controls.” Define the boundary and baseline. That is more useful than declaring the portal a success after one hundred registrations.</p><h2 id="run-the-platform-as-an-internal-product">Run the platform as an internal product</h2><p>Give every path a product owner, technical owner, service level, documentation, support channel, lifecycle policy, and roadmap. Treat teams as users rather than captive consumers. Publish incidents and breaking changes. Maintain examples that run in CI so documentation cannot drift indefinitely.</p><p>Create feedback loops at three levels:</p><ol><li><strong>Inline:</strong> task-specific errors explain how to recover.</li><li><strong>Operational:</strong> telemetry identifies failure, latency, abandonment, and support hotspots.</li><li><strong>Product:</strong> interviews, exception reviews, and outcome metrics change priorities and contracts.</li></ol><p>Prioritize by user outcome and systemic risk. A new dashboard may be less valuable than reducing a twenty-minute build queue or clarifying ownership during incidents.</p><h2 id="a-synthetic-path-launching-a-standard-api">A synthetic path: launching a standard API</h2><p>Here is how I would model a synthetic “managed API” path. The user supplies ownership and data classification and receives a repository with service metadata, tests, CI, deployment descriptors, and observability. The pipeline verifies policy and deploys to a supported runtime. The catalogue shows ownership, dependency, health, and runbook links.</p><p>A team needing unusual network placement requests an exception. The platform cannot currently satisfy it, so the team uses an approved external pattern with named support and a review date. Three similar exceptions appear; product discovery confirms a reusable networking capability is warranted. The capability is added, migration support is offered, and the exception count falls.</p><p>That loop is the key: the platform learns from friction instead of treating deviation as disobedience.</p><h2 id="the-design-test">The design test</h2><p>My design rule is that a golden path should be easy to enter, safe to operate, inexpensive to upgrade, and possible to leave. It should expose composable contracts rather than conceal an opaque stack. Its success is measured in delivery and operational outcomes, not enforced adoption.</p><p>Build one end-to-end journey, instrument it, support it, and let evidence determine the next path. The goal is a platform product that earns trust—not a golden cage with better branding.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/architecture-decision-records/">Architecture Decision Records That Teams Keep Using</a></li><li><a href="https://chahidarid.online/en/core-banking-modernization/">Modernizing Core Banking Without a Big-Bang Rewrite</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://tag-app-delivery.cncf.io/whitepapers/platforms/?ref=chahidarid.online">CNCF — Platforms White Paper</a></li><li><a href="https://tag-app-delivery.cncf.io/whitepapers/platform-eng-maturity-model/?ref=chahidarid.online">CNCF — Platform Engineering Maturity Model</a></li><li><a href="https://backstage.io/docs/features/software-templates/?ref=chahidarid.online">Backstage — Software Templates</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Architecture Decision Records That Teams Keep Using]]></title>
            <description><![CDATA[A useful ADR captures the forcing context, chosen trade-off, and consequences at the moment of decision.]]></description>
            <link>https://chahidarid.online/en/architecture-decision-records/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/architecture-decision-records/</guid>
            <category><![CDATA[Architecture]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:31:21 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/architecture-decision-records.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>Architecture</p><p>I consider an architecture decision record useful when a future engineer can recover the context, understand the trade-off, and tell whether new evidence should supersede it.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/architecture-decision-records.png?v=20260721-motion-v2" class="kg-image" alt="Architecture decision record lifecycle from proposal to acceptance, implementation, review trigger, and superseding record" loading="lazy" width="1600" height="1460"><figcaption>Figure 19. Accepted ADRs remain immutable historical records; new evidence creates a linked superseding decision rather than rewriting the past.</figcaption></figure><h2 id="the-question-an-adr-must-answer">The question an ADR must answer</h2><p>Code shows what the system does now. It rarely preserves why one option was chosen, which constraints mattered, what was deliberately not solved, or what evidence would justify reversal. Those details disappear when a chat closes or a meeting ends.</p><p>Michael Nygard’s original <a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions?ref=chahidarid.online">“Documenting Architecture Decisions”</a> proposed a deliberately small record: title, context, decision, status, and consequences. The format remains useful because it captures a decision at the moment the trade-off is understood, not months later as historical fiction.</p><p>An ADR is appropriate for a decision that is architecturally significant, costly to reverse, contested, or likely to surprise a future maintainer. It is not required for every library update or implementation detail. Teams should define their threshold and use examples to calibrate it.</p><h2 id="a-compact-record-with-sharp-fields">A compact record with sharp fields</h2><p>I start with a record that fits on one or two screens:</p><ol><li><strong>ID, title, date, owner, status.</strong> Use a stable sequence or identifier and a verb phrase such as “Use an outbox for payment events.”</li><li><strong>Context.</strong> State the forces: scale, reliability, regulation, team capability, deadlines, existing constraints, and what is out of scope.</li><li><strong>Options considered.</strong> Present credible alternatives fairly, including “do nothing.” Link experiments or measurements.</li><li><strong>Decision and rationale.</strong> State the chosen option and the decisive trade-off. “Use Kafka” is not a rationale.</li><li><strong>Consequences.</strong> Record benefits, costs, risks, migration work, and operational obligations.</li><li><strong>Validation and review triggers.</strong> Define how the assumption will be tested and what evidence would prompt reconsideration.</li><li><strong>Links.</strong> Connect code, diagrams, incidents, benchmarks, issues, and related ADRs.</li></ol><p>The <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/architectural-decision-records/adr-process.html?ref=chahidarid.online">AWS ADR process guidance</a> similarly describes concise context, options, decision, consequences, ownership, status, review, and superseding accepted records rather than editing them silently. Treat the page as living guidance and capture the access/version context when using it as an organizational standard.</p><h2 id="a-worked-example-synchronous-call-or-outbox">A worked example: synchronous call or outbox</h2><p>Weak record:</p><blockquote>We chose events because they scale better.</blockquote><p>This cannot be challenged. It omits the transaction boundary, delivery expectations, operational cost, alternatives, and measurement.</p><p>Here is how I would make the synthetic record stronger. It says the service must commit an order and reliably notify downstream fulfilment without a distributed transaction. It compares a synchronous downstream call, database polling, change-data capture, and a transactional outbox. It chooses the outbox because the order row and outbound record can share one local transaction, while acknowledging duplicate delivery, ordering, relay operations, and consumer idempotency. It links a failure test and states review triggers: database throughput below a threshold, relay lag above a service objective, or adoption of a platform capability that provides equivalent guarantees.</p><p>The record does not prove the design works. Tests, telemetry, and operational documentation provide current evidence. The ADR explains why this design was chosen under stated conditions.</p><h2 id="put-adrs-in-the-path-of-work">Put ADRs in the path of work</h2><p>Store ADRs close to the code or architecture repository they govern. Use a predictable directory, searchable index, and lightweight template. A pull request works well because reviewers can comment alongside the proposed implementation and the accepted record becomes versioned.</p><p>Suggested states are <code>proposed</code>, <code>accepted</code>, <code>rejected</code>, <code>deprecated</code>, and <code>superseded</code>. Ownership and decision date prevent an orphaned proposal from appearing authoritative. When a decision changes, create a new ADR that links <code>supersedes</code> and update the old record to point to it without erasing its original context.</p><p>The AWS introduction to <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/architectural-decision-records/introduction.html?ref=chahidarid.online">using ADRs to streamline decisions</a> emphasizes alignment, decision history, reduced repeated debate, and handover. Those benefits depend on discoverability and adoption; creating files alone produces no organizational memory.</p><p>Integrate simple checks: unique IDs, required fields, valid status links, and an index. More important, include an ADR link in significant design pull requests and incident reviews. If a production event invalidates an assumption, open the review trigger rather than leaving the record as dead documentation.</p><h2 id="review-without-approval-theatre">Review without approval theatre</h2><p>Review should improve the decision while it is reversible. Ask:</p><ul><li>Is the problem and scope clear?</li><li>Are alternatives represented honestly?</li><li>Which evidence supports the decisive assumption?</li><li>Who carries each consequence?</li><li>How will implementation validate the choice?</li><li>What new fact would cause reconsideration?</li></ul><p>Avoid committees for every record. Match reviewers to impact: security for a trust boundary, data engineering for consistency, operations for on-call consequences, and product for user-facing trade-offs. Time-box discussion and name the decision owner. Consensus is useful but not always possible; accountability must remain clear.</p><h2 id="failure-patterns-that-make-adrs-decay">Failure patterns that make ADRs decay</h2><p><strong>Decision archaeology.</strong> Writing the ADR after implementation turns it into justification. Draft it with the proposal.</p><p><strong>Template inflation.</strong> A dozen mandatory sections encourages vague filler. Keep the core short and link supporting evidence.</p><p><strong>Technology inventory.</strong> “We use PostgreSQL” is not a decision unless the context, alternatives, and consequences are present.</p><p><strong>Mutable history.</strong> Rewriting an accepted ADR hides why earlier implementation exists. Supersede it explicitly.</p><p><strong>No review trigger.</strong> A record based on traffic, cost, regulation, or team assumptions needs a signal that can invalidate them.</p><p><strong>Disconnected truth.</strong> An ADR that cannot be reached from code, service catalogues, or architecture views will not be found when needed.</p><h2 id="the-smallest-useful-discipline">The smallest useful discipline</h2><p>Start with one repository, a seven-field template, a review state, and supersession links. Apply it only to meaningful choices. Revisit records during incidents, migrations, and major design changes.</p><p>My design rule is not comprehensive documentation. I want to preserve the reasoning that code cannot express: what was known, which compromise was chosen, who accepted the consequences, and which evidence should make the team decide again.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/platform-golden-paths/">Platform Engineering Golden Paths Without Golden Cages</a></li><li><a href="https://chahidarid.online/en/core-banking-modernization/">Modernizing Core Banking Without a Big-Bang Rewrite</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions?ref=chahidarid.online">Michael Nygard — Documenting Architecture Decisions</a></li><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/architectural-decision-records/adr-process.html?ref=chahidarid.online">AWS — Architectural decision record process</a></li><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/architectural-decision-records/introduction.html?ref=chahidarid.online">AWS — Using ADRs to streamline decision-making</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Observability for AI: From Tokens to Business Outcomes]]></title>
            <description><![CDATA[AI observability must connect traces and model behavior to user outcomes, policy decisions, and cost.]]></description>
            <link>https://chahidarid.online/en/ai-observability/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/ai-observability/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:31:15 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/ai-observability.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>I design AI observability to connect a user request to the exact model, context, tools, controls, cost, and outcome—without turning sensitive content into a second uncontrolled dataset.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/ai-observability.png?v=20260721-motion-v2" class="kg-image" alt="AI observability architecture correlating an application trace with model, retrieval, tool, evaluation, outcome, and privacy-controlled telemetry" loading="lazy" width="1600" height="1070"><figcaption>Figure 18. A correlation spine joins technical signals to evaluation and business outcomes, while a privacy boundary decides which content is retained.</figcaption></figure><h2 id="healthy-infrastructure-can-hide-a-failing-product">Healthy infrastructure can hide a failing product</h2><p>An AI endpoint can return HTTP 200 in two seconds while giving an unsupported answer, invoking the wrong tool, or costing three times more than the previous release. CPU, memory, error rate, and latency remain necessary, but they do not describe model behaviour.</p><p>The unit of observation should be a task attempt. Give it a trace ID and capture the configuration that shaped the result: application version, prompt template, model/provider, sampling parameters, retrieval/index version, tool definitions, policy version, and experiment cohort. Then connect technical execution to evaluation and outcome signals.</p><p><a href="https://opentelemetry.io/docs/specs/semconv/?ref=chahidarid.online">OpenTelemetry semantic conventions</a> provide a common model for traces, metrics, logs, events, and resources. When this article was researched on 19 July 2026, the page referenced semantic conventions 1.43.0 and GenAI-related attributes were still evolving. Pin the convention version; do not assume an experimental attribute will remain stable.</p><h2 id="design-a-correlation-spine">Design a correlation spine</h2><p>I start with one root span for the application task. Child spans represent retrieval, model calls, tool execution, and policy checks. I use stable, low-cardinality names such as <code>chat.answer</code> or <code>payment_policy.lookup</code>, not the user’s question. The OpenTelemetry guidance on <a href="https://opentelemetry.io/docs/specs/semconv/how-to-write-conventions/?ref=chahidarid.online">writing semantic conventions</a> explicitly emphasizes low-cardinality operation naming, stability, signal design, and careful treatment of expensive or sensitive attributes.</p><p>Record references to versions rather than copying entire artifacts into every span. A prompt registry ID can resolve to the approved template; a retrieval snapshot ID can identify the indexed corpus; a tool-call span can contain the validated operation name and status. This keeps traces queryable while preserving a chain to deeper evidence under stricter access controls.</p><p>Use metrics for aggregates and service objectives: request rate, first-token and total latency, tokens, cost estimate, tool error rate, empty retrieval rate, policy blocks, and task completion. Logs should capture exceptional diagnostic events. Traces explain causality across a sampled request. Evaluation records attach quality judgments that may arrive later.</p><h2 id="observe-quality-at-three-timescales">Observe quality at three timescales</h2><p><strong>Inline controls</strong> run before or during the response. Examples include schema validation, tool authorization, citation presence, secret detection, and policy filters. Their outputs should appear as named decisions with policy versions, not anonymous “guardrail failed” strings.</p><p><strong>Nearline evaluation</strong> samples recent traces after completion. Deterministic checks and calibrated graders can assess groundedness, instruction following, or tool correctness. Sample by risk and cohort; a uniform 1% sample may miss rare high-impact workflows.</p><p><strong>Offline evaluation</strong> uses controlled, versioned datasets before release and after incidents. It measures regressions reproducibly. Production traces can suggest new cases, but copying live personal data into a test set without consent and governance creates another privacy problem.</p><p>The <a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST Generative AI Profile</a>, published in July 2024 and updated on its page in April 2026, supports lifecycle monitoring of performance, incidents, misuse, privacy, and information-integrity risks. It does not prescribe one telemetry stack; the organization must map signals to its actual harms and controls.</p><h2 id="treat-privacy-as-architecture-not-redaction-later">Treat privacy as architecture, not redaction later</h2><p>Prompts, retrieved documents, model outputs, and tool arguments may contain personal data, financial information, credentials, or proprietary material. Do not log them by default. Decide per field whether to drop, transform, hash, tokenize, sample, encrypt, or store in a restricted evidence vault with a shorter retention period.</p><p>Separate a low-sensitivity operational stream from controlled content evidence. Most dashboards need counts, durations, versions, and status—not raw conversations. If content capture is justified for incident investigation, enforce purpose limitation, role-based access, audit logs, retention/deletion, and tenant isolation. Redaction should happen before data leaves the application trust boundary; downstream cleanup is too late.</p><p>Be cautious with labels. User thumbs-up is affected by interface and expectation; escalation may be the correct outcome; a grader score may drift when its model changes. Store label source, definition, rubric, and version. Do not merge unlike signals into a single “quality” metric.</p><h2 id="build-alerts-around-decisions">Build alerts around decisions</h2><p>An alert should name a failure, affected slice, threshold, and response. Useful examples include:</p><ul><li>unauthorized tool attempts above zero for a privileged workflow;</li><li>groundedness regression for one language or corpus version;</li><li>p95 cost per completed task exceeding its release budget;</li><li>retrieval empty-result rate rising after an index migration;</li><li>model fallback or policy block rate changing sharply by cohort;</li><li>user corrections increasing while infrastructure metrics remain stable.</li></ul><p>Avoid cardinality explosions by putting user IDs, full URLs, prompts, or document IDs into metric labels. Keep those identifiers in controlled trace/event records if justified. Sample strategically: retain all severe incidents, a representative baseline, and enough cases from rare high-risk slices.</p><h2 id="a-synthetic-incident-walkthrough">A synthetic incident walkthrough</h2><p>Here is how I would investigate a synthetic incident. Consider an assistant that answers policy questions from a versioned knowledge base. After a deployment, latency and HTTP errors remain unchanged, but user corrections rise for Arabic questions.</p><p>The correlation spine shows that affected traces used prompt version 12 and index version 31. Retrieval returned relevant passages, but a nearline evaluator flags incorrect date interpretation. The release comparison isolates the regression to the new prompt, not the model or index. Traffic is routed to prompt version 11, the failing traces become de-identified regression cases, and a language-specific release threshold is added.</p><p>Without versioned prompts, retrieval evidence, outcome labels, and slice-aware evaluation, the team would see only healthy servers and anecdotal complaints.</p><h2 id="the-observability-contract">The observability contract</h2><p>For every material AI workflow, document what constitutes success, which configuration identifiers are required, which signals are collected, who may access them, how long they persist, and which threshold triggers rollback or investigation. Test trace completeness just as you test application behaviour.</p><p>My design rule is not “log everything.” I want a deliberately limited evidence system that explains how a particular configuration produced a particular outcome and whether that outcome was acceptable. The privacy boundary is part of its correctness.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech: Control Before Autonomy</a></li><li><a href="https://chahidarid.online/en/rag-financial-operations/">RAG for Financial Operations: Evidence Over Eloquence</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://opentelemetry.io/docs/specs/semconv/?ref=chahidarid.online">OpenTelemetry — Semantic Conventions</a></li><li><a href="https://opentelemetry.io/docs/specs/semconv/how-to-write-conventions/?ref=chahidarid.online">OpenTelemetry — How to write semantic conventions</a></li><li><a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST — Generative AI Profile</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Responsible AI Governance as an Engineering System]]></title>
            <description><![CDATA[Governance becomes effective when policies compile into inventories, tests, approvals, and runtime controls.]]></description>
            <link>https://chahidarid.online/en/responsible-ai-governance/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/responsible-ai-governance/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:31:09 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/responsible-ai-governance.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>I consider responsible AI governance real only when every deployed use case has an owner, a traceable system record, evidence-based controls, and a tested way to stop or change it.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/responsible-ai-governance.png?v=20260721-motion-v2" class="kg-image" alt="Responsible AI governance lifecycle connecting inventory, context mapping, measurement, approval, operation, monitoring, and incident response" loading="lazy" width="1600" height="1410"><figcaption>Figure 17. Governance is a continuous control loop: context determines risk, risk determines evidence, and runtime events update both the inventory and the controls.</figcaption></figure><h2 id="policy-is-not-yet-a-control">Policy is not yet a control</h2><p>“Use AI responsibly” gives an engineering team no executable instruction. A control must identify the risk it addresses, the systems and actors in scope, the evidence required, who decides, when it runs, and what happens on failure.</p><p>The <a href="https://www.nist.gov/itl/ai-risk-management-framework?ref=chahidarid.online">NIST AI Risk Management Framework 1.0</a> organizes work into Govern, Map, Measure, and Manage. That structure is useful because it connects institutional accountability with system context, evaluation, and treatment. NIST was working on a revision in 2026, so teams should record the framework version they map to rather than treating a living reference as timeless.</p><p>Governance is broader than model approval. A system may combine a foundation model, retrieval corpus, prompt, rules, tools, user interface, human review, and downstream workflow. A safe model can still participate in an unsafe process; a risky model can be constrained to a low-impact assistive role. Inventory the use case as an operational system.</p><h2 id="create-one-system-record-that-joins-the-evidence">Create one system record that joins the evidence</h2><p>I start with a stable use-case ID before production access. Here is the system record I would require:</p><ul><li>accountable business owner and technical operator;</li><li>intended purpose, users, affected people, and prohibited uses;</li><li>jurisdictions, deployment role, and preliminary legal classification;</li><li>model/provider, data sources, prompts, tools, dependencies, and versions;</li><li>risk tier and rationale;</li><li>evaluation, security, privacy, accessibility, and human-oversight evidence;</li><li>approved limitations, monitoring thresholds, incident owner, and rollback plan.</li></ul><p>Third-party models do not remove accountability. Track contractual restrictions, data handling, geographic processing, update policy, incident notification, and the provider version actually used. A silent provider update should trigger change assessment, not flow directly into a high-impact process.</p><p>Link artifacts rather than copying them into a governance spreadsheet. The inventory should point to source-controlled prompts, test reports, data lineage, threat models, approvals, deployment records, and incidents. The goal is a traversable evidence graph: from a production call to the approved configuration and from an identified risk to the controls that mitigate it.</p><h2 id="tier-risk-from-context-not-technology-labels">Tier risk from context, not technology labels</h2><p>“Generative AI” is not a risk tier. Map plausible harms using purpose, autonomy, data sensitivity, scale, reversibility, and effect on people. A summarizer for public documentation differs from an agent that can change a person’s payment limit.</p><p>Higher impact should demand stronger evidence and tighter runtime boundaries: independent validation, representative slice testing, least-privilege tools, approval for material actions, appeal routes, enhanced logging, rollout limits, and rehearsed rollback. The <a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST Generative AI Profile</a>, published in July 2024 and updated on its page in April 2026, identifies risks such as confabulation, information integrity, privacy, and misuse and suggests lifecycle actions. It is voluntary guidance, not a legal classification.</p><p>Risk tiers should also state residual risk. Passing controls does not make a system harmless; it makes the accepted exposure visible to the accountable decision-maker.</p><h2 id="separate-legal-applicability-from-internal-assurance">Separate legal applicability from internal assurance</h2><p>The <a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj?ref=chahidarid.online">EU Artificial Intelligence Act, Regulation (EU) 2024/1689</a>, establishes a role- and risk-based regime covering prohibited practices, high-risk systems, transparency, and general-purpose AI. Its applicability is phased and depends on geography, system classification, role, and use case. As of 19 July 2026, teams must verify the current application dates, Commission guidance, and professional legal advice before stating that a specific obligation applies.</p><p>Do not encode “EU AI Act compliant: yes/no” as a casual field. Record the legal analysis, role (for example provider or deployer), system category, applicable provisions, assessor, date, and assumptions. Keep internal responsible-AI controls distinct: an organization may choose stricter requirements than law, and voluntary NIST practices do not become statutory duties merely because both appear in the same control library.</p><h2 id="make-approval-a-reproducible-release-gate">Make approval a reproducible release gate</h2><p>I would generate the approval package from the system record and the exact deployment candidate. It should include versioned evaluations, open findings, risk acceptance, use limitations, monitoring, and rollback. The approver must see what changed since the last approved release.</p><p>Classify changes before deployment. A copy edit may require basic checks; a new model, tool permission, jurisdiction, sensitive dataset, or autonomous action may require reassessment and independent review. CI/CD should verify that a valid approval covers the exact artifact versions. It should not allow a generic ticket to authorize later configurations.</p><p>Avoid approval theatre. A committee that receives a hundred-page document after implementation cannot provide effective challenge. Bring assurance specialists into mapping and test design, and define evidence templates that teams can produce continuously.</p><h2 id="close-the-loop-with-incidents-and-appeals">Close the loop with incidents and appeals</h2><p>Monitor technical behaviour and human impact: prohibited tool attempts, unsupported claims, harmful content, override frequency, complaint/appeal outcomes, subgroup performance where lawful and appropriate, provider changes, drift, cost, and latency. Preserve privacy by collecting only the evidence required and controlling access and retention.</p><p>Thresholds need actions. A severe policy breach may disable a tool immediately; rising error on one slice may restrict rollout; a provider version change may route traffic to the last approved version. Incident handling must link affected calls to configuration, notify accountable roles, preserve evidence, correct harm where possible, and create new tests or controls.</p><p>Appeals and human overrides are not simply “noise.” They can expose a faulty policy, inaccessible interface, missing data, or unjustified automation. Feed those findings back into mapping, measurement, and risk acceptance.</p><h2 id="the-operating-model">The operating model</h2><p>My design rule is that governance is a loop, not a launch checkpoint: inventory the whole system, map context and law, measure risks, approve a specific configuration, monitor use, manage incidents, and update the evidence.</p><p>The strongest test is operational: can the organization identify every affected deployment, explain why it was approved, show which controls ran, contact its owner, and roll it back quickly? If not, the policy has not yet become an engineering system.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech: Control Before Autonomy</a></li><li><a href="https://chahidarid.online/en/rag-financial-operations/">RAG for Financial Operations: Evidence Over Eloquence</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.nist.gov/itl/ai-risk-management-framework?ref=chahidarid.online">NIST — AI Risk Management Framework</a></li><li><a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST — Generative AI Profile</a></li><li><a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj?ref=chahidarid.online">EUR-Lex — Regulation (EU) 2024/1689</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vector Search Architecture Beyond the Demo]]></title>
            <description><![CDATA[Production retrieval combines semantic similarity with filters, lexical signals, freshness, and measurable relevance.]]></description>
            <link>https://chahidarid.online/en/vector-search-architecture/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/vector-search-architecture/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:31:03 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/vector-search-architecture.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>I model production vector search as two systems joined by a contract: an indexing plane that maintains searchable evidence and a query plane that retrieves authorized, relevant results.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/vector-search-architecture.png?v=20260721-motion-v2" class="kg-image" alt="Vector search architecture with separate indexing and query planes converging through filtered hybrid retrieval and reranking" loading="lazy" width="1600" height="1180"><figcaption>Figure 16. Ingestion and query processing are independent flows; versioned indexes, access filters, lexical retrieval, and semantic retrieval meet only at controlled fusion and reranking stages.</figcaption></figure><h2 id="the-demo-hides-the-hard-contracts">The demo hides the hard contracts</h2><p>A demo embeds ten documents, performs nearest-neighbour search, and prints five passages. Production must answer harder questions: which version of a document is searchable, who may see it, how deletion propagates, whether a filtered query preserves recall, and how a new embedding model is cut over without mixing vector spaces.</p><p>Dense retrieval is valuable because it can match semantic similarity beyond exact terms. The original <a href="https://arxiv.org/abs/2005.11401?ref=chahidarid.online">retrieval-augmented generation paper</a> combined generation with retrieved non-parametric memory, but retrieval remains only one subsystem. A fluent answer can still misuse a relevant passage; retrieval quality and generation quality must be evaluated separately.</p><p>I start with the retrieval contract: input query and identity context; eligible corpus and freshness target; returned identifiers, passages, scores, and versions; latency percentile; and the metric by which relevance will be judged.</p><h2 id="indexing-is-a-governed-data-product">Indexing is a governed data product</h2><p>The indexing plane starts with authoritative sources, not arbitrary files. Every item needs a stable source ID, tenant or entitlement metadata, document version, effective time, deletion state, and provenance. Normalize formats, but retain a link to the original evidence. Chunking should follow semantic boundaries where possible and record parent-child relationships so a retrieved fragment can be interpreted in context.</p><p>Embedding records require at least the embedding model/version, dimensions, normalization rule, chunker version, and creation time. Vectors produced by different models generally do not share a meaningful distance space. Never silently write a new model’s vectors into an old index.</p><p>Treat reindexing as a migration. Build a shadow index, replay changes into both old and new versions, evaluate them against the same judgments, then switch an alias or routing rule. Keep a rollback window and verify that deletions and entitlement changes reached both copies. Freshness is an observable service level: measure source-to-search lag and deletion lag, not merely job success.</p><h2 id="query-time-retrieval-is-more-than-ann">Query-time retrieval is more than ANN</h2><p>At query time, normalize the query using the same contract as the index and carry caller identity into retrieval. Authorization should constrain eligibility before results leave the search boundary. Post-filtering an unrestricted top-k can leak metadata and destroy recall when most candidates are forbidden.</p><p>Semantic search should usually work alongside lexical retrieval. Exact identifiers, error codes, account products, and regulatory phrases often favour term-based methods; paraphrased questions favour embeddings. Hybrid fusion—such as reciprocal-rank fusion—combines ranks without pretending incomparable raw scores share a scale. A reranker can then use richer query-document interactions on a small candidate set.</p><p>Approximate nearest-neighbour indexes exchange perfect recall for speed and memory. The <a href="https://arxiv.org/abs/1603.09320?ref=chahidarid.online">HNSW paper</a> describes a multilayer graph approach with tunable search complexity. The operational consequence is not “HNSW is fast”; it is that build parameters, query exploration, filters, data distribution, and hardware jointly determine recall and latency.</p><p>The <a href="https://github.com/pgvector/pgvector?ref=chahidarid.online">pgvector documentation</a> distinguishes exact search from HNSW and IVFFlat and documents filtering, iterative scans, and recall measurement. Its repository showed version 0.8.2 when this article was researched on 19 July 2026; re-check the current release and behaviour before implementing. Product-specific defaults are not universal benchmarks.</p><h2 id="measure-recall-against-a-trustworthy-reference">Measure recall against a trustworthy reference</h2><p>For a labelled query set, run exact search or a high-quality exhaustive reference and compare the approximate result set. Report recall@k or another named metric alongside p50/p95/p99 latency. Record:</p><ul><li>corpus size and vector dimensions;</li><li>distance metric and vector normalization;</li><li>index type and parameters;</li><li>candidate and final k;</li><li>filter selectivity and tenant distribution;</li><li>hardware, concurrency, cache state, and software version.</li></ul><p>Without those conditions, “10 ms search” is marketing, not engineering evidence. Evaluate by slice: short versus long queries, rare vocabulary, fresh documents, languages, tenants, and restrictive filters. Also measure end-to-end relevance such as nDCG or judged precision, because high ANN recall can faithfully retrieve the wrong content if the embedding or chunks are poor.</p><h2 id="a-failure-scenario-the-invisible-tenant-regression">A failure scenario: the invisible tenant regression</h2><p>Here is how I would test the filter boundary with a synthetic case. Imagine an index with one million chunks. An entitlement filter leaves only 0.2% eligible for a particular team. The ANN engine finds globally close neighbours, then a post-filter removes almost all of them. Latency appears healthy, but the team receives empty results.</p><p>Possible remedies include pre-filter-aware indexing, partitioning by a stable tenancy boundary, iterative scanning with a bounded budget, or retrieving a larger candidate pool. Each changes cost and recall. The correct choice depends on tenant size distribution and isolation requirements; it cannot be inferred from unfiltered benchmarks.</p><p>The same scenario shows why access control belongs in the architecture, not in a prompt. Cache keys, logging, reranking, and answer generation must preserve the authorization context. Deletion and revocation need tested end-to-end propagation.</p><h2 id="operate-retrieval-as-a-versioned-service">Operate retrieval as a versioned service</h2><p>Expose the chosen index, embedding, chunker, lexical configuration, filter policy, fusion method, and reranker version in traces. Monitor empty-result rate, source freshness, deletion lag, filter selectivity, score distribution, exact-versus-ANN recall samples, and human relevance feedback. Alert on slice regressions rather than one global average.</p><p>My design rule is simple: keep indexing and query flows separate, join them through a versioned retrieval contract, and test every optimization against exact search and user relevance. That is how vector search graduates from an impressive demo to an evidence service that can be upgraded, audited, and rolled back.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech: Control Before Autonomy</a></li><li><a href="https://chahidarid.online/en/rag-financial-operations/">RAG for Financial Operations: Evidence Over Eloquence</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://arxiv.org/abs/1603.09320?ref=chahidarid.online">Malkov and Yashunin — Efficient and Robust Approximate Nearest Neighbor Search Using HNSW</a></li><li><a href="https://github.com/pgvector/pgvector?ref=chahidarid.online">pgvector — official documentation</a></li><li><a href="https://arxiv.org/abs/2005.11401?ref=chahidarid.online">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LLM Evaluation for Systems That Must Be Right]]></title>
            <description><![CDATA[Evaluation must measure task outcomes, groundedness, safety, and operational cost against versioned cases.]]></description>
            <link>https://chahidarid.online/en/llm-evaluation/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/llm-evaluation/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:57 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/llm-evaluation.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>I do not treat an LLM evaluation as a leaderboard. I treat it as a versioned argument—with evidence—about which failures a particular system can tolerate before release.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/llm-evaluation.png?v=20260721-motion-v2" class="kg-image" alt="LLM evaluation system with deterministic checks, calibrated graders, human escalation, release gate, and production feedback" loading="lazy" width="1600" height="1280"><figcaption>Figure 15. A release decision combines complementary evaluators; production failures become new controlled cases rather than disappearing into an average score.</figcaption></figure><h2 id="begin-with-the-decision-not-the-metric">Begin with the decision, not the metric</h2><p>“Is model B better?” is too vague to evaluate. A useful question names the task, users, consequence of error, operating configuration, and release decision. For a support copilot, the decision might be: can version 7 replace version 6 for English and Arabic refund questions without increasing unsupported policy claims or p95 cost beyond an agreed budget?</p><p>That framing matters because language-model quality is multidimensional. The <a href="https://arxiv.org/abs/2211.09110?ref=chahidarid.online">HELM research framework</a> evaluates models across scenarios and metrics including accuracy, calibration, robustness, fairness, toxicity, and efficiency rather than collapsing quality into one universal number. A production system adds more variables: system prompt, retrieval corpus, tools, sampling parameters, safety policy, and post-processing. Change any of them and the evaluated object has changed.</p><p>Create an evaluation dossier with immutable identifiers for the model, prompt, tools, retrieval snapshot, code commit, dataset, rubric, and judge. A score without that configuration is not reproducible evidence.</p><h2 id="build-a-test-portfolio-around-risk">Build a test portfolio around risk</h2><p>I start with a task taxonomy, then sample cases by consequence and frequency. The portfolio I would take to a release review contains:</p><ul><li>representative cases from real, consented, and de-identified workflows;</li><li>boundary cases where policy, dates, currencies, languages, or permissions interact;</li><li>adversarial cases such as prompt injection and unsupported instructions;</li><li>regression cases distilled from incidents and user corrections;</li><li>counterfactual pairs that vary one sensitive attribute or one critical fact;</li><li>explicit abstention cases where the correct action is to refuse or escalate.</li></ul><p>Protect a holdout set from prompt tuning and routine developer inspection. Otherwise the team optimizes to a familiar test rather than the task. Track provenance and licenses; remove secrets and personal data; document synthetic cases as synthetic. Repeated trials are necessary when sampling or tool execution is nondeterministic. Report the run count and dispersion, not only the mean.</p><p>Slice results by risk, language, workflow, document freshness, and tool path. A 94% aggregate pass rate can conceal a 60% pass rate on high-value transfers or Arabic policy questions. Confidence intervals are especially important for small slices: “zero failures in ten cases” is not evidence of zero risk.</p><h2 id="use-an-evaluation-stack-not-one-judge">Use an evaluation stack, not one judge</h2><p>Different checks answer different questions.</p><p>Deterministic assertions should verify what code can know exactly: JSON schema, required citations, tool arguments, arithmetic, forbidden actions, latency, and cost. Reference-based metrics can compare structured outputs or retrieval results. Model graders can assess qualities such as relevance or groundedness at scale, but they introduce their own bias, instability, and blind spots. Human experts remain necessary for ambiguous, novel, or high-impact cases.</p><p>Calibrate a model grader against independently labeled examples. Publish its agreement, false-pass rate, false-fail rate, and ambiguous region by slice. Do not let the same model family silently grade its own preferred style. <a href="https://www.nist.gov/publications/evaluation-machine-generated-reports?ref=chahidarid.online">NIST’s work on evaluating machine-generated reports</a> emphasizes dimensions such as completeness, accuracy, verifiability, and claim-to-source support—useful reminders that fluent prose is not a factuality metric.</p><p>Route disagreement and high-risk failures to human review. Measure inter-rater agreement and improve a rubric when qualified reviewers interpret it differently. “Human reviewed” is not a method unless the reviewer role, sampling rule, rubric, and resolution process are recorded.</p><h2 id="turn-scores-into-release-policy">Turn scores into release policy</h2><p>I set thresholds before examining the candidate result; otherwise the goal tends to move around the preferred release. Here is how I would model a practical gate:</p><ol><li>zero violations of non-negotiable controls, such as unauthorized tool calls;</li><li>no statistically credible regression on high-risk slices;</li><li>a bounded false-pass rate for the calibrated grader;</li><li>latency and cost budgets at named percentiles;</li><li>documented acceptance of residual risk by the accountable owner.</li></ol><p>Suppose a collections assistant improves answer completeness from 86% to 92% but unsupported fee claims rise from 0.5% to 1.4%. The average may improve while the product becomes less safe. The correct response could be a narrower rollout, a deterministic policy lookup, or mandatory review—not simply another prompt tweak.</p><p>In regulated financial institutions, evaluation evidence may also feed model-risk governance. The US Federal Reserve’s <a href="https://www.federalreserve.gov/frrs/guidance/supervisory-guidance-on-model-risk-management.htm">revised model risk management guidance published on 17 April 2026</a> stresses risk-proportionate governance, independent validation, ongoing monitoring, use limitations, and effective challenge. Applicability depends on the institution and use case; an evaluation suite is evidence for governance, not proof of compliance.</p><h2 id="monitor-the-gap-between-tests-and-production">Monitor the gap between tests and production</h2><p>Pre-release cases are a model of reality, not reality itself. In production, record privacy-safe outcome signals: escalation, correction, task completion, policy violation, user appeal, latency, and cost. Join them to the evaluated configuration. Define drift alerts for input mix and failure slices, not only token volume.</p><p>Every confirmed failure should produce three artifacts: an incident record, a minimized reproducible test, and a decision about whether the release threshold or control must change. Re-run the suite when the model, prompt, tool, retrieval data, policy, or evaluator changes. Benchmark results decay as these components evolve.</p><h2 id="what-a-defensible-evaluation-can-claim">What a defensible evaluation can claim</h2><p>My design rule is to claim only what the evidence supports. A strong evaluation cannot guarantee that an LLM “must be right.” It can state a narrower and more useful conclusion: under a named configuration, on a versioned portfolio, with stated uncertainty, the candidate met defined thresholds and known residual risks are controlled or accepted.</p><p>That claim is less dramatic than a leaderboard win. It is also the kind of claim an engineering owner can reproduce, challenge, monitor, and reverse.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech: Control Before Autonomy</a></li><li><a href="https://chahidarid.online/en/rag-financial-operations/">RAG for Financial Operations: Evidence Over Eloquence</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://arxiv.org/abs/2211.09110?ref=chahidarid.online">Liang et al. — Holistic Evaluation of Language Models (HELM)</a></li><li><a href="https://www.nist.gov/publications/evaluation-machine-generated-reports?ref=chahidarid.online">NIST — On the Evaluation of Machine-Generated Reports</a></li><li><a href="https://www.federalreserve.gov/frrs/guidance/supervisory-guidance-on-model-risk-management.htm">Federal Reserve — Supervisory Guidance on Model Risk Management</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RAG for Financial Operations: Evidence Over Eloquence]]></title>
            <description><![CDATA[A useful RAG answer is a claim linked to current, authorized evidence—not merely fluent text.]]></description>
            <link>https://chahidarid.online/en/rag-financial-operations/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/rag-financial-operations/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:51 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/rag-financial-operations.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>I treat retrieval-augmented generation (RAG) as governed evidence retrieval with a generator attached. It is useful in financial operations only when every material claim traces to current, authorized evidence; retrieval does not guarantee truth, completeness, or permission. The operations examples below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/rag-financial-operations.png?v=20260721-motion-v2" class="kg-image" alt="RAG architecture with governed ingestion, entitlement-aware retrieval, generation, citation verification, abstention, and evaluation feedback" loading="lazy" width="1600" height="1450"><figcaption>Figure 14. Authorization applies before and after retrieval; a citation verifier checks claim support, and abstention is a valid controlled outcome.</figcaption></figure><h2 id="define-the-decision-the-answer-may-influence">Define the decision the answer may influence</h2><p>“Search our procedures” is too broad. Specify the user, task, evidence set, and allowed consequence. Answering where to find a policy is lower risk than recommending whether to release a payment exception. A system that drafts an investigation note differs from one that triggers a workflow.</p><p>The original <a href="https://arxiv.org/abs/2005.11401?ref=chahidarid.online">RAG paper by Lewis and colleagues</a> combines parametric generation with retrieved non-parametric memory and provides provenance for generated text. It establishes a technique, not a control framework for financial decisions.</p><p>Write an answer contract: supported sources, maximum source age, jurisdiction or entity, required citations, prohibited data, confidence/abstention behavior, and actions the answer cannot authorize. For a material decision, keep the human or deterministic rule responsible and present the retrieved evidence separately from the model’s interpretation.</p><h2 id="govern-ingestion-as-a-data-product">Govern ingestion as a data product</h2><p>Each document needs a stable identifier, version, effective and expiry dates, owner, jurisdiction, confidentiality label, retention rule, and source URL. Preserve the original artifact and a checksum. Chunked text is a derived representation; link every chunk to its page, section, and document version.</p><p>Process updates and deletions. A superseded procedure must stop appearing in new answers while remaining available to authorized audit workflows where retention requires it. Build deletion propagation through the object store, search index, vector index, cache, and evaluation fixtures.</p><p>Do not mix incompatible embeddings during a silent model change. Version the chunking algorithm, embedding model, and index. Build a shadow index, compare retrieval quality and access behavior, then cut over atomically. Retain enough lineage to reconstruct which index produced an answer.</p><h2 id="enforce-authorization-throughout-retrieval">Enforce authorization throughout retrieval</h2><p>An entitlement filter drawn as one box is insufficient. Access control begins before ingestion, is attached to each derived chunk, is enforced during candidate generation and ranking, and is checked again before context reaches the model and before an answer reaches the user.</p><p>Use allow-based policies tied to authenticated identity, role, entity, jurisdiction, case, and purpose. Avoid retrieving broadly and filtering only after ranking: unauthorized titles or snippets may leak through logs, scores, caches, or model context. Partition indexes where the security boundary warrants it, and test negative access cases continuously.</p><p>Retrieved documents are untrusted inputs. They can contain malicious instructions or obsolete advice. Treat their content as evidence, never as system instructions. Strip active content, delimit quotations, allowlist tools, and prevent a document from changing the answer contract.</p><h2 id="separate-retrieval-quality-from-answer-quality">Separate retrieval quality from answer quality</h2><p>Evaluate retrieval first. For versioned test questions, measure whether the required evidence appears in the top results, whether forbidden evidence is absent, and whether filters, freshness, and citations resolve to the expected version. Track recall at a fixed candidate count, rank quality, no-result rate, and latency by security slice.</p><p>Then evaluate generation. NIST’s work <a href="https://www.nist.gov/publications/evaluation-machine-generated-reports?ref=chahidarid.online">On the Evaluation of Machine-Generated Reports</a> emphasizes dimensions such as completeness, accuracy, verifiability, and claim-to-source support rather than fluency alone. Break answers into atomic claims and label each as entailed, contradicted, unsupported, or not requiring evidence.</p><p>A citation is not proof. The linked passage may be real but irrelevant. Add a verifier that checks quotation location, document version, user entitlement, and whether the cited passage entails the claim. High-risk or low-confidence results should abstain with a useful explanation: what could not be established, which sources were checked, and what the operator should do next.</p><h2 id="keep-privacy-and-auditability-in-tension">Keep privacy and auditability in tension</h2><p>The <a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST Generative AI Profile</a> addresses information integrity, privacy, confabulation, monitoring, and provenance risks. Apply data minimization: do not log full prompts, documents, or answers by default. Record identifiers, versions, policy decisions, model configuration, latency, token and cost measures, citation results, and a protected reference to content where investigation requires it.</p><p>Use synthetic examples for development and evaluation. Redact personal and account data before model calls. Define model-provider retention and training settings, data residency, encryption, incident duties, and subcontractors. Do not put secrets or access tokens in the retrieval context.</p><h2 id="design-the-operating-loop">Design the operating loop</h2><p>Monitor unsupported-claim rate, citation entailment, stale-source use, access denials, abstention, retrieval miss, feedback by task, and operator overrides. Segment by document family, language, entity, and risk tier. User thumbs-up is not a correctness label; collect structured reasons and review sampled evidence.</p><p>Re-evaluate after changing the model, prompt, chunker, embedding, index parameters, reranker, policy engine, document set, or provider. Keep a release record that binds all those versions to results and known limitations. A kill switch should disable generation while leaving ordinary source search available.</p><h2 id="test-the-dangerous-cases">Test the dangerous cases</h2><p>Test a user asking about another entity, a document whose permissions changed, a superseded policy, conflicting sources, a malicious instruction inside a PDF, a missing page, an unsupported numerical claim, an answer in Arabic citing an English source, and deletion of a sensitive artifact. Verify both the response and the absence of leakage in logs and caches.</p><h2 id="the-architecture-decision">The architecture decision</h2><p>Build RAG as governed evidence retrieval with a generator attached—not as a chatbot with a vector database. Version sources and derived data, enforce entitlement at every boundary, evaluate retrieval and generation separately, verify claim-to-source support, and make abstention successful. The goal is not the most fluent answer. It is an answer whose scope, evidence, and uncertainty an operator can inspect before acting.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/ai-agents-regulated-fintech/">AI Agents in Regulated FinTech: Control Before Autonomy</a></li><li><a href="https://chahidarid.online/en/llm-evaluation/">LLM Evaluation for Systems That Must Be Right</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://arxiv.org/abs/2005.11401?ref=chahidarid.online">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks</a></li><li><a href="https://www.nist.gov/publications/evaluation-machine-generated-reports?ref=chahidarid.online">NIST — On the Evaluation of Machine-Generated Reports</a></li><li><a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST — Generative AI Profile</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI Agents in Regulated FinTech: Control Before Autonomy]]></title>
            <description><![CDATA[Agentic systems should earn autonomy through bounded tools, explicit approvals, and replayable evidence.]]></description>
            <link>https://chahidarid.online/en/ai-agents-regulated-fintech/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/ai-agents-regulated-fintech/</guid>
            <category><![CDATA[AI Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:45 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/ai-agents-regulated-fintech.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>AI Engineering</p><p>An AI agent should not receive financial authority merely because it can plan and call tools. I treat autonomy as a graduated control decision: constrain tools, bind every action to policy and identity, require approval for material effects, and preserve replayable evidence. The use cases below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/ai-agents-regulated-fintech.png?v=20260721-motion-v2" class="kg-image" alt="Controlled AI agent loop with untrusted context, planner, policy enforcement, human approval, bounded tools, evidence, and monitoring" loading="lazy" width="1600" height="1570"><figcaption>Figure 13. The model proposes; deterministic policy, scoped credentials, and approval gates decide whether a financial tool may execute.</figcaption></figure><h2 id="start-with-an-authority-map">Start with an authority map</h2><p>“Agent” describes an interaction pattern, not a risk class. A read-only assistant that summarizes a public policy differs fundamentally from a system that changes a customer limit or initiates a refund. Inventory each use case by data accessed, tools callable, maximum financial effect, reversibility, customer impact, and legal or compliance owner.</p><p>The US banking agencies’ <a href="https://www.federalreserve.gov/supervisionreg/srletters/SR2602.htm">Revised Guidance on Model Risk Management, SR 26-2</a>, issued 17 April 2026, describes risk-tiered inventories, validation, monitoring, governance, effective challenge, and third-party considerations for models used by banking organizations. Whether a particular agent meets a jurisdiction’s formal definition of a model requires legal and supervisory analysis; the control disciplines remain useful for the model, tools, and decision process.</p><p>The Bank of England and FCA’s <a href="https://www.bankofengland.co.uk/report/2024/artificial-intelligence-in-uk-financial-services-2024?ref=chahidarid.online">2024 survey of AI in UK financial services</a> reports industry practices and concerns around accountable ownership, data governance, and third-party dependencies. It is survey evidence, not a rulebook.</p><h2 id="separate-proposal-from-execution">Separate proposal from execution</h2><p>Let the model produce a structured action proposal: tool name, typed parameters, purpose, supporting evidence, confidence or uncertainty, and requested authority. A deterministic policy gateway validates it against the authenticated user, customer/account scope, amount limits, time window, segregation of duties, and current case state.</p><p>The model never chooses its own credential. The gateway issues a short-lived, single-purpose capability to a narrowly scoped tool adapter. The adapter validates parameters again and maps the request to a domain command with an idempotency key. Financial state transitions remain owned by the domain service and ledger, not by the conversation history.</p><p>Material actions require an approval bound to the exact proposal. Show the reviewer the amount, destination, affected account, source evidence, model and prompt version, and expiry. If any material field changes after approval, invalidate it. “A human clicked approve” is not a control if the screen hides what will execute or the agent can substitute parameters later.</p><h2 id="treat-all-retrieved-content-as-untrusted">Treat all retrieved content as untrusted</h2><p>Prompts, emails, documents, web pages, and tool results can contain instructions aimed at the model. Never let retrieved text redefine policy or grant tools. Label content and instructions separately, allowlist tool schemas, validate every output, and prevent the model from selecting arbitrary URLs, SQL, shell commands, or recipients.</p><p>The <a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST Generative AI Profile, AI 600-1</a> identifies risks including confabulation, information integrity, privacy, misuse, and third-party dependencies, with lifecycle actions for governance, measurement, and management. Use it as voluntary risk guidance, not as law.</p><p>Redact or tokenize sensitive context before it reaches a third-party model where possible. Define data residency, retention, training-use, incident notification, and subprocessors contractually. Keep secrets outside prompts and traces. A tool call should reference a protected object identifier rather than copying account data into free text.</p><h2 id="build-an-evidence-record-not-a-surveillance-dump">Build an evidence record, not a surveillance dump</h2><p>For each run, record the authenticated actor, use case, policy version, model and configuration, prompt-template version, retrieval document identifiers and versions, proposed actions, approvals, tool requests, domain-operation identifiers, outcomes, and exceptions. Hash or reference sensitive content instead of logging it indiscriminately.</p><p>Evidence must answer: who requested this, what authority applied, which facts were available, what did the model propose, what deterministic controls allowed, what financial operation executed, and how was the final outcome reconciled? Preserve stable identifiers across traces, cases, and ledger records.</p><p>Replay does not mean re-executing tools. An evaluation harness should reconstruct the input and proposed decision in an isolated environment with mocked tools. Production replay must never move money.</p><h2 id="increase-autonomy-only-after-measured-evidence">Increase autonomy only after measured evidence</h2><p>Use stages: offline evaluation; shadow mode; read-only production; recommendations requiring approval; low-value reversible actions within limits; and only then narrowly defined automation. Promotion criteria should cover task success, unsupported-action rate, policy-block rate, approval disagreement, tool error, security tests, customer harm, operational load, and rollback performance.</p><p>Monitor by risk slice, not only averages. A low overall error rate can hide failures for a rare product or language. Revalidate when the model, prompt, retrieval source, tool schema, policy, or provider changes. Maintain a kill switch at the policy/tool layer that does not depend on the model cooperating.</p><h2 id="test-adversarial-and-operational-failure">Test adversarial and operational failure</h2><p>Test prompt injection in a retrieved document, a model attempting an unlisted tool, parameter substitution after approval, duplicate execution after timeout, expired credentials, a compromised third-party tool, conflicting policies, missing evidence, and a model/provider change during an active case. Assert that no unapproved financial effect occurs and that the evidence still explains the block.</p><h2 id="the-architecture-decision">The architecture decision</h2><p>Put the model inside the control plane, not in charge of it. Models may interpret and propose; deterministic services authenticate, authorize, enforce limits, acquire approval, execute idempotently, and record outcomes. Autonomy should be narrow, measurable, reversible, and earned one use case at a time. That architecture supports useful agents without turning probabilistic text generation into unbounded financial authority.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/rag-financial-operations/">RAG for Financial Operations: Evidence Over Eloquence</a></li><li><a href="https://chahidarid.online/en/llm-evaluation/">LLM Evaluation for Systems That Must Be Right</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.federalreserve.gov/supervisionreg/srletters/SR2602.htm">Federal Reserve — Revised Guidance on Model Risk Management, SR 26-2</a></li><li><a href="https://www.bankofengland.co.uk/report/2024/artificial-intelligence-in-uk-financial-services-2024?ref=chahidarid.online">Bank of England and FCA — Artificial intelligence in UK financial services 2024</a></li><li><a href="https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence?ref=chahidarid.online">NIST — Generative AI Profile, AI 600-1</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tokenization Boundaries for PCI Scope Reduction]]></title>
            <description><![CDATA[Tokenization can support PCI scope reduction when the design, controls, and assessor-confirmed boundaries keep PAN out of ordinary services.]]></description>
            <link>https://chahidarid.online/en/tokenization-and-pci/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/tokenization-and-pci/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:39 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/tokenization-and-pci.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>Tokenization can reduce where primary account numbers travel, but it does not automatically remove PCI DSS scope. I start by drawing the complete card-data flow, including security impact, administrative access, the vault, keys, integrations, and provider responsibilities. The checkout examples below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/tokenization-and-pci.png?v=20260721-motion-v2" class="kg-image" alt="PCI tokenization trust-boundary diagram separating browser capture, token service, vault, application zone, processor, and controlled detokenization" loading="lazy" width="1600" height="1280"><figcaption>Figure 12. Keep PAN inside an isolated card-data zone; send constrained tokens to ordinary services and make every detokenization path explicit and controlled.</figcaption></figure><h2 id="define-what-a-token-is%E2%80%94and-is-not">Define what a token is—and is not</h2><p>Tokenization replaces a primary account number (PAN) with a surrogate value. A well-designed token reveals no PAN, has little value outside its permitted domain, and can be mapped back only through a protected token service or vault. It is not encryption: an encrypted PAN remains cardholder data, and possession of a decryption key restores it directly.</p><p>EMVCo’s <a href="https://www.emvco.com/emv-technologies/payment-tokenisation/?ref=chahidarid.online">Payment Tokenisation</a> framework describes payment tokens with domain controls and lifecycle functions. Its current specification page listed version 2.4, published 9 July 2026, at research time. Network payment tokens, a processor vault token, and an internal alias solve different problems; document which one is used and where.</p><p>The PCI Security Standards Council’s <a href="https://www.pcisecuritystandards.org/documents/Tokenization_Guidelines_Info_Supplement.pdf?ref=chahidarid.online">Tokenization Guidelines</a> explicitly state that tokenization does not replace PCI DSS requirements and that systems with access to PAN, tokenization components, and systems that can affect their security may remain in scope. The supplement dates from August 2011, so it should be read with the current standard and assessor guidance rather than treated as a complete modern scoping rule.</p><h2 id="draw-the-data-flow-before-drawing-the-boundary">Draw the data flow before drawing the boundary</h2><p>Inventory every path on which PAN can appear: browser or mobile capture, API request, memory, log, trace, queue, retry store, support tool, analytics export, backup, error report, and disaster-recovery environment. Mark token flows separately. Include administrators, deployment systems, secrets managers, vulnerability tooling, and network controls that can change the card-data environment.</p><p>A common lower-exposure design uses processor-hosted fields or a hosted payment page so PAN is submitted directly from the customer’s browser to a validated payment provider. The application receives a token. That can reduce exposure, but the page, scripts, redirects, and systems that can affect the payment form may still carry obligations. The exact self-assessment questionnaire and scope depend on the integration and acquiring relationship; obtain an assessor or acquirer determination rather than promising an outcome in architecture documentation.</p><p>PCI SSC identified <a href="https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1?ref=chahidarid.online">PCI DSS v4.0.1</a> as a limited revision and retained 31 March 2025 as the effective date for the future-dated requirements. Pin the version used by the assessment and verify current PCI SSC publications before release.</p><h2 id="isolate-the-token-service">Isolate the token service</h2><p>Place PAN capture, token generation, vault storage, and detokenization in a restricted card-data zone. Ordinary product services should accept only tokens and last-four/brand metadata needed for user experience. Enforce at multiple layers:</p><ul><li>separate network and identity boundaries;</li><li>least-privilege service identities, with no shared vault credential;</li><li>authenticated encryption and managed keys separated from stored ciphertext;</li><li>explicit allowlists for detokenization purpose and destination;</li><li>immutable audit records for vault administration and reveal operations;</li><li>PAN discovery scans on logs, object stores, queues, tickets, and data warehouses;</li><li>retention and deletion rules for PAN, tokens, mappings, and backups.</li></ul><p>Do not rely on developers remembering not to log a field. Reject PAN-shaped data at API boundaries that should be token-only, redact observability pipelines, and test error paths. Use synthetic test numbers outside production; never copy live card data into lower environments.</p><h2 id="make-tokens-domain-constrained">Make tokens domain-constrained</h2><p>A token valid everywhere becomes a bearer secret with a different shape. Bind tokens to a merchant, channel, device, transaction type, or processor where the token scheme supports it. Separate display aliases from payment credentials. Rotate and suspend tokens through an explicit lifecycle, and propagate lifecycle events without exposing PAN.</p><p>Token format also matters. A format-preserving token can pass legacy validation and accidentally enter fields assumed to contain PAN. Tag values with a token type and provider; never infer semantics solely from length or prefix. Keep the mapping between internal payment method identifiers and external tokens versioned and auditable.</p><h2 id="control-detokenization-as-an-exceptional-operation">Control detokenization as an exceptional operation</h2><p>Most application workflows should never reveal PAN. Where a business process genuinely requires it, require a narrowly scoped service identity, purpose code, destination restriction, rate and volume limit, approval for high-risk use, and complete audit trail. Return the minimum data for the shortest time and prevent it from entering logs or traces.</p><p>Monitor reveal attempts, denied purposes, unusual volume, new callers, vault administration, key access, PAN discovery findings, and token use outside its allowed domain. Include the vault and token provider in incident-response and business-continuity exercises.</p><h2 id="validate-the-claim-about-scope">Validate the claim about scope</h2><p>Test the architecture with data-flow review, code and configuration inspection, PAN discovery, segmentation testing, access review, and provider evidence. Reassess after changes to checkout scripts, observability, support tooling, processor routing, backups, or deployment privileges. A new “harmless” analytics integration can reconnect an otherwise separated environment.</p><p>Scope reduction is a conclusion reached from evidence by the organization, its acquirer, and where appropriate a Qualified Security Assessor—not a feature flag. Architecture should say “designed to minimize PAN exposure,” then record the formal determination separately.</p><h2 id="the-architecture-decision">The architecture decision</h2><p>Capture PAN in the smallest isolated zone, replace it with a clearly typed and domain-constrained token, block PAN at ordinary service boundaries, and make detokenization rare and observable. Tokenization is valuable because it reduces exposure and simplifies controls. It is dangerous when it becomes a slogan that hides remaining security dependencies. Design the complete flow, validate it against PCI DSS v4.0.1 and current guidance, and let evidence—not the presence of a token—determine scope.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/real-time-fraud/">Real-Time Fraud Decisions Under 100 Milliseconds</a></li><li><a href="https://chahidarid.online/en/open-banking-consent/">Open Banking Consent as a First-Class State Machine</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.pcisecuritystandards.org/documents/Tokenization_Guidelines_Info_Supplement.pdf?ref=chahidarid.online">PCI SSC — Tokenization Guidelines</a></li><li><a href="https://blog.pcisecuritystandards.org/just-published-pci-dss-v4-0-1?ref=chahidarid.online">PCI SSC — PCI DSS v4.0.1 publication notice</a></li><li><a href="https://www.emvco.com/emv-technologies/payment-tokenisation/?ref=chahidarid.online">EMVCo — EMV Payment Tokenisation</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Resilient Payment APIs: Timeouts, Retries, and Circuit Breakers]]></title>
            <description><![CDATA[Resilience policies must reflect whether an operation is safe to repeat and whether its outcome is known.]]></description>
            <link>https://chahidarid.online/en/resilient-payment-apis/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/resilient-payment-apis/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:33 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/resilient-payment-apis.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>In a payment API, a timeout means “the caller does not know,” not “the payment failed.” My design rule is to separate transport recovery from financial truth and give every operation a durable identity that can be queried before repetition. The checkout cases below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/resilient-payment-apis.png?v=20260721-motion-v2" class="kg-image" alt="Payment API decision flow for safe retry, status query, circuit breaking, callbacks, and reconciliation" loading="lazy" width="1600" height="1280"><figcaption>Figure 11. Retry only when method semantics and operation state make repetition safe; route unknown outcomes to status recovery rather than a blind second charge.</figcaption></figure><h2 id="classify-the-operation-before-choosing-a-retry-policy">Classify the operation before choosing a retry policy</h2><p>HTTP method names are only a starting point. <a href="https://www.rfc-editor.org/rfc/rfc9110.html?ref=chahidarid.online#section-9.2.2">RFC 9110 section 9.2.2</a> defines idempotent methods by their intended effect and says clients should not automatically retry a non-idempotent request unless they know its semantics are idempotent or can detect that the original was not applied.</p><p>A balance query is normally safe to repeat. Creating a payment is not safe unless the API binds repeated attempts to the same durable operation. A capture or refund may be conditionally repeatable with an idempotency key and state transition guard. A payout routed to an external rail may remain unknown until a later status message arrives.</p><p>Publish an operation matrix for every endpoint: side effect, idempotency scope, retryable failures, non-retryable failures, unknown outcome, status-query path, callback, and maximum decision age. This makes resilience a domain contract rather than a generic HTTP middleware setting.</p><h2 id="use-one-deadline-and-bounded-attempts">Use one deadline and bounded attempts</h2><p>Set an end-to-end deadline at the caller and pass the remaining budget downstream. Connection, TLS, request, and response timeouts must fit inside it. A timeout should be based on measured latency distributions and business tolerance, not a round number copied between services.</p><p>The Amazon Builders’ Library article on <a href="https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?ref=chahidarid.online">timeouts, retries, backoff, and jitter</a> explains why retries can amplify overload and why exponential backoff, jitter, and retry budgets are needed. Apply those techniques at one controlled layer. If a client, gateway, service, and SDK each retry three times, one request can multiply into dozens of provider calls.</p><p>Limit attempts by both count and deadline. Respect provider rate limits. Use jitter to avoid synchronized retry storms. Do not retry validation failures, authentication failures, or explicit business declines. For a timeout after request transmission, prefer querying the operation by its internal and provider references.</p><h2 id="make-unknown-a-first-class-state">Make unknown a first-class state</h2><p>Return a durable <code>operation_id</code> as early as possible. Persist the request fingerprint, caller, state, and provider attempt before initiating an external effect. If the response is lost after the provider accepts the payment, a repeated request with the same idempotency key should return the existing operation, not create another.</p><p>Model at least <code>received</code>, <code>processing</code>, <code>succeeded</code>, <code>failed_definitive</code>, and <code>unknown_external</code>. The last state is not an error bucket. It has an owner, a next query time, a maximum age, and a recovery route through provider status, webhook, reconciliation file, or manual investigation.</p><p>Callbacks are evidence, not magic. Verify signatures, store the raw event, acknowledge quickly, deduplicate delivery, and apply an allowed state transition. A late “succeeded” event must be able to resolve <code>unknown_external</code>; it must not overwrite a terminal refund or another payment’s state.</p><h2 id="place-the-circuit-breaker-at-the-provider-boundary">Place the circuit breaker at the provider boundary</h2><p>A circuit breaker protects callers and a struggling dependency; it does not decide whether money moved. The <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/circuit-breaker.html?ref=chahidarid.online">AWS circuit-breaker pattern</a> describes closed, open, and half-open states with controlled recovery probes.</p><p>Implement the breaker in the provider adapter, scoped so one failing provider or operation does not disable unrelated traffic. When open, reject or queue new work according to product semantics. Do not reroute an in-flight unknown payment to another provider. A fallback is safe only before any provider may have accepted the effect, or after the first outcome is definitively negative.</p><p>Expose breaker state, rejected calls, probe results, and the age of unknown operations. Operational dashboards must separate transport health from financial completion.</p><h2 id="design-the-api-response-for-recovery">Design the API response for recovery</h2><p>For synchronous success, return the operation identifier, stable state, and provider reference when disclosure is appropriate. For accepted asynchronous work, use an explicit processing response with a status URL and retry guidance. For unknown outcomes, state that finality is pending; do not return a generic failure that invites the user to pay again.</p><p>Status queries should be strongly tied to the original caller and protected from enumeration. They should expose monotonic financial facts while allowing operational metadata to evolve. If a previous response is replayed for an idempotency key, make that behavior observable in headers or metadata.</p><h2 id="test-failure-at-each-boundary">Test failure at each boundary</h2><p>Inject loss before connect, after connect, after request bytes are sent, after provider commit, and after your database commit. Test duplicate callbacks, out-of-order events, a breaker opening during a retry, provider recovery in half-open state, exhausted deadlines, and an operation that remains unknown beyond its service target.</p><p>Assert the number of external effects, not only HTTP responses. Monitor attempts per operation, retry amplification, deadline exhaustion, breaker state, duplicate-effect prevention, unknown-outcome age, status-query success, and reconciliation breaks.</p><h2 id="the-architecture-decision">The architecture decision</h2><p>Build resilience around operation semantics. Bound time with one propagated deadline, retry at one layer with backoff and jitter, guard effects with durable idempotency, place circuit breakers at dependency boundaries, and represent uncertainty explicitly. The safest payment API is not the one that retries most aggressively; it is the one that can tell a caller whether to wait, query, retry, or stop without risking a second financial effect.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/real-time-fraud/">Real-Time Fraud Decisions Under 100 Milliseconds</a></li><li><a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model, Not an XML Problem</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.rfc-editor.org/rfc/rfc9110.html?ref=chahidarid.online#section-9.2.2">IETF — RFC 9110 §9.2.2, Idempotent Methods</a></li><li><a href="https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?ref=chahidarid.online">Amazon Builders’ Library — Timeouts, retries, and backoff with jitter</a></li><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/circuit-breaker.html?ref=chahidarid.online">AWS — Circuit breaker pattern</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Modernizing Core Banking Without a Big-Bang Rewrite]]></title>
            <description><![CDATA[A strangler program succeeds when capabilities, data ownership, and migration checkpoints are explicit.]]></description>
            <link>https://chahidarid.online/en/core-banking-modernization/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/core-banking-modernization/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:27 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/core-banking-modernization.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>Core-banking modernization succeeds when it moves one owned capability at a time, preserves financial truth, and makes every migration checkpoint reversible. I start with the critical operation, not a fashionable target architecture; the migration examples below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/core-banking-modernization.png?v=20260721-motion-v2" class="kg-image" alt="Strangler modernization architecture with channel facade, legacy core, anti-corruption layer, modern capability, migration controls, and rollback checkpoints" loading="lazy" width="1600" height="1160"><figcaption>Figure 10. A routing façade shifts one capability at a time while explicit ownership, comparison, and rollback controls prevent two writable truths.</figcaption></figure><h2 id="define-the-critical-operation-before-the-target-architecture">Define the critical operation before the target architecture</h2><p>Begin with the customer or business operation that must remain available: posting a transfer, calculating interest, servicing a loan, or producing a statement. Map its data, batch dependencies, downstream reports, manual controls, and recovery objectives. The Basel Committee’s <a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Principles for Operational Resilience</a> call for identifying critical operations, mapping dependencies, setting tolerances for disruption, and testing the ability to stay within them. Those principles apply directly to banks; they are also a disciplined frame for any regulated modernization.</p><p>A service decomposition is not the objective. The objective is to change a capability without losing balances, contractual behavior, audit evidence, or recoverability. A modular monolith may be a better destination than microservices if it gives clear ownership with lower operational cost.</p><h2 id="choose-a-bounded-migration-slice">Choose a bounded migration slice</h2><p>The <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/strangler-fig.html?ref=chahidarid.online">AWS strangler-fig pattern</a> describes routing requests through a façade while new components incrementally replace old functionality. Its value is controlled scope, not a guarantee of safety.</p><p>Select a slice with a coherent business boundary and measurable exit criteria. “Customer profile” can still be unsafe if dozens of legacy jobs update it. “Read-only statement history for accounts opened after a cutoff date” is often easier to own. Document:</p><ul><li>commands and queries in scope;</li><li>source of record for every field and business date;</li><li>allowed writers during each phase;</li><li>reconciliation and acceptance thresholds;</li><li>rollback procedure and maximum rollback age;</li><li>legacy dependencies that must be removed before completion.</li></ul><p>Never leave “temporary” dual write as an architectural assumption. Two systems accepting independent updates create conflicts that timestamps cannot reliably solve. Prefer one authoritative writer, with changes propagated through a transactional outbox or controlled replication. If dual write is unavoidable for a short phase, define conflict ownership, failure handling, and a hard removal date.</p><h2 id="isolate-legacy-semantics">Isolate legacy semantics</h2><p>Legacy interfaces often encode product rules in field names, batch timing, or error codes. Do not spread those semantics into every new service. An <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html?ref=chahidarid.online">anti-corruption layer</a> translates between the legacy model and the modern domain boundary.</p><p>The layer should be intentionally narrow. It validates requests, maps identifiers and values, preserves the original reference, and exposes uncertainty rather than inventing success. Version mappings and test them against recorded, anonymized or synthetic cases. If it grows into a second core containing business policy, the migration has merely moved the monolith.</p><h2 id="separate-data-movement-from-authority-transfer">Separate data movement from authority transfer</h2><p>Copying data does not make the destination authoritative. Treat migration as distinct stages:</p><ol><li><strong>baseline:</strong> inventory and checksum the source population;</li><li><strong>backfill:</strong> copy historical data with transformation lineage;</li><li><strong>catch-up:</strong> apply ordered changes after the baseline point;</li><li><strong>shadow read:</strong> compare outputs without serving customers;</li><li><strong>controlled cutover:</strong> route a defined cohort or capability to the new owner;</li><li><strong>stabilize:</strong> monitor, reconcile, and retain a tested rollback path;</li><li><strong>decommission:</strong> remove legacy writers, jobs, credentials, and data copies.</li></ol><p>Change-data capture can support catch-up, but it does not explain business meaning. Deletes, out-of-order records, schema changes, and updates performed outside the captured log all need handling. Record a migration watermark and prove that every source change before it is present once in the destination.</p><p>For financial records, compare more than row counts. Reconcile opening and closing balances, transaction counts and amounts by currency, posting dates, account states, and independently computed control totals. Differences become owned cases; they are not automatically “fixed” by copying the latest value.</p><h2 id="make-routing-and-rollback-observable">Make routing and rollback observable</h2><p>The façade should emit the capability, cohort, chosen backend, rule version, operation identifier, latency, and outcome. A feature flag alone is not a migration control. Routing changes need approval, staged rollout, and an auditable history.</p><p>Define rollback before cutover. If the new system has accepted writes, switching traffic back may require replaying those facts into the legacy system or choosing forward recovery instead. A rollback plan that says “turn off the flag” is valid only while the legacy side remains complete and compatible.</p><p>Test failure during backfill, lag in replication, a poison record, schema drift, legacy batch overlap, a partial cohort cutover, and loss of the façade. Rehearse with production-shaped synthetic data and a clock that crosses end-of-day and month-end boundaries.</p><h2 id="know-when-the-slice-is-finished">Know when the slice is finished</h2><p>A capability is not modernized while the legacy system still computes its results, repairs its data, or remains the only recovery path. Exit criteria should include no legacy writers, reconciled history, operational runbooks, demonstrated recovery, performance within objectives, trained support teams, retired credentials, and a retention decision for old data.</p><p>Track business outcomes: change lead time, incidents, reconciliation breaks, recovery time, manual work, and cost per operation. Counting new services rewards movement rather than improvement.</p><h2 id="the-modernization-decision">The modernization decision</h2><p>Use a façade to control traffic, an anti-corruption layer to contain old semantics, one writer to preserve authority, and explicit checkpoints to move data and responsibility separately. Modernization is complete only when a bounded capability has one clear owner and the obsolete path can be removed. The safest program is not the one with the most ambitious target diagram; it is the one that can explain and reverse each step without compromising financial truth.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model, Not an XML Problem</a></li><li><a href="https://chahidarid.online/en/real-time-fraud/">Real-Time Fraud Decisions Under 100 Milliseconds</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/strangler-fig.html?ref=chahidarid.online">AWS — Strangler fig pattern</a></li><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html?ref=chahidarid.online">AWS — Anti-corruption layer pattern</a></li><li><a href="https://www.bis.org/bcbs/publ/d516.htm?ref=chahidarid.online">Basel Committee — Principles for operational resilience</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reconciliation by Design, Not by Spreadsheet]]></title>
            <description><![CDATA[Reconciliation is a continuous comparison of independently produced financial facts, with explainable tolerances and ownership.]]></description>
            <link>https://chahidarid.online/en/reconciliation-by-design/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/reconciliation-by-design/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:21 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/reconciliation-by-design.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>Reconciliation is not a month-end spreadsheet exercise. I design it as a control system that compares independently produced financial facts, proves completeness, classifies differences, and routes exceptions to accountable owners without erasing evidence. The settlement cases below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/reconciliation-by-design.png?v=20260721-motion-v2" class="kg-image" alt="Reconciliation control loop from independent sources through normalization, matching, exceptions, and controlled adjustment" loading="lazy" width="1600" height="1320"><figcaption>Figure 09. A reconciliation pipeline proves source completeness before matching, keeps exceptions visible, and feeds approved corrections back as new ledger entries.</figcaption></figure><h2 id="independence-is-the-point">Independence is the point</h2><p>Comparing a balance projection with another projection built from the same tables can reproduce the same defect twice. A meaningful control compares records created through independent paths: for example, an internal ledger against a processor settlement file, a bank statement, or a scheme report.</p><p>The principle is consistent with <a href="https://www.bis.org/publ/bcbs239.htm?ref=chahidarid.online">BCBS 239</a>, which emphasizes accuracy, completeness, timeliness, adaptability, and traceability in risk-data aggregation. BCBS 239 directly applies to banks’ risk-data capabilities, not to every payment company; here it is a rigorous analogue for designing financial-data controls. The Basel Committee’s <a href="https://www.bis.org/publ/bcbs_nl36.htm?ref=chahidarid.online">January 2026 implementation newsletter</a> also identifies fragmented data estates, weak lineage, and manual processes as continuing obstacles.</p><h2 id="prove-completeness-before-comparing-values">Prove completeness before comparing values</h2><p>A matching engine cannot detect a missing file it never expected. Begin each run with a source manifest:</p><ul><li>source, business date, timezone, and cutoff;</li><li>file or API page sequence and expected count;</li><li>record count, control total, and cryptographic checksum where available;</li><li>schema and producer version;</li><li>ingestion start, completion, and retry history.</li></ul><p>Quarantine malformed or incomplete deliveries. Do not mark a business date reconciled because the records received happen to match. A green result requires evidence that the population itself is complete.</p><p>Normalize without destroying provenance. Convert external identifiers, signs, currencies, decimals, and timestamps into a comparison model, but retain the raw source reference and transformation version. ISO 20022’s <a href="https://www.iso20022.org/standardsrepository?ref=chahidarid.online">standards repository</a> provides official payment and cash-management message definitions; a local implementation must still pin the message version and market-practice profile it accepts.</p><h2 id="matching-is-a-sequence-of-explainable-rules">Matching is a sequence of explainable rules</h2><p>Start with deterministic exact matches using stable references, amount, currency, and expected date. Then apply controlled rules for known differences:</p><ol><li><strong>one-to-one:</strong> one ledger movement equals one external movement;</li><li><strong>one-to-many:</strong> one settlement deposit corresponds to many captured payments;</li><li><strong>many-to-one:</strong> split internal postings correspond to one provider record;</li><li><strong>timing:</strong> both facts are valid but fall on different business dates;</li><li><strong>value:</strong> amounts differ because of fees, foreign exchange, rounding, or an error;</li><li><strong>missing/duplicate:</strong> a fact exists on one side only or appears more than once.</li></ol><p>Every rule needs an identifier, version, effective date, owner, priority, and test set. Record which rule produced a match and the evidence it consumed. Tolerances must be justified by currency, rail, or contract; “less than 1” is not a safe global rule. Automatic write-offs need separately approved thresholds and aggregate monitoring so many small differences cannot hide a material loss.</p><h2 id="exceptions-are-products-not-leftovers">Exceptions are products, not leftovers</h2><p>An unmatched record should become a durable case with a reason category, amount at risk, age, owner, service-level target, and links to both source facts. Distinguish “awaiting expected late file” from “unknown value difference” and “suspected duplicate.” That lets operations prioritize risk instead of working a flat queue.</p><p>Investigation notes and evidence belong to the case. If an adjustment is approved, post a new balanced ledger transaction with the case identifier, reason code, approver, and effective date. Never edit the original entry to force a match. Re-run the comparison after the correction and retain both the pre- and post-adjustment results.</p><p>Ageing matters as much as counts. Monitor unreconciled value and item count by reason and age bucket, source completeness failures, late deliveries, tolerance usage, manual-adjustment volume, reopened cases, and the percentage matched by each rule version. A falling exception count can be bad news if a broad new rule is swallowing genuine breaks.</p><h2 id="design-the-operating-boundary">Design the operating boundary</h2><p>The ledger owns internal postings. The ingestion layer proves and stores external evidence. The reconciliation service compares normalized facts and records decisions; it does not silently create money movements. The case workflow owns investigation and approvals. A posting service executes approved adjustments through the same ledger controls as any other financial transaction.</p><p>This separation makes replay safe. If a parser or matching rule changes, rebuild normalized facts and match decisions from immutable source snapshots. Compare old and new results before promoting the rule. Do not re-download a mutable provider endpoint and assume it represents what was originally received.</p><h2 id="test-the-control-not-only-the-code">Test the control, not only the code</h2><p>Use synthetic scenarios for missing files, truncated pages, duplicate records, daylight-saving cutoffs, one-to-many settlements, partial refunds, fee netting, currency precision, late reversals, and provider corrections. Inject a defect into a complete dataset and verify that the expected exception is created, aged, approved, adjusted, and closed with evidence.</p><p>Also test segregation of duties: the person who changes a matching rule should not approve the breaks it automatically resolves. Access to raw account data, case evidence, and adjustment actions should be independently authorized and logged.</p><h2 id="the-design-decision">The design decision</h2><p>Reconciliation is a continuous, evidence-preserving control loop. Prove the population, normalize with lineage, match through versioned rules, classify every difference, and correct through controlled new entries. A spreadsheet can support investigation, but it cannot be the authoritative workflow. When reconciliation is designed into the platform, every green result explains what was compared, which rules were used, and who accepted the remaining risk.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model, Not an XML Problem</a></li><li><a href="https://chahidarid.online/en/double-entry-ledger/">Double-Entry Ledger Architecture for Product Engineers</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.bis.org/publ/bcbs239.htm?ref=chahidarid.online">Basel Committee — Principles for effective risk data aggregation and risk reporting</a></li><li><a href="https://www.bis.org/publ/bcbs_nl36.htm?ref=chahidarid.online">Basel Committee — BCBS 239 implementation newsletter, January 2026</a></li><li><a href="https://www.iso20022.org/standardsrepository?ref=chahidarid.online">ISO 20022 — Standards repository</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Card Authorization Lifecycle Engineers Actually Need]]></title>
            <description><![CDATA[Authorization, clearing, reversal, and dispute are related but independent financial events with different clocks.]]></description>
            <link>https://chahidarid.online/en/card-authorization-lifecycle/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/card-authorization-lifecycle/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:14 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/card-authorization-lifecycle.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>A card payment is not one transaction moving neatly from “pending” to “complete.” It is a set of related financial events—authorization, presentment, clearing, settlement, reversal, refund, and sometimes dispute—created by different actors on different clocks. My design rule is to correlate those events without collapsing them into one mutable status; the purchase scenarios below are synthetic.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/card-authorization-lifecycle.png?v=20260721-motion-v2" class="kg-image" alt="Sequence diagram separating card authorization, presentment, reversal, settlement, and dispute events" loading="lazy" width="1600" height="1320"><figcaption>Figure 08. Authorization is a real-time round trip; clearing, settlement, reversals, and disputes arrive later and must remain separately identifiable.</figcaption></figure><h2 id="start-with-two-timelines-not-one-status">Start with two timelines, not one status</h2><p>At authorization time, the merchant sends an amount and card context through its acquirer and the card network to the issuer. The issuer approves or declines and returns identifiers that help later messages refer to the decision. Approval normally causes the issuer to reserve available funds; it does not prove that money has settled to the merchant.</p><p>Presentment happens later, commonly in a batch. Its amount can differ legitimately from the authorization because of tips, estimated amounts, partial fulfilment, currency conversion, or multiple shipments. Clearing calculates obligations among participants; settlement moves funds according to the network arrangement. A refund is a new credit transaction after capture, while a reversal tells the issuer that an authorization is no longer needed. Those are not interchangeable operations.</p><p>That distinction matters to customers. A stale authorization can leave funds unavailable even though the merchant will never capture them. Visa’s merchant guidance explains that an <a href="https://usa.visa.com/dam/VCOM/download/merchants/authorization-reversals.pdf?ref=chahidarid.online">authorization reversal</a> should carry matching transaction information so the issuer can identify and release the hold. Exact fields and deadlines vary by network, region, merchant category, and processor contract; the guidance is an example, not a universal clock.</p><p>Available balance and ledger balance should therefore remain separate concepts. The issuer may expose a pending hold immediately while the settled ledger changes later. On the merchant side, an approved authorization is not yet revenue or cash. Product screens may simplify these views, but their labels and support tools must preserve the distinction.</p><h2 id="model-correlation-without-collapsing-events">Model correlation without collapsing events</h2><p>Use a stable internal <code>payment_id</code>, but give every network-facing event its own identifier and immutable record. A practical model separates:</p><ul><li><code>authorization</code>: requested amount, approved amount, decision, network references, expiry policy;</li><li><code>presentment</code>: presentment identifier, related authorization, amount, currency, sequence, received time;</li><li><code>reversal</code>: full or partial amount, reason, related authorization, acknowledgement state;</li><li><code>refund</code>: a new movement linked to the captured payment, with its own lifecycle;</li><li><code>dispute</code>: case identifier, disputed amount, condition, evidence deadline, and outcome.</li></ul><p>Store both event time and ingestion time. The former says when the network says something happened; the latter explains when your platform learned it. Never overwrite an authorization amount with a clearing amount merely to make a single row “current.” Derive a customer-facing projection from the event history and retain the facts required to rebuild it.</p><p>The same rule applies to state. <code>authorized</code>, <code>partially_presented</code>, <code>fully_presented</code>, <code>reversed</code>, <code>expired</code>, and <code>disputed</code> may coexist across related records. A payment aggregate can summarize them, but its state machine must reject impossible transitions and expose uncertainty when a provider outcome is unknown.</p><h2 id="design-for-asynchronous-and-imperfect-delivery">Design for asynchronous and imperfect delivery</h2><p>Processors frequently report later changes through webhooks. Stripe’s official <a href="https://docs.stripe.com/webhooks?ref=chahidarid.online">webhook guidance</a> documents duplicate delivery, asynchronous retries, and the absence of ordering guarantees. Treat that as a representative operational constraint, not as a rule for every processor.</p><p>Verify signatures, persist the raw notification, acknowledge quickly, and process from a queue. Deduplicate by the provider’s event identifier, while also making the domain handler idempotent. An event saying “payment updated” is a notification to query or apply a specific change; it is not automatically an accounting posting.</p><p>Unknown outcomes deserve an explicit state. If an authorization request times out, do not immediately route it to another acquirer: the first issuer may already have approved it. Query using the provider reference or wait for a definitive callback. Escalate aged unknowns to reconciliation instead of guessing.</p><h2 id="treat-disputes-as-a-separate-case-workflow">Treat disputes as a separate case workflow</h2><p>A dispute can begin weeks or months after authorization and settlement. It has reason-specific evidence, response deadlines, provisional financial effects, and an outcome. Visa’s <a href="https://by.visa.com/dam/VCOM/global/support-legal/documents/merchants-dispute-management-guidelines.pdf?ref=chahidarid.online">Dispute Management Guidelines for Merchants, June 2024</a> illustrate why a dispute is not simply a terminal payment status.</p><p>Keep case management separate from the payment state machine. Link the case to the original authorization and presentment, then post fees, provisional credits, reversals, or final losses as distinct ledger transactions. This preserves both the operational history and the accounting explanation.</p><h2 id="controls-engineers-can-test">Controls engineers can test</h2><p>Test more than the happy path:</p><ol><li>an approved authorization followed by no presentment and an explicit reversal;</li><li>one authorization followed by two partial presentments;</li><li>presentment received before a delayed authorization notification;</li><li>duplicate reversal and dispute webhooks;</li><li>a timeout followed by a late approval;</li><li>refund requested during an open dispute;</li><li>currencies or amounts that do not match within the permitted scheme rules.</li></ol><p>For every test, assert the hold projection, customer-visible status, ledger entries, reconciliation result, and audit trail. Monitor authorization-to-presentment age, unmatched presentments, unreleased holds, unknown outcomes, duplicate notifications, and dispute deadlines.</p><h2 id="the-architecture-decision">The architecture decision</h2><p>Do not build a card platform around one mutable <code>payment_status</code>. Build it around correlated, immutable events with explicit ownership and separate clocks. The aggregate is useful for product experiences, but authorization, presentment, reversal, refund, settlement, and dispute must remain independently explainable. That is what lets support answer a customer, finance reconcile a processor, and engineering recover safely when messages arrive late or twice.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/double-entry-ledger/">Double-Entry Ledger Architecture for Product Engineers</a></li><li><a href="https://chahidarid.online/en/iso-20022/">ISO 20022 as a Domain Model, Not an XML Problem</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://usa.visa.com/dam/VCOM/download/merchants/authorization-reversals.pdf?ref=chahidarid.online">Visa — Authorization Reversals</a></li><li><a href="https://by.visa.com/dam/VCOM/global/support-legal/documents/merchants-dispute-management-guidelines.pdf?ref=chahidarid.online">Visa — Dispute Management Guidelines for Visa Merchants, June 2024</a></li><li><a href="https://docs.stripe.com/webhooks?ref=chahidarid.online">Stripe — Webhooks</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Open Banking Consent as a First-Class State Machine]]></title>
            <description><![CDATA[Consent is a time-bound authorization with scope, actor, purpose, and revocation—not a boolean flag.]]></description>
            <link>https://chahidarid.online/en/open-banking-consent/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/open-banking-consent/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:08 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/open-banking-consent.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start with the authorization the user actually granted: scope, purpose, actors, time, and terminal states. I do not reduce open-banking consent to <code>customer_consented = true</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/open-banking-consent.png?v=20260721-motion-v2" class="kg-image" alt="Open banking consent state machine linked to OAuth tokens and resource access" loading="lazy" width="1600" height="1400"><figcaption>Figure 07. Consent has its own lifecycle; OAuth tokens enforce access but do not replace the underlying user authorization record.</figcaption></figure><h2 id="state-the-jurisdiction-before-the-schema">State the jurisdiction before the schema</h2><p>Consent semantics differ across UK Open Banking, EU payment-services rules, UAE Open Finance, and other ecosystems. This article uses the UK Open Banking Implementation Entity (OBIE) Read/Write API v3.1.2 as a concrete engineering example; it is not a universal legal model.</p><p>The OBIE <a href="https://openbankinguk.github.io/read-write-api-site2/standards/v3.1.2/resources-and-data-models/aisp/account-access-consents?ref=chahidarid.online">Account Access Consents specification</a> defines a consent resource with permissions, creation and status-update timestamps, optional expiration, and states including <code>AwaitingAuthorisation</code>, <code>Authorised</code>, <code>Rejected</code>, and <code>Revoked</code>. It also defines POST, GET, and DELETE behavior. The related <a href="https://openbankinguk.github.io/read-write-api-site2/standards/v3.1.2/profiles/read-write-data-api-profile?ref=chahidarid.online">Read/Write API Profile</a> explains that an intent carries fine-grained permissions beyond coarse OAuth scopes.</p><p>Those versioned specifications support the architecture below. They do not settle what counts as legally valid consent in another jurisdiction; product, compliance, and legal owners must map the applicable rules.</p><h2 id="consent-authorization-and-tokens-are-different-objects">Consent, authorization, and tokens are different objects</h2><p>A third-party provider can create an account-access consent describing requested permissions and expiry. The bank authenticates the payment-service user and asks them to authorize that intent. Only then does the consent become <code>Authorised</code>. An OAuth authorization code and resulting access token let software exercise the authorization.</p><p>Here is how I would keep the boundaries visible:</p><ul><li><strong>Consent resource:</strong> who requested what, for which purpose and accounts, under which policy/version, and in which lifecycle state.</li><li><strong>Authorization session:</strong> evidence of user authentication, disclosure presented, selections, and the authorization result.</li><li><strong>Access/refresh tokens:</strong> technical credentials with audience, scope, sender constraint, and expiry.</li><li><strong>Resource server decision:</strong> evaluates the current consent, token, account entitlement, and policy for each request.</li></ul><p>A token can expire while a long-lived consent remains authorized; OBIE v3.1.2 explicitly describes this separation. Conversely, revocation should make further resource access fail even if a token’s cryptographic expiry is later.</p><h2 id="use-a-real-state-machine">Use a real state machine</h2><p>Create the consent in <code>AwaitingAuthorisation</code>. From there, a successful user journey moves it to <code>Authorised</code>; rejection moves it to <code>Rejected</code>; cancellation or timeout follows the adopted profile. <code>Rejected</code> and <code>Revoked</code> are terminal in the cited OBIE profile. Never move a revoked record back to authorized; create a new consent with new evidence.</p><p>Make transitions conditional and atomic. Store <code>status</code>, <code>status_updated_at</code>, <code>version</code>, and an append-only transition log. A concurrent revocation and data-access request must resolve against a declared consistency rule. For high-value access, the authorization decision should read current consent state strongly enough that a completed revocation cannot be ignored by a stale cache.</p><p>Consent expiry is a scheduled transition or an evaluated condition, not a nightly cleanup detail. Record the exact instant and time zone, and test access at the boundary.</p><h2 id="model-scope-as-data-not-prose">Model scope as data, not prose</h2><p>Permissions need machine-enforceable identifiers. Bind the consent to the authorized accounts or selection rule, data clusters, transaction-history window, expiry, requesting party, bank, and user. Store the disclosure/policy version and a hash or immutable reference to what was shown.</p><p>Purpose can be legally significant but difficult to enforce technically. Keep it explicit and use it in downstream policy, monitoring, and audit. Do not assume an OAuth scope such as <code>accounts</code> captures account selection, date limits, or purpose.</p><p>For security-sensitive financial APIs, the OpenID Foundation’s <a href="https://openid.net/specs/fapi-security-profile-2_0-final.html?ref=chahidarid.online">FAPI 2.0 Security Profile</a> defines protections such as sender-constrained access tokens. It reached Final status on 20 February 2025. Adoption still depends on the ecosystem profile; cite the version actually implemented.</p><h2 id="revocation-is-an-end-to-end-operation">Revocation is an end-to-end operation</h2><p>Revocation must update the consent authority, invalidate or block tokens as required, clear caches, stop scheduled collection, and propagate to downstream data products. The OBIE consent specification says that when a user revokes through the third party, that party calls DELETE on the consent resource before confirming revocation.</p><p>Keep evidence of the revoked resource and transition even if the API presents deletion semantics. Data already collected has a separate retention and deletion policy; revoking future access does not automatically define how prior records must be handled.</p><h2 id="test-the-uncomfortable-cases">Test the uncomfortable cases</h2><p>Exercise authorization abandonment, user rejection, partial account selection, consent expiry during token refresh, revocation concurrent with data retrieval, duplicate callbacks, clock skew, third-party certificate rotation, and a downstream job running from cached permission data.</p><p>An auditor should be able to answer: what was requested, what was shown, who authorized it, which accounts and data were included, which policy applied, when access occurred, and why it stopped. Do not log raw credentials or unnecessary personal data to achieve that evidence.</p><p>Operational measures include authorization completion, rejection, revocation propagation time, access denied by reason, expired-consent use attempts, stale-cache detections, and downstream jobs stopped after revocation.</p><h2 id="the-engineering-rule">The engineering rule</h2><p>My design rule is to make consent a first-class domain aggregate and let tokens reference it. I evaluate both on protected access, preserve transition evidence, and qualify the model by jurisdiction and specification version. A boolean can say “yes”; it cannot explain what was authorized or which system must stop when the answer becomes “no.”</p><p>Before launch, walk one consent through authorization, data access, token expiry, re-authentication, user revocation at each channel, and downstream deletion or retention handling. Confirm that policy and customer wording match the implemented state machine. Repeat the review when the jurisdiction, profile version, requested data cluster, or third-party role changes; those changes can alter both the contract and the evidence required.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/double-entry-ledger/">Double-Entry Ledger Architecture for Product Engineers</a></li><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://openbankinguk.github.io/read-write-api-site2/standards/v3.1.2/resources-and-data-models/aisp/account-access-consents?ref=chahidarid.online">OBIE: Account Access Consents v3.1.2</a></li><li><a href="https://openbankinguk.github.io/read-write-api-site2/standards/v3.1.2/profiles/read-write-data-api-profile?ref=chahidarid.online">OBIE: Read/Write Data API Profile v3.1.2</a></li><li><a href="https://openid.net/specs/fapi-security-profile-2_0-final.html?ref=chahidarid.online">OpenID Foundation: FAPI 2.0 Security Profile—Final</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Real-Time Fraud Decisions Under 100 Milliseconds]]></title>
            <description><![CDATA[Low-latency fraud systems separate synchronous decision features from slower enrichment and investigation workflows.]]></description>
            <link>https://chahidarid.online/en/real-time-fraud/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/real-time-fraud/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:30:02 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/real-time-fraud.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start a real-time fraud design by defining the stopwatch and the cost of a wrong decision. A sub-100-millisecond target is useful only when its boundary, percentile, degraded mode, and loss trade-off are explicit.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/real-time-fraud.png?v=20260721-motion-v2" class="kg-image" alt="Parallel fraud rules and model scoring within a latency budget and asynchronous feedback loop" loading="lazy" width="1600" height="1140"><figcaption>Figure 06. Synchronous features, rules, and model scoring fit inside a declared p99 budget; enrichment, review, labels, and retraining remain asynchronous.</figcaption></figure><h2 id="define-what-the-stopwatch-measures">Define what the stopwatch measures</h2><p>“The model responds in 12 ms” says little about an authorization decision. A useful service-level objective might be: <strong>p99 under 100 ms from the risk API receiving a complete request to returning approve, decline, or step-up, measured in one region at a stated throughput</strong>. Network travel to the merchant, 3-D Secure interaction, issuer processing, and processor latency are outside that boundary and must be reported separately.</p><p>There is no universal 100 ms fraud standard. It is an architectural budget. Publish p50, p95, and p99, workload, geography, and measurement point. Otherwise a precise title creates false confidence.</p><p>EMVCo’s official <a href="https://www.emvco.com/emv-technologies/3-d-secure/?ref=chahidarid.online">3-D Secure material</a> shows the broader risk-based context: lower-risk transactions may follow a frictionless flow, while higher-risk transactions can require additional authentication. The current protocol details are versioned in the <a href="https://www.emvco.com/whitepapers/emv-3-d-secure-whitepaper/3-d-secure-documentation/3-d-secure-specification/?ref=chahidarid.online">3-D Secure specification hub</a>. A platform must state which 3DS version and market rules it implements.</p><h2 id="build-the-budget-backwards">Build the budget backwards</h2><p>Here is how I would sketch a synthetic 100 ms internal p99 budget:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Stage</th>
<th style="text-align:right">p99 budget</th>
</tr>
</thead>
<tbody>
<tr>
<td>Request validation and identity</td>
<td style="text-align:right">5 ms</td>
</tr>
<tr>
<td>Online feature retrieval</td>
<td style="text-align:right">25 ms</td>
</tr>
<tr>
<td>Rules and model in parallel</td>
<td style="text-align:right">25 ms</td>
</tr>
<tr>
<td>Policy combination and reason codes</td>
<td style="text-align:right">10 ms</td>
</tr>
<tr>
<td>Resilience reserve</td>
<td style="text-align:right">35 ms</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>These numbers are synthetic, not a benchmark. The reserve matters because tail latency compounds across dependencies. The service should avoid synchronous calls to a data warehouse, case-management tool, or third-party enrichment that cannot meet the same budget.</p><h2 id="split-features-by-freshness-and-consequence">Split features by freshness and consequence</h2><p>Compute stable attributes—merchant history aggregates, known device relationships, account tenure—offline or through streaming pipelines into a low-latency online store. Compute request-local signals such as amount deviation and device velocity at decision time. Every feature needs an owner, definition, freshness timestamp, default behavior, and training/serving consistency test.</p><p>Missing data is not zero. If device history is unavailable, expose <code>missing</code> and the cause. Models trained on complete data can fail silently when the online store degrades. Monitor feature age and missingness by segment, not only overall.</p><p>Rules and model scores often run in parallel. A deterministic sanctions or blocked-credential rule may override the model. A model score may select approve, decline, or step-up thresholds by product and risk appetite. Record rule version, model version, feature-set version, threshold policy, and reason codes with each decision.</p><h2 id="design-degradation-before-the-outage">Design degradation before the outage</h2><p>When the feature store times out, “fail open” and “fail closed” are both too crude. Define a tiered policy:</p><ul><li>low-value, established-customer traffic may use a conservative reduced feature set;</li><li>high-risk operations may step up authentication;</li><li>materially exposed operations may decline or queue when the product permits;</li><li>the platform may reduce traffic or disable an unstable model version.</li></ul><p>The policy should be approved, tested, and observable. <a href="https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.100-1.pdf?ref=chahidarid.online">NIST AI RMF 1.0</a> frames AI risk work across Govern, Map, Measure, and Manage; it is voluntary guidance, but its lifecycle approach is useful for documenting owners, testing, monitoring, and response.</p><h2 id="optimize-expected-loss-not-a-single-metric">Optimize expected loss, not a single metric</h2><p>Accuracy is usually misleading for rare fraud. Evaluate precision, recall, false-positive rate, approval impact, calibration, and monetary outcomes by relevant slices. A simple decision cost can include fraud loss, authentication cost, interchange or processing cost, customer abandonment, and support burden.</p><p>Thresholds should reflect asymmetric consequences. A false decline can lose a good customer; a false approval can create fraud loss and operational work. Report both, and check whether performance differs across regions, devices, customer tenure, or other legally and ethically reviewed segments.</p><h2 id="close-the-delayed-label-loop">Close the delayed-label loop</h2><p>Fraud labels arrive late and imperfectly through disputes, confirmed account takeover, investigation, and recovery. Store the label source and observation window. Do not treat every chargeback as fraud or every unreported transaction as legitimate.</p><p>Case management is asynchronous. Investigators need the decision evidence available at the time—not a reconstruction using today’s features and model. Their outcomes feed a governed training dataset with leakage checks, lineage, and approval. Compare production decisions with later outcomes to detect drift and threshold decay.</p><h2 id="operate-the-whole-decision-system">Operate the whole decision system</h2><p>Monitor latency, errors, feature freshness, reduced-mode use, score distribution, rule hit rate, step-up rate, approval, confirmed loss, false positives, and label maturity. Shadow a candidate model before promotion and retain a rapid rollback path. Repeated trials and segment-level evaluation matter because aggregate averages hide tail failures.</p><p>My design rule is to keep the synchronous path small and deterministic, then move enrichment, investigation, training, and analytics off it. Under 100 ms can then be an honest SLO for a defined boundary—not a promise detached from financial outcomes.</p><p>Before promotion, run the candidate against a time-separated, versioned dataset and a production shadow stream. Review performance by material segments, repeat trials when inference is nondeterministic, and define rollback thresholds before seeing the result. Validate the degraded feature set as a separate decision system. Finally, confirm that investigators can reconstruct a historical decision without retrieving mutable features from today’s store.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li><li><a href="https://chahidarid.online/en/double-entry-ledger/">Double-Entry Ledger Architecture for Product Engineers</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.emvco.com/emv-technologies/3-d-secure/?ref=chahidarid.online">EMVCo: EMV 3-D Secure</a></li><li><a href="https://www.emvco.com/whitepapers/emv-3-d-secure-whitepaper/3-d-secure-documentation/3-d-secure-specification/?ref=chahidarid.online">EMVCo: 3-D Secure Specification v2.3.1 hub</a></li><li><a href="https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.100-1.pdf?ref=chahidarid.online">NIST: AI Risk Management Framework 1.0</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ISO 20022 as a Domain Model, Not an XML Problem]]></title>
            <description><![CDATA[The durable value of ISO 20022 is its precise payment vocabulary; XML is only one transport representation.]]></description>
            <link>https://chahidarid.online/en/iso-20022/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/iso-20022/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:29:34 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/iso-20022.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start ISO 20022 work with the financial concept, not the XML tag. The standard is most useful as a governed vocabulary; an ingestion-only project can preserve syntax while losing meaning.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/iso-20022.png?v=20260721-motion-v2" class="kg-image" alt="ISO 20022 business concepts mapped through versioned usage guidelines to multiple syntaxes" loading="lazy" width="1600" height="1320"><figcaption>Figure 05. Business concepts remain central; a versioned market profile constrains message definitions before XML, JSON, or an internal API representation is produced.</figcaption></figure><h2 id="the-standard-is-larger-than-a-schema">The standard is larger than a schema</h2><p>The official <a href="https://www.iso20022.org/iso20022-repository/business-model?ref=chahidarid.online">ISO 20022 Business Model</a> describes industry-agreed financial concepts and relationships used to derive message elements. The <a href="https://www.iso20022.org/frequently-asked-questions?ref=chahidarid.online">ISO 20022 FAQ</a> clarifies that the standard defines a development methodology and repository, while XML and ASN.1 are generated syntaxes. The repository includes a Data Dictionary and Business Process Catalogue.</p><p>That distinction changes implementation. <code>Dbtr</code>, <code>Cdtr</code>, and <code>EndToEndId</code> are not valuable because their tag names are standardized; they are valuable because communities agree on their roles, cardinality, and business use in a particular message definition and usage guideline.</p><p>ISO 20022 does not provide one universal runtime payload. A message family, version, market practice, scheme rulebook, and bilateral constraints determine what is actually allowed. Saying “we support pacs.008” without the version and profile is incomplete.</p><h2 id="use-a-payment-example">Use a payment example</h2><p>Imagine receiving a customer credit transfer. The platform may need the instructed amount, settlement amount, debtor, creditor, accounts, agents, remittance information, purpose, and identifiers. A naive mapper copies fields into a generic <code>Payment</code> object and drops unfamiliar structures into <code>metadata</code>. That makes the happy path easy and investigations impossible.</p><p>Here is how I would build the mapping catalog for every target field:</p><ul><li>source message and element path;</li><li>semantic definition and multiplicity;</li><li>profile/version in which it is valid;</li><li>transformation or code-list rule;</li><li>whether absence means optional, unavailable, or invalid;</li><li>lossiness and preservation strategy;</li><li>owner, tests, and effective date.</li></ul><p>Preserve the original validated message under an appropriate retention and privacy policy. The internal model can optimize product workflows, but evidence must remain available when a mapping loses detail or a scheme interpretation changes.</p><h2 id="choose-the-canonical-boundary-carefully">Choose the canonical boundary carefully</h2><p>A proprietary canonical model can reduce coupling across several rails, yet it can also flatten ISO 20022’s distinctions. “Party” is not enough when the source distinguishes ultimate debtor, initiating party, debtor, creditor, and ultimate creditor. A single <code>reference</code> field cannot safely absorb instruction ID, end-to-end ID, transaction ID, and UETR.</p><p>Adopt ISO concepts where they fit the product. Where the internal model intentionally differs, document the anti-corruption layer and loss. The ISO page on <a href="https://www.iso20022.org/about-iso-20022/apis-and-iso-20022?ref=chahidarid.online">APIs and ISO 20022</a> explicitly notes that ISO business semantics can provide a consistent view for API exchanges; XML is not required at every internal boundary.</p><h2 id="version-mappings-as-production-code">Version mappings as production code</h2><p>Run old and new message versions in parallel during migration. Dispatch validation and mapping by declared namespace/profile, not by guessing from present fields. Store the mapper version with the resulting payment. Golden fixtures should cover optional parties, structured remittance, charges, return information, supplementary data, and external code sets—not only a minimal sample.</p><p>Schema validation catches structure. It does not prove compliance with a market-practice rule, a valid external code on that date, or a coherent business relationship. Add profile rules and semantic assertions. For example, validate that identifiers remain unique in their defined scope and that a return references the original transaction as required by the adopted scheme.</p><p>External code sets and market profiles evolve independently of application releases. Treat them as governed reference data with provenance, activation date, rollback, and monitoring. Do not fetch mutable code lists at transaction time without a controlled snapshot.</p><h2 id="preserve-extensions-without-surrendering-governance">Preserve extensions without surrendering governance</h2><p>Supplementary data exists for controlled extensions, not as a dumping ground. Define the extension owner, schema, namespace, consumers, and fallback behavior. A receiving party that does not understand an extension must still process or reject the message according to the applicable profile; internal teams should not assume an extension travels across every network.</p><p>When exposing JSON APIs, retain semantic names and constraints rather than mechanically converting XML tags. Publish an API contract that traces back to the ISO concept and profile. This makes it possible to adopt another serialization without reinterpreting the domain.</p><h2 id="operational-controls">Operational controls</h2><p>Measure validation failures by message version and rule, unmapped-field frequency, truncation, unknown code values, mapping latency, and reconciliation breaks traced to semantic loss. Provide an operator view showing original value, normalized value, mapping rule, and profile version.</p><p>For a version upgrade, run historical messages through both mappers and classify differences. Some changes are representation-only; others change a business interpretation and require product, operations, compliance, and counterparty sign-off.</p><h2 id="the-durable-architecture">The durable architecture</h2><p>My design rule is to keep four layers visible: ISO business concepts, versioned message/profile definitions, serialization adapters, and the internal domain model. I test the mappings and preserve evidence at the edges. Starting with the business model keeps the durable question in view: what financial fact does this document communicate?</p><p>Before claiming support, publish a conformance statement naming the message definitions, versions, market-practice guideline, validation layers, unsupported elements, and extension policy. Give operations a searchable mapping view and a controlled way to quarantine—not silently repair—invalid messages. Revisit the statement whenever a code set, scheme guideline, or counterparty contract changes. “ISO 20022 compatible” without that scope is too vague to test.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li><li><a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger That Auditors Can Trust</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.iso20022.org/iso20022-repository/business-model?ref=chahidarid.online">ISO 20022: Business Model</a></li><li><a href="https://www.iso20022.org/frequently-asked-questions?ref=chahidarid.online">ISO 20022: Frequently asked questions</a></li><li><a href="https://www.iso20022.org/about-iso-20022/apis-and-iso-20022?ref=chahidarid.online">ISO 20022: APIs and ISO 20022</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Double-Entry Ledger Architecture for Product Engineers]]></title>
            <description><![CDATA[Every money movement should become a balanced transaction whose entries are immutable and denominated explicitly.]]></description>
            <link>https://chahidarid.online/en/double-entry-ledger/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/double-entry-ledger/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:29:27 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/double-entry-ledger.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start a ledger design by asking which financial states must be impossible to represent. Double-entry provides a powerful constraint, but only when accounts, units, time, and corrections are modelled deliberately.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/double-entry-ledger.png?v=20260721-motion-v2" class="kg-image" alt="Balanced journal transaction with debit and credit entries and derived balances" loading="lazy" width="1600" height="1280"><figcaption>Figure 04. One journal transaction posts equal values to two accounts in the same currency; balances are projections of immutable entries.</figcaption></figure><h2 id="work-one-posting-before-discussing-architecture">Work one posting before discussing architecture</h2><p>A customer tops up a wallet with AED 100. In a simplified chart of accounts, the platform gains AED 100 in a safeguarded bank asset and owes AED 100 to the customer:</p>
<!--kg-card-begin: html-->
<table>
<thead>
<tr>
<th>Entry</th>
<th>Account</th>
<th style="text-align:right">Debit</th>
<th style="text-align:right">Credit</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Safeguarded cash — AED</td>
<td style="text-align:right">100.00</td>
<td style="text-align:right">0.00</td>
</tr>
<tr>
<td>2</td>
<td>Customer wallet liability — AED</td>
<td style="text-align:right">0.00</td>
<td style="text-align:right">100.00</td>
</tr>
</tbody>
</table>
<!--kg-card-end: html-->
<p>The journal balances in AED. “Debit” and “credit” are directions within an account classification, not synonyms for money entering or leaving a customer UI. A payout would reduce both the cash asset and wallet liability with the directions appropriate to those account types.</p><p>Square’s public description of <a href="https://developer.squareup.com/blog/books-an-immutable-double-entry-accounting-database-service/?ref=chahidarid.online">Books</a> explains why it chose balanced, immutable journal entries as a production consistency primitive. David Ellerman’s <a href="https://arxiv.org/abs/1407.1898?ref=chahidarid.online">mathematical treatment</a> formalizes double-entry using pairs of unsigned quantities. Neither source replaces an organization’s accounting policy; they support the invariant, not a universal chart of accounts.</p><h2 id="model-transactions-and-entries-separately">Model transactions and entries separately</h2><p>Here is how I would model it. A journal transaction is the atomic business posting, carrying a transaction ID, business reference, posting-rule version, recorded time, effective time, description, and lifecycle links. Its entries carry account, direction, amount, currency or asset unit, and analytical dimensions.</p><p>At commit, enforce:</p><ul><li>at least two entries per journal;</li><li>sum of debits equals sum of credits for each currency or asset unit;</li><li>amounts use an explicit fixed precision and cannot be negative;</li><li>accounts accept the stated unit and were open at the effective time;</li><li>a business operation cannot post twice;</li><li>entries cannot be updated or deleted through ordinary application paths.</li></ul><p><a href="https://www.postgresql.org/docs/current/ddl-constraints.html?ref=chahidarid.online">PostgreSQL constraints</a> provide building blocks such as checks, unique constraints, and foreign keys. Cross-row balance normally also needs a controlled posting transaction or database routine; a row-level check alone cannot prove that the journal balances.</p><h2 id="never-balance-currencies-by-accident">Never balance currencies by accident</h2><p>USD 100 debit and EUR 100 credit do not form a balanced journal. Balance per unit. A foreign-exchange trade typically needs separate balanced legs and explicit FX position or clearing accounts, plus the agreed rate and valuation evidence. Reporting can translate values into a base currency, but that valuation is a derived view—not permission to mix denominations inside the posting invariant.</p><p>Represent money as integer minor units only when the currency and product precision make that safe. Otherwise use a fixed decimal with validated scale. Floating-point arithmetic has no place in the authoritative amount.</p><h2 id="pending-is-a-product-decision-not-a-universal-entry-type">Pending is a product decision, not a universal entry type</h2><p>An authorization hold can be represented with memorandum accounts, encumbrance entries, separate available-balance reservations, or another subledger design. Posting pending and settled positions as full journal entries is one valid choice, not a law of double-entry.</p><p>Choose based on which questions the ledger must answer. If available balance is financially material, the reservation needs durable identity, expiry, partial capture, release, and reconciliation. Do not mutate a pending entry into a settled one. Link the settlement transaction to the reservation and post the release or reclassification required by the chosen accounting model.</p><h2 id="correct-with-reversals-not-edits">Correct with reversals, not edits</h2><p>If AED 40 was credited to the wrong customer, create a reversal transaction linked to the original and then a corrected transaction. Preserve reason, initiator, approval, and timestamps. A reversal should copy the exact original units and accounts in opposite directions; a compensating adjustment that uses different accounts is a different accounting action and should be named accordingly.</p><p>This append-only approach keeps statements, period close, and downstream events explainable. Administrative database access still exists, so “immutable” also requires access control, audit logging, backups, and integrity monitoring.</p><h2 id="balances-are-projections-with-proofs">Balances are projections with proofs</h2><p>The authoritative balance is the sum of entries through a defined cutoff. For performance, maintain balance snapshots or account aggregates in the same transaction as posting, or rebuild them from the journal. Store a sequence/checkpoint and test that projected balances equal recomputed balances.</p><p>Concurrency matters when the product forbids a negative available balance. Lock the relevant account, use serializable logic, or maintain a conditional balance row so two withdrawals cannot both pass the same balance check. Double-entry proves the journal balances; it does not by itself prevent overspending.</p><h2 id="the-controls-that-make-it-production-grade">The controls that make it production-grade</h2><p>For every posting, an investigator should retrieve the initiating operation, posting rule, journal, entries, reversal chain, external reference, and resulting balance. Monitor rejected unbalanced journals, duplicate business keys, stale reservations, reversal rates, projection drift, and reconciliation breaks.</p><p>Property-based tests are especially useful: generate posting inputs and assert per-unit balance, immutability, idempotency, and rebuild equivalence. Then test concrete product scenarios—partial capture, fee, refund, chargeback, and currency conversion—with accounting review.</p><p>My design rule is not merely “use debits and credits.” I want a chart of accounts and posting boundary where every movement is balanced, denominated, traceable, corrected by new evidence, and independently recomputable.</p><p>Before releasing a product flow, have accounting and engineering review its full posting matrix: initiation, pending state, settlement, fee, partial completion, expiry, refund, dispute, and correction. Include the external evidence and customer statement description for every leg. A technically balanced journal can still be economically wrong when it uses the wrong account, date, party, or valuation rule; balance is a necessary control, not the whole accounting judgment.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger That Auditors Can Trust</a></li><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://developer.squareup.com/blog/books-an-immutable-double-entry-accounting-database-service/?ref=chahidarid.online">Square: Books, an immutable double-entry accounting database service</a></li><li><a href="https://arxiv.org/abs/1407.1898?ref=chahidarid.online">Ellerman: On Double-Entry Bookkeeping—The Mathematical Treatment</a></li><li><a href="https://www.postgresql.org/docs/current/ddl-constraints.html?ref=chahidarid.online">PostgreSQL: Constraints</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Idempotency at Scale: More Than a Request Header]]></title>
            <description><![CDATA[Reliable idempotency binds a caller-defined key to a canonical operation, payload fingerprint, and durable outcome.]]></description>
            <link>https://chahidarid.online/en/idempotency-at-scale/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/idempotency-at-scale/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:29:22 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/idempotency-at-scale.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start with the business effect, not the header. Idempotency is a concurrency contract that gives repeated requests one durable operation; it is not duplicate detection after the fact.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/idempotency-at-scale.png?v=20260721-motion-v2" class="kg-image" alt="Three-lane payment sequence showing an idempotency key preventing a duplicate charge after a network timeout" loading="lazy" width="1600" height="1320"><figcaption>Figure 03. The first request claims one operation; a retry with the same key reads and returns the completed result instead of charging again.</figcaption></figure><h2 id="http-semantics-are-only-the-starting-point">HTTP semantics are only the starting point</h2><p><a href="https://www.rfc-editor.org/rfc/rfc9110.html?ref=chahidarid.online#section-9.2.2">RFC 9110</a> defines an idempotent method as one whose intended server effect is the same for multiple identical requests as for one. It classifies PUT, DELETE, and safe methods as idempotent, while warning clients not to retry non-idempotent methods automatically unless they know the application semantics are idempotent.</p><p>Payment creation is usually POST because it creates a new operation. The application therefore needs a stronger contract: a client supplies an idempotency key; the server binds that key to one canonical operation, one payload fingerprint, and one outcome. Stripe’s <a href="https://docs.stripe.com/api/idempotent_requests?ref=chahidarid.online">idempotent request documentation</a> is a useful public example: it replays the first result, checks that reused keys have matching parameters, and documents a retention window. Those details are Stripe-specific, not properties of HTTP.</p><h2 id="define-the-namespace-before-the-table">Define the namespace before the table</h2><p><code>abc-123</code> is not globally unique simply because a client generated it. Build a namespace such as <code>(tenant_id, api_operation, idempotency_key)</code>. A key used for <code>CreatePayment</code> should not collide with the same text used for <code>CreateRefund</code>, and one tenant must not be able to discover another tenant’s operation.</p><p>Canonicalize the request fields that determine the financial effect, then hash them. Exclude transport-only metadata, but include amount, currency, destination, capture mode, and any account whose change would create a different operation. Store both the fingerprint and enough protected request metadata to investigate mismatches. Reuse with a different fingerprint should return a conflict, never silently replay the old result.</p><h2 id="claim-atomically-under-concurrency">Claim atomically under concurrency</h2><p>The hardest race occurs when two identical requests arrive before either has written a result. A read-then-insert implementation allows both workers to execute. Use a unique database constraint and an atomic insert/claim. The winner creates an operation record; the loser reads it.</p><p>Here is how I would model the durable record:</p><ul><li>namespace and idempotency key;</li><li>payload fingerprint;</li><li>canonical operation ID;</li><li>state such as <code>in_progress</code>, <code>succeeded</code>, <code>failed_final</code>, or <code>outcome_unknown</code>;</li><li>response status and stable response body or resource reference;</li><li>lease owner and expiry for recoverable work;</li><li>creation and expiry timestamps.</li></ul><p>When a duplicate sees <code>in_progress</code>, the API can return <code>202 Accepted</code> with the operation URL, briefly wait for completion, or return a documented conflict/retry response. It should not start another provider call. A lease helps recover an abandoned worker, but lease expiry alone does not prove that an external side effect did not happen.</p><h2 id="close-the-crash-window">Close the crash window</h2><p>Imagine the worker successfully sends AED 75 to a provider, then crashes before saving the response. Reassigning the lease and sending again can duplicate the payment. The correct recovery depends on the downstream contract:</p><ol><li>pass a stable downstream idempotency key derived from the internal operation;</li><li>persist the provider request reference before or with submission where possible;</li><li>after an ambiguous timeout, query provider status or await a signed callback;</li><li>keep the internal state <code>outcome_unknown</code> until evidence resolves it.</li></ol><p>This is why a response cache placed after the provider is insufficient. The durable operation must exist before the effect, and recovery must understand the effect’s financial semantics.</p><h2 id="decide-what-gets-replayed">Decide what gets replayed</h2><p>Validation failures that occur before execution need not reserve a key forever, but the API contract should say so. Once execution starts, replaying the same stable response—including some failures—can be safer than running again. Stripe documents that its API v1 saves the first status and body, including <code>500</code> responses; its newer API behavior differs, so never generalize a vendor rule without checking the API version.</p><p>Some results should be represented by a resource reference instead of storing a large response blob. Reconstruct the response from an immutable operation and version the representation. Otherwise a later serialization change can make the “same response” impossible to explain.</p><h2 id="retention-regions-and-scale">Retention, regions, and scale</h2><p>Retention must cover realistic client retry, offline queue, and webhook-redelivery windows. Expiring too early permits a delayed replay to create a new effect; retaining forever increases storage and privacy obligations. After expiry, a tombstone or business uniqueness rule may still be required for operations that cannot safely repeat.</p><p>Multi-region active-active writes complicate atomic claiming. Eventual replication can allow both regions to win. Options include routing a namespace to one home region, using a globally linearizable store, or accepting provisional requests while one authority assigns the operation. Measure the consistency cost instead of claiming “global idempotency” from a local unique index.</p><p>Operational metrics should include claim contention, in-progress age, fingerprint conflicts, unknown outcomes, expired replays, storage growth, and hot keys. Load tests must send concurrent duplicates, not only unique traffic.</p><h2 id="the-contract-to-publish">The contract to publish</h2><p>My design rule is to publish the contract—supported operations, namespace, retention, mismatch behavior, in-progress response, and error replay—and enforce the same identity downstream. At scale, I earn idempotency with one atomic claim and one recoverable operation, not with the presence of a request header.</p><p>Review the contract with failure injection, not only API examples. Pause a worker after its provider call, race one hundred identical requests, reuse a key with one changed field, fail over between regions, and replay after expiry. Inspect both the external side effect and the internal operation. The pass condition is one explainable effect; a collection of identical HTTP responses can still hide duplicated money movement.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger That Auditors Can Trust</a></li><li><a href="https://chahidarid.online/en/payment-orchestration/">Payment Orchestration Without the Spaghetti</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://www.rfc-editor.org/rfc/rfc9110.html?ref=chahidarid.online#section-9.2.2">IETF RFC 9110 §9.2.2: Idempotent Methods</a></li><li><a href="https://docs.stripe.com/api/idempotent_requests?ref=chahidarid.online">Stripe: Idempotent requests</a></li><li><a href="https://docs.stripe.com/webhooks?ref=chahidarid.online#handle-duplicate-events">Stripe: Handling duplicate webhook events</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Designing an Event-Driven Ledger That Auditors Can Trust]]></title>
            <description><![CDATA[Events can drive workflow, but immutable accounting entries—not transient messages—must remain the financial source of truth.]]></description>
            <link>https://chahidarid.online/en/event-driven-ledger/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/event-driven-ledger/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:29:16 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/event-driven-ledger.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I use events to distribute financial work, but I do not let the message stream declare itself the ledger. My starting point is an explicit posting boundary, correction model, and replay contract.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/event-driven-ledger.png?v=20260721-motion-v2" class="kg-image" alt="Atomic ledger posting and outbox architecture with projections and replay" loading="lazy" width="1600" height="1000"><figcaption>Figure 02. Balanced journal entries and an outbox record commit together; delivery, projections, and workflow happen after the accounting fact exists.</figcaption></figure><h2 id="three-records-that-teams-often-confuse">Three records that teams often confuse</h2><p>Consider a wallet transfer of AED 250. The request to move funds is a <strong>command</strong>: it can be rejected for insufficient available balance. The balanced debit and credit are <strong>accounting facts</strong>: once booked, they are corrected with new entries rather than edited. <code>TransferBooked</code> is an <strong>integration event</strong>: a versioned notification that helps other services react.</p><p>These records can share an identifier, but they do not have the same authority. A Kafka topic is not automatically a journal, and an event-sourced workflow is not automatically double-entry accounting. If downstream consumers independently infer financial postings from an evolving business event, the same transfer can acquire different meanings in settlement, statements, and reporting.</p><p>Square’s engineering account of <a href="https://developer.squareup.com/blog/books-an-immutable-double-entry-accounting-database-service/?ref=chahidarid.online">Books</a> describes an immutable, balanced accounting service as the consistency primitive. AWS’s <a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html?ref=chahidarid.online">transactional outbox guidance</a> explains the complementary delivery pattern: write domain state and an outbox record in one database transaction, then publish asynchronously. It also states that duplicates remain possible and consumers should be idempotent.</p><h2 id="put-the-transaction-boundary-around-the-posting">Put the transaction boundary around the posting</h2><p>Here is how I would define the posting boundary. The posting service validates the journal before commit:</p><ul><li>every entry belongs to one transaction identifier;</li><li>debits and credits balance for each currency or asset unit;</li><li>accounts, dimensions, and posting rule version are valid;</li><li>monetary values use explicit minor-unit or decimal precision;</li><li>the command’s business key has not already produced a posting;</li><li>recorded time, effective time, and source time are preserved separately.</li></ul><p>Inside one database transaction, insert the immutable journal entries and an outbox row describing the committed transaction. Do not publish first and hope the database commit succeeds. Do not write the ledger and broker independently. The outbox closes that dual-write gap, but it does not provide exactly-once delivery; the published event must carry a stable event ID and transaction ID.</p><p>This model also clarifies what “event-driven ledger” should mean: events drive workflows around an authoritative ledger, and committed postings emit events. It does not mean any event stream becomes financially authoritative by declaration.</p><h2 id="design-corrections-as-accounting-events">Design corrections as accounting events</h2><p>Suppose an operator booked AED 250 to the wrong merchant account. Editing the row destroys the evidence seen by previous statements and consumers. Instead, post a reversal linked to the original transaction, then post the corrected transaction. The current balance changes, but the historical explanation remains intact.</p><p>Corrections need a reason code, initiator, approval evidence where required, original transaction reference, and a new idempotency key. “Reversal” should not be used as a generic delete. Some rails reverse a pending authorization; the ledger may instead record release of an encumbrance. The accounting representation must follow the product’s chart of accounts and posting policy.</p><h2 id="make-time-and-versions-first-class">Make time and versions first-class</h2><p>Financial systems have several clocks. <code>recorded_at</code> tells auditors when the platform accepted the posting. <code>effective_at</code> tells statements and interest logic when its economic effect applies. <code>source_occurred_at</code> preserves the rail or provider timestamp. Late settlement data can legitimately be recorded today with an earlier effective date; collapsing the clocks makes period close and investigation ambiguous.</p><p>Event schemas evolve too. Include a schema version and stable semantic identifiers. Additive changes are easier to consume, but a changed posting meaning deserves a new event type or versioned contract. Keep the original payload when regulatory or operational evidence requires it, while ensuring secrets and personal data are protected.</p><h2 id="replay-without-paying-twice">Replay without paying twice</h2><p>Projection consumers should store the last processed event or a deduplication record in the same transaction as their local update. A replay can rebuild balances, statements, and analytics from authoritative entries, but it must never call an external payment provider merely because an old event was read again.</p><p>Separate pure projections from effectful process managers. A statement projection can be rebuilt. A process manager that sends a payout needs its own durable command state and idempotency boundary. This distinction turns replay from a production hazard into a recovery tool.</p><h2 id="controls-auditors-can-test">Controls auditors can test</h2><p>An audit-ready platform can demonstrate:</p><ol><li>the posting rule and code version that produced every journal;</li><li>balance invariants checked at commit time;</li><li>an unbroken link from command to posting to outbox event;</li><li>corrections represented as linked entries;</li><li>delivery lag, duplicate rate, dead-letter age, and projection checkpoints;</li><li>periodic comparison of ledger totals with external settlement evidence.</li></ol><p>Test the awkward moments: database commits but publisher crashes; broker redelivers after acknowledgement loss; a consumer deploy changes schema interpretation; a late correction crosses an accounting period; and a projection is rebuilt while live events continue. Recovery is complete only when balances and evidence agree.</p><h2 id="the-durable-conclusion">The durable conclusion</h2><p>My design rule is that events distribute a committed financial fact; they never manufacture its authority. I trust the ledger when balanced entries commit atomically, corrections preserve history, time semantics are explicit, and every derived event traces back to a posting.</p><p>Before production, document which database transaction contains the posting and outbox row, how the publisher checkpoints, and how each consumer deduplicates. Rebuild a projection in a clean environment and compare it with production totals. Then simulate a publisher crash, schema rollback, and late correction across a period boundary. If the team cannot explain the resulting journal and consumer checkpoints, replay is not yet a safe recovery mechanism.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/payment-orchestration/">Payment Orchestration Without the Spaghetti</a></li><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html?ref=chahidarid.online">AWS: Transactional outbox pattern</a></li><li><a href="https://developer.squareup.com/blog/books-an-immutable-double-entry-accounting-database-service/?ref=chahidarid.online">Square: Books, an immutable double-entry accounting database service</a></li><li><a href="https://docs.stripe.com/webhooks?ref=chahidarid.online">Stripe: Webhook delivery behavior</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Payment Orchestration Without the Spaghetti]]></title>
            <description><![CDATA[A payment orchestrator should isolate routing policy from provider-specific execution while preserving one observable payment state.]]></description>
            <link>https://chahidarid.online/en/payment-orchestration/</link>
            <guid isPermaLink="true">https://chahidarid.online/en/payment-orchestration/</guid>
            <category><![CDATA[FinTech Engineering]]></category>
            <dc:creator><![CDATA[Chahid Arid]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 17:28:36 +0400</pubDate>
            <media:content url="https://chahidarid.online/assets/diagrams/payment-orchestration.png?v&#x3D;20260721-diagram-alignment-v1" medium="image" />
            <content:encoded><![CDATA[<p>FinTech Engineering</p><p>I start with one question: who can explain the payment when two processors disagree? Payment orchestration becomes valuable when it creates one truthful lifecycle across several processors—not when it merely wraps several APIs behind another endpoint.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chahidarid.online/assets/diagrams/payment-orchestration.png?v=20260721-motion-v2" class="kg-image" alt="Payment orchestration control plane, processor attempts, callbacks, and reconciliation" loading="lazy" width="1600" height="1190"><figcaption>Figure 01. The orchestrator owns intent and attempt state; processors remain external systems whose callbacks and reports must be reconciled.</figcaption></figure><h2 id="start-with-the-ambiguity-not-the-routing-rule">Start with the ambiguity, not the routing rule</h2><p>Assume a merchant submits a USD 180 order. The orchestrator sends an authorization to Processor A, but the connection closes before the response arrives. Retrying immediately with Processor B may improve availability—or create two valid authorizations. The transport failed; the financial outcome is unknown.</p><p>That distinction determines the architecture. A payment intent represents the merchant’s objective. Provider attempts represent executions of that intent. An attempt may be <code>submitted</code>, <code>authorised</code>, <code>declined</code>, <code>unknown</code>, <code>reversed</code>, or <code>expired</code> without forcing the intent into the same state. The orchestrator should never compress “we did not receive a response” into “the provider declined.”</p><p>Stripe’s official documentation illustrates both sides of this problem: its <a href="https://docs.stripe.com/payments/orchestration/retries?ref=chahidarid.online">orchestration retries</a> are constrained by processor capabilities and authentication outcomes, while its <a href="https://docs.stripe.com/webhooks?ref=chahidarid.online">webhook documentation</a> warns that events can be duplicated and delivered out of order. Stripe Orchestration was still described as a private preview when this article was reviewed in July 2026, so it is an example rather than a universal contract.</p><h2 id="separate-policy-execution-and-financial-truth">Separate policy, execution, and financial truth</h2><p>Here is how I would model it, with four distinct responsibilities:</p><ol><li><strong>Intent service:</strong> owns the merchant-facing identifier, amount, currency, capture mode, and permitted terminal states.</li><li><strong>Decision service:</strong> selects a route using declared inputs such as geography, scheme, cost, processor health, risk decision, and contractual constraints. It records the policy version and explanation.</li><li><strong>Provider adapters:</strong> translate canonical commands into provider-specific authorization, capture, void, and refund operations. They map provider references and error classes without owning business state.</li><li><strong>State and evidence store:</strong> atomically records attempts, transitions, idempotency keys, raw provider references, signed callback metadata, and the resulting ledger instruction.</li></ol><p>This is a control plane with branches and feedback, not a five-stage pipeline. Risk may run before route selection, after a soft decline, or during step-up authentication. Capture may occur hours after authorization. A refund is a new financial operation, not a mutation of the original attempt.</p><h2 id="make-cross-processor-retry-a-guarded-decision">Make cross-processor retry a guarded decision</h2><p>Define an operation-specific retry matrix. A deterministic validation error is not retryable. A network failure before a request leaves the adapter may be safe to retry with the same provider and idempotency key. A timeout after submission is unknown and should move to status inquiry, callback waiting, or reconciliation—not automatic cross-provider execution.</p><p>A safe failover policy might say:</p><ul><li>retry Processor A once only when its API guarantees idempotent replay for the same merchant operation;</li><li>do not route to Processor B while A is <code>unknown</code> unless the product explicitly accepts a temporary double hold and has an automated reversal plan;</li><li>bind any approval or 3-D Secure result to the processor and attempt for which it is valid;</li><li>cap attempts by payment intent, not independently inside every adapter.</li></ul><p>The routing engine should return a decision record, not only a provider name: <code>policy_version</code>, evaluated facts, excluded routes, chosen route, and retry budget. This makes later disputes explainable and prevents a dashboard configuration change from silently rewriting history.</p><h2 id="treat-callbacks-as-evidence-not-commands">Treat callbacks as evidence, not commands</h2><p>Verify callback signatures, persist the raw envelope, deduplicate on provider event identifiers, and then apply a legal state transition. If an <code>authorised</code> callback arrives after an attempt was marked <code>unknown</code>, it resolves uncertainty. If it arrives after a successful failover, it triggers a duplicate-authorization workflow; it must not overwrite the winning attempt.</p><p>The callback handler should acknowledge quickly and process asynchronously. The same principle appears in Stripe’s guidance to queue complex webhook work and handle duplicate delivery. Consumers should retrieve provider state when required because event ordering is not guaranteed.</p><p>Daily settlement reports form an independent control channel. Compare processor transactions, internal attempts, and ledger postings. This catches missing callbacks, operator changes, partial captures, and provider-side adjustments. Orchestration without reconciliation only centralizes uncertainty.</p><h2 id="what-to-measure">What to measure</h2><p>Approval rate alone rewards aggressive retrying and can conceal customer harm. Operate the platform with a balanced scorecard:</p><ul><li>authorization rate by route, issuer region, and policy version;</li><li><code>unknown</code> attempt rate and age;</li><li>duplicate authorization and reversal rate;</li><li>callback delay and reconciliation breaks;</li><li>incremental approval uplift net of fees, fraud loss, and false declines;</li><li>manual interventions and time to explain a payment.</li></ul><p>Chaos tests should inject a commit-before-timeout, duplicate callbacks, late success after failover, and a provider outage. The assertion is not merely that services recover. It is that one intent produces explainable attempts, balanced ledger effects, and a deterministic customer outcome.</p><h2 id="the-design-rule">The design rule</h2><p>My design rule is simple: let the orchestrator own the payment intent and its attempt state machine, let adapters own protocol translation, and let the ledger own financial postings. I cross a provider boundary only through a recorded decision, and I resolve unknown outcomes before creating new money movement. That boundary removes the real “spaghetti”: not API variety, but contradictory ownership of the same financial fact.</p><p>Before launch, review each operation separately—authorization, capture, void, refund, and payout. Confirm its authoritative identifier, permitted transitions, retry evidence, callback behavior, reconciliation source, and customer-visible wording. Record who may change routing policy and how an emergency change is rolled back. A failover feature should not ship until operations can identify and reverse a duplicate outcome without database surgery.</p><h2 id="related-reading">Related reading</h2><ul><li><a href="https://chahidarid.online/en/event-driven-ledger/">Designing an Event-Driven Ledger That Auditors Can Trust</a></li><li><a href="https://chahidarid.online/en/idempotency-at-scale/">Idempotency at Scale: More Than a Request Header</a></li></ul><h2 id="references">References</h2><ul><li><a href="https://docs.stripe.com/payments/orchestration/retries?ref=chahidarid.online">Stripe: Cross-processor retries with Orchestration</a></li><li><a href="https://docs.stripe.com/webhooks?ref=chahidarid.online">Stripe: Webhook delivery and duplicate-event handling</a></li><li><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/saga-orchestration.html?ref=chahidarid.online">AWS: Saga orchestration pattern</a></li></ul>]]></content:encoded>
        </item>
</channel>
</rss>
