Journal
Problems I ran into in production, what caused them and what fixed them. Two posts a month, in English and Turkish.
2026
Making technical debt visible enough to pay
Debt nobody can see never gets budget. Give each item a cost, an interest rate and a place on the same list as features, then pay it inside feature work.
Where your team writes down its decisions
Decisions live in chat threads and in people's heads until they vanish. A one paragraph record next to the code keeps the reasons where the work is.
Five questions for the first meeting with a client
Ask what success is in numbers, what must keep running, who owns the accounts, what was tried before, and who decides. Then write the answers down.
Debugging: measure first, guess later
The order that finds bugs is reproduce, narrow, measure, then theorise. Here is how to bisect changes and inputs, and why the first theory costs the most.
Long jobs get finished in small reversible steps
How to cut a large piece of work into steps that can each ship and each be undone, with feature flags, expand and contract migrations and a real rollback.
Lab numbers, field data and a real phone
A synthetic audit, real user data and a phone in your hand answer three different questions. Here is when each one lies and how to measure a change.
An eleven kilobyte home page: the case for static sites
What pre rendering buys you, what it costs, and the build pipeline, page weight budget and caching rules that keep a static site small and boring.
The scheduled job that never runs at midnight
A schedule written in one zone on a machine running another, plus daylight saving twice a year, makes a nightly job run late, twice or not at all.
Your backup is a zero byte file and nobody noticed
Backups fail quietly: a missing tool in the container, a permission, a full disk. Check the size and the row counts, and restore on a schedule.
Three fields that make logs worth reading
Most logs cannot answer the one question an incident asks. A correlation id, the subject being worked on and an outcome with a reason fix that.
What your AI feature does when the credit runs out
A model provider can fail for billing, quota or an outage. Without a fallback ladder every user sees the same generic error. How to degrade on purpose.
Keeping an AI feature affordable in production
An AI feature that works can still produce a bill nobody planned for. Where the tokens go, what to cache, what to trim and how to cap spend per user.
Payment webhooks: signatures, replays and idempotency keys
Verify the signature over the raw body, reject old timestamps, store the event id, never trust the amount. A compact handler and the checks around it.
From a slow query to the right index
Find the query, read the plan, then choose the column order. A practical route from a slow page to an index that actually gets used, and its cost.
Idempotent jobs: doing the work exactly once
Queues and webhooks deliver at least once, so every handler runs twice eventually. How to make the second run harmless with a key and a constraint.
Two accessibility mistakes almost every site makes
Heading levels chosen to match the layout, and an aria-label that hides the visible text. Both break keyboard and voice users, and both are cheap to fix.
2025
Self hosted fonts, privacy law and one request less
A hosted font stylesheet hands every visitor IP address to another company before your page renders. Self hosting removes the transfer and a round trip.
What the responsive images audit is really telling you
The audit compares the file you sent with the pixels the device needs, which is layout width times pixel ratio. Here is how to read it and what to change.
Invisible dot underscore files travelling from macOS to your server
Extended attributes become AppleDouble files inside archives made on a Mac, land in your web root and get served. How to spot, stop and clean them up.
The rsync flag that breaks certificate renewal
A mirroring deploy removes everything the build did not create, including the path a certificate authority reads. How to catch it weeks before the expiry.
Blue green deploys: check the build exit code before you restart
A deploy script that restarts after a failed build serves a half written directory. Exit codes, an atomic switch, a smoke test and a rollback that works.
A streamed response that never closes hangs for a hundred seconds
Tokens arrive, the answer is complete, and the request stays open until a proxy kills it. How to close a stream and test it through the real domain.
Proxy tunnels that die after ten idle seconds
A tunnel that closes on idle turns one long session into thousands of reconnects. On metered bandwidth the handshake traffic quietly becomes the bill.
How a misconfigured root exposes your .env to the internet
When the web root points at the project directory and the server falls back to any file that exists, your .env and .git are one plain request away.
Never write to /tmp with a generic name on a shared server
On a shared machine a generic temp filename may already exist and belong to someone else. The write fails, the read succeeds, you ship the wrong file.
Log rotation, retention and the silent cost of a full disk
A disk creeping towards full is a slow outage: writes get refused, builds fail, backups truncate. How to find what grew and stop it growing back.
A hundred thousand rows pasted into a form: move it to a queue
A synchronous bulk import dies at the proxy timeout, leaves half written state and charges the user twice. Accept, store, return, then work in chunks.
The invisible suffix that split every message in two
Two features each appended five characters after the length check ran. Every message crossed a segment boundary and the cost doubled in silence.
Why one Turkish character can double your message cost
Short messages use a 7 bit alphabet with 160 characters, or a 16 bit one with 70. A single accented letter switches the whole message and the bill.
The delivery report that arrives before the record
An asynchronous callback can reach you before the row it refers to is committed. Keep the orphan, retry the match, and stop losing final states.
Treating a rate limit as a permanent failure is expensive
A 429 or a protocol throttle means come back later. A client that files it as a permanent failure throws away paid work and hides the real capacity.
From 300 to 19,000 an hour: where sending throughput actually goes
A sender stuck at 300 messages an hour is not short of CPU or bandwidth. It is waiting for one round trip at a time, and the fix is measurable.
The campaign that never sent: time zones in the database
A job scheduled for 09:00 sits in the queue with no error at all. The application wrote local time, the worker reads UTC, and the offset does the rest.
Mail lands in spam: the From alignment nobody checks
SPF and DKIM can both pass while mail keeps landing in spam. The header that explains it names three domains, and DMARC only accepts one answer.
A strict content security policy with an inline first paint
Inlining critical CSS and a small script is good for first paint and illegal under a strict CSP. Hashes fix it, if you compute them at the right moment.
Your entrance animation is delaying your largest paint
An element that starts at opacity zero is never painted, so a fade in pushes Largest Contentful Paint out by the delay plus part of the animation.
When PNG beats AVIF: images made of hard dots
Lossy codecs are built for photographs. On a halftone screen an indexed PNG beat AVIF at every width, and the only way to know is to measure both.
Subset the fonts, match the fallback metrics, stop the layout shift
A page that renders 127 characters should not download 889 glyphs. Subset at build time, metric match the fallback, and the font swap stops moving things.
The Turkish uppercase trap: i, I and the dotted İ in code
In Turkish, i uppercases to İ and I lowercases to ı. Case folding, CSS text-transform and database collations all get this wrong by default.
2024
Justified Turkish text without a hyphenation dictionary
Chrome ships no Turkish hyphenation dictionary, so justified Turkish opens rivers of white space. Insert soft hyphens at build time instead.
The reboot that took the site down: process managers and boot persistence
A process manager only brings apps back after a reboot if its service unit is enabled and the process list was saved. How to check both and prove it.
When your control panel and your nginx file disagree
Hosting panels re-render the vhost file from their own template, so hand edits vanish at the next certificate renewal. Where to put the change instead.
The nginx rule that quietly drops your security headers
In nginx, add_header is only inherited when the child level defines none of its own. One cache header in a location removes HSTS and nosniff from it.
Why redirecting every missing page to the home page costs you in search
A blanket redirect from missing pages to the home page creates soft 404s, wastes crawl budget and hides broken links. Serve a real 404 instead.
A cached 301 can break your site after a redesign
Browsers keep permanent redirects for good. If a new site reverses an old 301, returning visitors land in a redirect loop. Here is how to find it and fix it.