<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Okikiola Ashiru</title>
    <description>The latest articles on DEV Community by Okikiola Ashiru (@0xkikiola).</description>
    <link>https://dev.to/0xkikiola</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4052767%2Fec3eb26e-78ed-42ea-9cb1-ef2565712b26.jpg</url>
      <title>DEV Community: Okikiola Ashiru</title>
      <link>https://dev.to/0xkikiola</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0xkikiola"/>
    <language>en</language>
    <item>
      <title>What Would a Fintech Payment System's DevOps Setup Actually Look Like?</title>
      <dc:creator>Okikiola Ashiru</dc:creator>
      <pubDate>Wed, 29 Jul 2026 14:10:43 +0000</pubDate>
      <link>https://dev.to/0xkikiola/what-would-a-fintech-payment-systems-devops-setup-actually-look-like-1pd</link>
      <guid>https://dev.to/0xkikiola/what-would-a-fintech-payment-systems-devops-setup-actually-look-like-1pd</guid>
      <description>&lt;p&gt;Every industry has different constraints that shape its infrastructure decisions. Banking cares about compliance and audit trails. E-commerce cares about traffic spikes on sale days. Fintech payment systems care about something more specific: &lt;strong&gt;money cannot be created or destroyed by accident.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqv774tcnjvcl5prd2x9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqv774tcnjvcl5prd2x9s.png" alt=" " width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a request gets retried because of a flaky network connection, a customer should never be charged twice. If two requests hit the database at the same time, the ledger should never end up in an inconsistent state. This one constraint, correctness under concurrency and failure, ends up driving almost every infrastructure decision in a payment system.&lt;/p&gt;

&lt;p&gt;I wanted to understand this properly, so I built a small payment gateway simulation called &lt;strong&gt;ledger-sentinel&lt;/strong&gt; to work through the actual engineering problems, not just read about them. Here's what the DevOps/infrastructure thinking behind a system like this looks like, using what I built as a concrete example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core problem: idempotency
&lt;/h2&gt;

&lt;p&gt;The first design question in any payment system is: &lt;strong&gt;what happens when the same request arrives twice?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This happens constantly in production; a client times out and retries, a load balancer resends a request, a mobile app has a flaky connection and fires the same "pay now" tap twice. If your system isn't built to handle this, you get duplicate charges.&lt;/p&gt;

&lt;p&gt;The standard pattern is an &lt;strong&gt;idempotency key&lt;/strong&gt;: the client sends a unique key with each logical transaction, and the server guarantees that request only actually processes once, no matter how many times it arrives.&lt;/p&gt;

&lt;p&gt;In ledger-sentinel, I implemented this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Redis distributed lock&lt;/strong&gt; (SET NX EX) that acts as a fast, short-lived gatekeeper... the first request to arrive with a given key gets the lock, any duplicate arriving while it's still processing gets rejected (HTTP 409) instead of double-processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A response cache layered&lt;/strong&gt; on top, so once a request has actually completed, any later retry with the same key gets served the original cached response (HTTP 200) instead of hitting the database again&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the difference between "the system happened to work in testing" and "the system is provably safe under concurrent retries", and it's the first thing I'd want to verify actually holds before trusting any payment infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why async settlement, not synchronous processing
&lt;/h2&gt;

&lt;p&gt;A naive payment API might try to validate, charge, and update the ledger all within a single HTTP request. This is a mistake at any real scale, because it means the client is waiting on your slowest downstream dependency (usually the database) for every single request, and a slow ledger write becomes a slow, unreliable API.&lt;/p&gt;

&lt;p&gt;The fix is decoupling ingestion from settlement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The API validates the request and idempotency key, then pushes the transaction onto a RabbitMQ queue&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A separate worker process consumes the queue and does the actual ledger update&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The client gets a fast response (validated and queued), and the ledger settles asynchronously&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This also gives you backpressure control, if the worker is falling behind under a traffic burst, messages queue up safely instead of the whole system falling over. I set a consumer prefetch limit (prefetch_count=10) specifically so the worker never grabs more in-flight work than it can actually handle at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the ledger itself still needs strict locking
&lt;/h2&gt;

&lt;p&gt;Queueing solves the "don't block the client" problem, but it doesn't solve the "two workers processing related transactions at the same time" problem. For that, the actual database work still needs strict consistency guarantees.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;PostgreSQL row-level locking&lt;/strong&gt; (SELECT ... FOR UPDATE) so that when a worker is updating a specific ledger balance, no other transaction can read or write that same row until the lock releases. This is slower than an unlocked read, but for a ledger, "slower but always correct" beats "fast but occasionally wrong", the entire point of a ledger is that the numbers are always right.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I actually verified this wasn't just "should work in theory"
&lt;/h2&gt;

&lt;p&gt;Design decisions are cheap to write down and easy to get wrong in practice. So I stress-tested it with k6, firing over 1,000 concurrent transaction requests at the system, some with duplicate idempotency keys on purpose, and checked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero duplicate charges&lt;/strong&gt; — verified directly against the database (GROUP BY idempotency_key HAVING COUNT(*) &amp;gt; 1 returned nothing)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;p99 latency&lt;/strong&gt; stayed sub-second even during the burst, and recovered to ~4–8ms once the burst normalized and requests were hitting the cache&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;100% success rate, zero HTTP 5xx errors, across the full load test&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa69ygbzvy5nfwcoenj8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa69ygbzvy5nfwcoenj8c.png" alt=" " width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also watched this fail in interesting ways during development, for example, my first version of the load test accidentally reused the same idempotency key across requests, which meant Redis was serving cached responses without ever reaching RabbitMQ at all. The queue graphs looked completely flat, which looked like a bug until I realized the test script itself was wrong, not the system. That kind of debugging, figuring out whether the system or the test is lying to you, is most of what this work actually is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd add next if this were a real production system
&lt;/h2&gt;

&lt;p&gt;This project is a simulation, not a production system, and there are things a real fintech payment platform would need that I haven't built here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Multi-region failover, since payment infra can't have a single point of failure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real reconciliation against an external payment processor's records, not just internal consistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proper secrets management and PCI-DSS-aligned access controls, rather than local .env files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chaos testing (deliberately killing a worker mid-transaction) rather than just load testing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core lesson holds even at small scale: in payment systems, the interesting infrastructure decisions aren't about which cloud provider or which orchestrator you pick. They're about how you handle the moment two things happen at exactly the same time.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/aashiruu/ledger-sentinel" rel="noopener noreferrer"&gt;https://github.com/aashiruu/ledger-sentinel&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>architecture</category>
      <category>redis</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Hi, I'm Okikiola. Cloud &amp; DevOps Engineer working through infrastructure problems in public</title>
      <dc:creator>Okikiola Ashiru</dc:creator>
      <pubDate>Wed, 29 Jul 2026 10:05:55 +0000</pubDate>
      <link>https://dev.to/0xkikiola/hi-im-okikiola-cloud-devops-engineer-working-through-infrastructure-problems-in-public-3391</link>
      <guid>https://dev.to/0xkikiola/hi-im-okikiola-cloud-devops-engineer-working-through-infrastructure-problems-in-public-3391</guid>
      <description>&lt;p&gt;Hi, I'm Okikiola — a Cloud &amp;amp; DevOps Engineer based in Lagos, Nigeria.&lt;/p&gt;

&lt;p&gt;I came into this field a little sideways. My background is in Biochemistry, but somewhere along the way I got pulled toward infrastructure... the kind of problems where something breaks under load and you have to figure out why, or where a system needs to survive a traffic burst without falling over or double-charging someone.&lt;/p&gt;

&lt;p&gt;That pull turned into two years of hands-on work: provisioning cloud environments on AWS and OCI, building CI/CD pipelines, and setting up the observability stacks that catch failures before a customer notices them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I work with day to day&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;: Modular Terraform across AWS and OCI, managing VPCs, IAM policies, and multi-node EKS clusters across projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: GitHub Actions workflows for automated linting, container builds, and deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: Prometheus, Grafana, and Loki for metrics, logs, and alerting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: Go and Python (Boto3) scripts for the unglamorous but necessary work, cleaning up orphaned cloud resources, enforcing tagging policies, automating backup rotations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I'm writing here&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've started building deeper projects specifically to understand infrastructure problems at the system level, not just to add tools to a resume.&lt;/p&gt;

&lt;p&gt;My most recent one, &lt;em&gt;ledger-sentinel&lt;/em&gt;, is a simulated fintech payment gateway I built to work through idempotency locking (Redis SET NX EX), async settlement via RabbitMQ, and database consistency under concurrent load. It's the kind of system where "it worked in my test" and "it's actually safe against double-charges" are two very different claims, and I wanted to prove the second one, not just assume it.&lt;/p&gt;

&lt;p&gt;I plan to write about this project and others like it, real architecture decisions, load-test numbers, what broke, and what I'd still need to add before calling any of it production-ready. I'd rather be upfront about the gap between a personal project and a production system than pretend it isn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'm currently digging into&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right now I'm spending time on distributed lock contention patterns, Redis cache short-circuiting, and queue backpressure mechanics, mostly because ledger-sentinel showed me how much nuance sits behind "just add a distributed lock" once thousands of requests hit the same endpoint at once.&lt;/p&gt;

&lt;p&gt;If you work in DevOps, SRE, or platform engineering, or you're navigating a non-traditional path into cloud infrastructure yourself, I'd like to connect. Always glad to talk through Terraform patterns, observability setups, or a race condition you've had to hunt down.&lt;/p&gt;

&lt;p&gt;GitHub: github.com/aashiruu&lt;br&gt;
LinkedIn: linkedin.com/in/okikiolaashiru&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>architecture</category>
      <category>introduction</category>
    </item>
  </channel>
</rss>
