<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Tonderai Khatai</title><link>https://tldkhatai.com/</link><description>Software engineer with 10+ years of experience writing about system design, architecture tradeoffs, and building resilient distributed systems in Node.js and TypeScript.</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 06 Oct 2026 00:02:00 +0100</lastBuildDate><atom:link href="https://tldkhatai.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Compare-and-Swap Updates in PostgreSQL, Explained</title><link>https://tldkhatai.com/posts/postgres-cas-explained/</link><pubDate>Tue, 06 Oct 2026 00:02:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/postgres-cas-explained/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/postgres-cas-explained.png" referrerpolicy="no-referrer">
            </div><p>Compare-and-swap means: change this row, but only if it is still what I last saw. In SQL that&rsquo;s an <code>UPDATE</code> with a guard in the <code>WHERE</code> clause, and you check how many rows it touched.</p>]]></description></item><item><title>PostgreSQL FOR UPDATE SKIP LOCKED, Explained</title><link>https://tldkhatai.com/posts/postgres-skip-locked-explained/</link><pubDate>Tue, 06 Oct 2026 00:01:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/postgres-skip-locked-explained/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/postgres-skip-locked-explained.png" referrerpolicy="no-referrer">
            </div><p><code>FOR UPDATE</code> locks the rows a query returns. Add <code>SKIP LOCKED</code> and a row that another transaction has already locked is skipped instead of waited for.</p>]]></description></item><item><title>SKIP LOCKED vs CAS in PostgreSQL</title><link>https://tldkhatai.com/posts/postgres-skip-locked-vs-cas/</link><pubDate>Tue, 06 Oct 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/postgres-skip-locked-vs-cas/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/postgres-skip-locked-vs-cas.png" referrerpolicy="no-referrer">
            </div><p>Eight workers poll the same <code>jobs</code> table. Each job must be processed by exactly one of them. Two PostgreSQL tools solve this, <code>FOR UPDATE SKIP LOCKED</code> and compare-and-swap (CAS), and on the same workload one claimed every job it tried for while the other lost most of its attempts.</p>]]></description></item><item><title>Fixed Window vs Sliding Window vs Token Bucket</title><link>https://tldkhatai.com/posts/rate-limiter-define-the-window/</link><pubDate>Sun, 20 Sep 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/rate-limiter-define-the-window/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/rate-limiter-define-the-window.png" referrerpolicy="no-referrer">
            </div><p>Say a limiter allows 100 requests per minute. At 11:59:59 a user sends 100. At 12:00:01 they send 100 more. That&rsquo;s 200 requests in 2 seconds, and one common implementation lets all of them through.</p>]]></description></item><item><title>One Droplet, One YAML File, Two Production Apps</title><link>https://tldkhatai.com/posts/digitalocean-infra-setup/</link><pubDate>Wed, 17 Jun 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/digitalocean-infra-setup/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/digitalocean-infra-setup.png" referrerpolicy="no-referrer">
            </div><p>Two production Node apps — <a href="https://handovha.com">Handovha</a> and Mail Koenig — run on one <a href="https://www.digitalocean.com">DigitalOcean</a> droplet at the smallest tier DigitalOcean sells. No <a href="https://kubernetes.io">Kubernetes</a>, no <a href="https://www.terraform.io">Terraform</a>, no PaaS.</p>]]></description></item><item><title>Mail Koenig: A Send Queue Made Entirely of Postgres</title><link>https://tldkhatai.com/posts/mail-koenig-architecture/</link><pubDate>Thu, 14 May 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/mail-koenig-architecture/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/mail-koenig-architecture.png" referrerpolicy="no-referrer">
            </div><p>Sending an email campaign to a few thousand people looks simple until the third recipient&rsquo;s inbox provider rate-limits you mid-send, the fourth bounces permanently, and the process doing the sending gets killed by a deploy halfway through.</p>]]></description></item><item><title>Functional Core, Imperative Shell Makes Sense for Micro Apps</title><link>https://tldkhatai.com/posts/functional-core-imperative-shell-micro-apps/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/functional-core-imperative-shell-micro-apps/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/functional-core-imperative-shell-micro-apps.png" referrerpolicy="no-referrer">
            </div><p>Functional core, imperative shell usually gets pitched as a full architecture: pure domain functions in one layer, all I/O pushed to the edges, everything wired together through explicit ports.</p>]]></description></item><item><title>Handovha: A Verifiable Handover, Without Accounts</title><link>https://tldkhatai.com/posts/handovha-architecture/</link><pubDate>Wed, 18 Mar 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/handovha-architecture/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/handovha-architecture.png" referrerpolicy="no-referrer">
            </div><p>Memory is not a receipt. A landlord and a tenant agree a flat is fine at handover; six months later they remember it differently. A fleet manager hands over a van with a full tank and no dents; a week later, someone disputes which of those was true.</p>]]></description></item><item><title>Building a Zero-Code Photography Portfolio</title><link>https://tldkhatai.com/posts/eyes-of-wadzi-architecture/</link><pubDate>Mon, 16 Feb 2026 00:00:00 +0100</pubDate><author>Tonderai Khatai</author><guid>https://tldkhatai.com/posts/eyes-of-wadzi-architecture/</guid><description><![CDATA[<div class="featured-image">
                <img src="/images/posts/eyes-of-wadzi-architecture.png" referrerpolicy="no-referrer">
            </div><p>Someone close to me is a photographer who needed a portfolio site but doesn&rsquo;t want to touch a terminal, a code editor, or a &ldquo;deploy&rdquo; button.</p>]]></description></item></channel></rss>