Engineering Notes

Why environment variables don’t suppress WP-CLI PHP Deprecated warnings — the phar + shebang path and a three-part structural fix

A previous post covered how to absorb PHP 8.2 Deprecated warnings from WP-CLI using a three-layer defense. The approach — prepending WP_CLI_PHP_ARGS to set error_reporting — works in many environments. But a case came up where Deprecated warnings wouldn’t disappear despite the same configuration. Tracing the cause revealed a structural reason why the environment variable never arrived. This post records that root cause and the three-part fix added in v1.6.8. Why environment variables don’t arrive — the phar + shebang execution path An agency reported that on Xserver, plugin list retrieval was failing across multiple sites (referred to here as “site A / site B”) with a large volume of …

Read more
Engineering Notes

A variable I refactored into one function — and kept referencing from another. Python lazy evaluation hid it, and an AST test finally caught it

One day the browser automation flow started failing right after plugin updates with NameError: name ‘plugin_form_selectors’ is not defined in the post-update “residual check” step. The refactor that introduced this had landed back in v1.6.1. The error didn’t surface until many rounds later. Reading the code, the cause is obvious in seconds — but nobody hit it for ages, because Python’s lazy evaluation kept the leftover reference hidden until exactly the right execution path ran. This post walks through what the bug was and how we structurally prevented its kind via an AST static-analysis test. What happened — a reference that crossed a scope boundary browser_utils.py has two functions involved: …

Read more
Engineering Notes

The day a user told me the running site looks weak — rebuilding the three-state visual hierarchy with background color

After we’d shipped the colored borders for in-maintenance and completed sites on the multi-site list, real-world usage produced an unexpected report: “the running site somehow looks weak.” Followed by: “the green border that’s supposed to stick for 24 hours doesn’t show up.” The first one was surprising. The running site has a pulsing blue border — it should be obvious. But when I looked at the screen again, yes, it did look weak relative to its neighbors. The problem turned out not to be the running-state color itself, but a hierarchy among the three states that had quietly inverted. This post walks through that visual-hierarchy collapse and rebuild, along with …

Read more
Engineering Notes

Visualizing maintenance status on the site list — blue pulsing border for running, green solid for done

When you’re running maintenance across several WordPress sites in sequence, a list view with text-only status doesn’t make “which site is being processed now” or “which ones are already done” easy to spot at a glance. A client put it plainly: “Make it visually obvious in the list which sites are in maintenance and which are finished.“ A colored border is the obvious move, but there are real choices to make. What colors? Where do we get the state from? When does the “done” mark go away? And — can we ship this without touching the backend? This post walks through those four calls and the minimal frontend-only implementation we …

Read more
Engineering Notes

Three pitfalls in a dashboard cache lifetime — boot-time restore, TTL, and partial invalidation

We added a cache-first design to the cross-site updates dashboard, then wired the site-list badge to read from the same cache. Both ships went well. But as soon as it hit real usage, we hit three distinct pitfalls around the cache’s “lifetime” in quick succession: “the badges all disappear on refresh,” “the 7-day TTL is too short,” “running maintenance on one site clears all the badges.” Each is a small spec call in isolation, but from the user’s side, all three feel like the same symptom: “badges aren’t sticking around the way I expect.” This post walks through each fix and what we got wrong. Pitfall 1 — All badges …

Read more
Engineering Notes

Detecting the running site from streaming logs — why log-order inference broke and how one marker fixed it

In a screen that runs maintenance across multiple sites in sequence, you want to show progress live: a blue border on “the site being processed now,” a green border on “completed sites.” Common UI. The approach we reached for first was “watch the streaming logs the backend emits, and infer which site is being processed.” It turned out to be surprisingly tricky — three rounds of fixes before it stabilized. This post records that trial-and-error as a design log. The first design — “site switched, so mark the previous one done” Logs arrive per-site as lines like [Site name] Starting maintenance. The first implementation was simple: // Initial implementation (later …

Read more
Engineering Notes

Implementing a dynamic OGP image generator for our blog — PHP GD, per-post 1200×630 cards

One day on our English X account, I posted a link to a fresh blog article and froze when the OGP card rendered: the image was the LP sales banner — “Stop babysitting updates. Start scaling maintenance revenue.” — not anything related to the post itself. Going back through the last seven announcement posts, every single one was showing the same LP sales banner. Articles written as technical engineering deep-dives had been quietly flowing through the X timeline for months looking like sales-promo posts. This article walks through how we found the structural cause and replaced it with a dynamic OGP image generation engine — using our own blog as …

Read more
Engineering Notes

Diff from the live server, not from your git history — when a local repo has drifted from production

An investigation agent flagged “the license API PHP returns Japanese-hardcoded messages” and we sat down to fix it. But something felt off the moment we opened the file — the version running on the production server didn’t match the latest commit in the local repo. Stranger still, production had more recent features than our local checkout. A bit of digging turned up the truth: months earlier, someone had hot-patched the production file in response to a different user issue, and that change had never been committed back to git. This post walks through how we detected that drift, and the two-stage strategy we used to merge production back into the …

Read more
Engineering Notes

Multilingual emails from a Stripe webhook — inferring language from currency

A subtle trap of taking a SaaS international: the system emails sent from your Stripe webhook. Purchase confirmation, renewal success, payment failure, plan change — four kinds of emails triggered by Stripe events. We recently discovered all four had been hardcoded to Japanese for months, sending Japanese receipts and failure notices to English-paying users overseas. The kind of bug that quietly persists forever unless you go looking. This post walks through the currency-based language inference design we landed on, plus the small mb_language trap that nearly ruined the fix. Where do you get “the user’s language” from? — three options There were essentially three design options for picking the email …

Read more
Engineering Notes

Sweeping i18n leaks with four parallel AI agents — from 300 candidates down to 60 real bugs

For any app past a certain size that’s gone bilingual, the question “how much hardcoded Japanese is still hiding in our repo?” never quite goes away. A naive grep for [ぁ-んァ-ヶ一-龯] returns thousands of hits, and the vast majority are inside translation tables, already-branched code, or comments. The real leaks are buried. For one cleanup pass we attacked this with four parallel AI investigation agents plus AST-based false-positive filtering. The result: ~300 candidates detected → ~60 real leaks → cleaned up across five rounds. This post walks through the flow and the most interesting bug it uncovered — paying English users had been getting Japanese email from the Stripe webhook …

Read more