the writing
Tech, hot takes, and life,
in no particular order.
Fourteen years of building things gives you opinions. This is where they land, alongside the occasional piece that has nothing to do with code.
How this site is built (and why it's so boring)
Astro, a few hundred lines of CSS, and a deploy I never think about. The why matters more than the what.
read itKnowing what not to fix
There's a typo in the name of an app I shipped. I'm never fixing it, on purpose. A field guide to the most senior column on a tech-debt list: don't.
read itThe idempotency key that did absolutely nothing
Three independent defenses against double-charging a customer. A user tapped three times and got billed three times. All three defenses were present, correct, and useless at once.
read itColor is a hierarchy tool, not decoration: the 80/15/5 budget
A home screen felt flat, so the instinct was to add more color. The fix was the opposite: spend color like a budget, almost all of it on the one thing the user should do next.
read itBuild for the user on one bar of signal
Most software is designed in conditions its users will never have. Here's what changes when you design for the real ones.
read it0712 vs 254712: the login bug that lives in phone-number formatting
A driver login kept returning 401 with a correct PIN, because the same phone number written four legitimate ways was four different users to the database.
read itWhy I still reach for Vue
React won the popularity contest. I keep choosing Vue anyway. And it isn't nostalgia.
read itThe confidentiality tightrope of working with many clients
My most convincing sales pitch is a project I'm legally forbidden to describe. When you build under NDA for several companies, discretion stops being legal hygiene and becomes the product.
read itFourteen years, one keyboard
A developer since 2013. Here's what I'd actually tell the version of me who was just starting.
read itBuilding address software for places without addresses
Most mapping tools assume a street number and name; much of the world has neither, so a usable address is something you assemble, not something the API hands you.
read itI don't think we'll ever hit 1000 drivers: right-sizing before you over-engineer
The agent reached for a message broker and persistent streams to serve a few dozen users; the right answer was a five-second poll and the nerve to say so.
read itYour status column is a state machine in disguise
A KYC flow tracked applicants through a varchar of freeform strings, so every typo was a silent dead-end state nobody could explain.
read itEm dashes are the new Comic Sans
One punctuation mark now quietly announces that a machine wrote your sentence, and sweeping it out is how you take your voice back.
read itCompress before upload and show the bar, or users will upload it five times
On my laptop the delivery photo uploads instantly; on one bar of signal in a dusty yard, the same photo takes five minutes with no feedback, so the driver taps again and I get three copies.
read itThe demo where everything that could break, broke
Software isn't tested until real users hit it at the same time on the same shaky network, and a live demo is where they all arrive at once.
read itThe notification that wakes a locked phone is a feature, not a nuisance
Every guide tells you to design notifications that respect Do Not Disturb. For a driver waiting on work, the notification that ignores it is the entire product.
read itPut a QR code on the paperwork so the document can't lie
In a paper-heavy market, a printed invoice is something people pay against, which makes it an attack surface. So make the paper verify itself.
read itBuilding payments for a world that pays by cheque
Half the payments content online assumes cards and instant settlement; my customers send a cheque, and the data model has to fit how money actually moves here.
read itMoney is integer cents, and the tax rate freezes on the line
Two boring data-modeling rules buy you a year without phantom accounting bugs: store money as integer cents end to end, and freeze the tax rate onto each line at creation.
read itYour customer API is quietly leaking your margin
Two endpoints correctly hid the markup. A third, written on a different afternoon, handed it to any logged-in customer through the network tab.
read itPlan for the day the App Store says no
If a single reviewer can pull your app without warning, you don't own a product, you own a permission slip that someone else can revoke.
read itThe client project that quietly became a product
I knew I'd built a product the day I edited the same notification service twice in one sitting, in two different backends, fixing the identical bug in both.
read itYour idempotency key triggers a CORS preflight that bills the user twice
The custom header I added to stop double-charges was the exact thing CORS silently stripped, so the safety mechanism became the bug it was supposed to prevent.
read itSix drivers crashed the API: when a streaming endpoint pins a worker per connection
A live demo died with six active users, and the cause wasn't slow code, it was arithmetic: long-lived streams each ate a worker until the pool ran dry.
read itSpec before you build, especially with an agent
A fast coding agent pointed at a fuzzy target doesn't slow down when it's confused, it accelerates, and produces a lot of confident, wrong code very quickly.
read itThe silent-notification trap: why a driver swears the phone stayed quiet
A driver insisted the phone never made a sound for a job worth real money, and he was right: the OS had quietly demoted my correct notification to silent.
read itThe last client's name is still hiding in this codebase
I shipped a brand-new product, and the last client's name was still quietly running the auth flow and routing my alert emails.
read itWhen the agent is making it worse, take the keyboard back
When you've patched the same bug five times, the bug isn't in the code the agent keeps rewriting. The bug is the architecture you keep asking it to defend.
read itMost clients don't need multi-tenant: single-tenant by default, architect the easy exit
Multi-tenancy is the tax everyone agrees to pay the moment they smell a product, for a feature most customers will never use.
read itMake it impossible to touch the wrong environment
I built a staging system that looked identical to production, so an ops lead actioned a real customer's order on the test database, and the fix was the ugliest UI I've ever shipped on purpose.
read itBuild the wipe-the-demo-data button before your first prospect logs in
The worst thing a prospect can see in your demo is your own junk data, and the page you build to wipe it is itself the next security hole.
read itCode flows downhill: main to staging to prod, no cherry-picking
The second you cherry-pick a hotfix straight to prod, you've built an environment containing code that exists nowhere else, and what's actually live becomes a research project.
read itI'm exhausted, keep building, play a sound when you're done
The strangest new texture of the job: handing an agent a real chunk of the build, saying I'm wiped, and going to sleep. Plus the 8.5GB that wasn't my bug.
read itRoles are a template, permissions are the truth, and the screen should teach the admin
An admin changed a role and nothing happened, which was exactly correct and exactly the bug, because a role is a stamp you press once, not a wire that stays connected.
read itDesign for thumbs on a steering wheel
A field app gets read in one glance by someone distracted and on their feet. I packed every link onto the screen, and the driver could miss the only thing that mattered.
read itSometimes the feature is refusing to let them skip a step, and showing why
An admin asked to invoice an order before it was delivered. The right feature was to say no, out loud, before they ever reached the button.
read itDesigning a map that gets out of the way
Building a good in-app map is almost entirely an act of deletion, and the only thing I let stay is the one label nobody thinks about.
read itYour clean schema meets the chaos of real deliveries (and a label is not a workflow)
A 'Customer Returns' dropdown is not returns tracking, and the maturity of a logistics system is how gracefully it names every way a delivery goes sideways.
read itDon't make customers file a complaint, walk them through it
When a delivery goes wrong, the worst thing you can show is a blank 'describe your issue' box. You'll lose the signal exactly when you need it most.
read itHalf my bugs came from asking: what does this date even mean?
One column was doing two jobs and defaulting silently to today. The most expensive bugs aren't crashes, they're fields that quietly mean two things and pass every test.
read itThe glorified calculator
Kenya's mandatory tax register has the computing power of a calculator and the price of a MacBook Pro. That isn't an accident.
read it