- stevencodes.swe
- Posts
- stevencodes.swe - August 3, 2025
stevencodes.swe - August 3, 2025
War story, sharing my visuals, creator update
👋 Hey friends,
Here’s what I’ve got for you this week:
Creator Update: PSA - imposter warning
Snippet from Chapter 3 of The Backend Lowdown
Community Corner: sharing my visuals
War Story: Coupon Code Meltdown
Let’s get into it! 👇
Creator Update: PSA - Imposter Warning
A few people brought it to my attention that someone set up another TikTok account with a lookalike handle using 2 dots instead of 1. First, I’m very grateful to those who let me know. That meant a lot to me! Second, please know that @stevencodes.swe (1 dot) is my only TikTok account and that other account is not me. I have reported them to TikTok, but apparently that’s all I can do. Please stay safe!
The Backend Lowdown: Chapter 3 Preview
Every newsletter will include a snippet from my book in progress, The Backend Lowdown, available for $1 right now on Gumroad!
Side note: Chapter 3 was just released last week! Go check it out!
Pagination Performance Cliffs
The most dangerous performance problems are the ones that work perfectly in development. Pagination is the poster child for this category: your local dataset of 100 records pages beautifully, while your production database with 10 million records is slowly dying as users browse deep into your result sets.
What Goes Wrong
OFFSET/LIMIT pagination looks deceptively simple and works exactly as you'd expect until you start dealing with many records:
-- Page 1: Lightning fast
SELECT * FROM products ORDER BY created_at DESC LIMIT 20 OFFSET 0;
-- Execution time: 2ms
-- Page 500: Database destruction
SELECT * FROM products ORDER BY created_at DESC LIMIT 20 OFFSET 10000;
-- Execution time: 800ms
Here's what your database actually does with OFFSET 10000
:
Builds the full result set from the beginning
Sorts all those rows according to your ORDER BY
Counts and discards the first 10,000 rows
Finally returns rows 10,001-10,020
You're forcing the database to do 99.8% wasted work to return 0.2% of the results.
Think of it like reading a book by starting from page 1 every single time, counting pages until you reach page 500. It doesn't matter that you have a bookmark, OFFSET pagination throws it away and starts counting from the beginning.
Community Corner: Sharing My Visuals
I recently started adding animations to my videos and there have been a lot of you asking about how I make them and to share them, so I figured I’d share this out here! The tools I use are:
Excalidraw to represent some of the data structures
ChatGPT / DALLE for a lot of the static images
Anthropic models for the animations
I prompt the LLMs to generate the assets, but as with any LLM right now, it’s hit or miss and sometimes I have to give up or do something else. It’s also quite time consuming as they usually require a fair bit of tweaking.
If you’re curious to see some of the animations, I’ve created a GitHub repo to start sharing them out. Check it out here: https://github.com/stevencodes-swe/video-visuals
I’ll be sure to keep adding them and will continue adding those from past videos. Some of them are interactive and fun to play with!
War Story: Coupon Code Meltdown
9 AM sharp, the Black Friday promo hits. Ads are humming, traffic is surging, and our Rails checkout API is holding steady, for a little while. New Relic lights up: 500 errors on POST /checkout
rocket from 0 -> 15% in under three minutes. Latency jumps from 250 ms to 4 s.
The red-herring fixes
We doubled Puma workers and added pods - CPU usage fell, but errors didn’t.
Bumped DB pool size - it cleared ActiveRecord queueing, but p95 was still at 4 seconds.
In our APM, every flame-graph sample funneled into one Postgres call:
SELECT "coupons".*
FROM "coupons"
WHERE LOWER("coupons"."code") = $1
LIMIT 1;
The real culprit: uniqueness validation
Days earlier Marketing demanded case-insensitive coupon codes, so someone added this to app/models/coupon.rb:
validates :code, uniqueness: { case_sensitive: false }
ActiveRecord enforces that by issuing LOWER(code) = ?
queries whenever a coupon is created or updated. Our checkout flow increments a redemptions_count
column inside a transaction, so every purchase triggered that validation-lookup. Under normal load it was fine; under Black Friday traffic, the lookup devolved into a sequential scan because we only had a plain btree (code)
index. Tens of thousands of shoppers meant tens of thousands of seq scans contending for the same rows until Puma hit its request timeout and threw 500s.
The 15-minute rescue
Add the right index (concurrently):
CREATE UNIQUE INDEX CONCURRENTLY idx_coupons_lower_code ON coupons (LOWER(code));
Drop the old plain index once the new one finished.
Delete the Rails-level validation and let the database’s unique functional index handle truth.
Optional hardening (we did this later): convert
code
to Postgres CITEXT to make future queries naturally case-insensitive.
The moment the index went "VALID", seq scans vanished and p95 checkout latency nosedived back to normal levels.
Takeaways
Database > model validations for uniqueness, especially on hot paths.
If you query on a transformation, index the transformation or use citext.
Load testing must mirror promo behavior. Our staging run never hit case-insensitive coupon redemptions at scale.
Watch your flame graphs; they pinpointed the hotspot long before we pieced together the narrative.
That’s a wrap for this week. If something here made your day smoother, feel free to reply and tell me about it. And if you think a friend or teammate would enjoy this too, I’d be grateful if you shared it with them.
Until next time,
Steven