← blog/posteptrack-v4-same-bug-twicePT
    // blog/posteptrack-v4-same-bug-twice.md

    PostepTrack v4: We Stopped Fixing the Same Bug Twice

    Two clients each fixed half of the same silent-failure bug. A third one still had it intact. That became a pattern.

    2026-08-14·7 min read·engineering·POSTEP Digital

    Hardened 204 · phone cascade · exceptions never vanish · retry queue · zero errors on deploy

    // 00

    The same bug, fixed twice

    Back in June we wrote about 5 pitfalls of Meta Ads → CRM attribution. One of them was the HTTP 204 Kommo returns when it can't find a lead — which turns into a silent exception if you call .json() without checking the status first.

    What we didn't tell you: two clients fixed that bug independently, neither knowing the other had the same problem. One hardened the 204 parse. The other built a phone lookup with multiple variants and a retry queue. Neither had the full package.

    And a third client — in production every single day since April — still had the exact same 204 bug, untouched. Nobody had noticed. We found it by auditing the real code that was actually deployed, not the docs describing what was supposed to be running.

    // 01

    The four pieces nobody had put together

    Instead of just fixing the third client and moving on, we stopped to synthesize the two partial fixes into a single package — and made that package a mandatory baseline for every new PostepTrack instance.

    // v4 — the hardening package
    1. bodyless HTTP 204 try/catch + defensive parse
    2. phone lookup 4 variants in cascade
    3. unhandled exception always written to `error`, never silent
    4. lead not found automatic retry queue

    The phone cascade exists because the CRM sometimes indexes the contact in a different format than what Meta sends: it tries the raw number, then with a “+”, then digits only, then the Brazilian variant without the mobile 9th digit. It only fires on a miss — the happy path stays a single call.

    The retry queue is an authenticated route (?action=retry) called every 5 minutes by an external schedule. A lead not found right away becomes recovery_pending=true instead of a final error — and gets retried for up to an hour before giving up for good.

    // 02

    Zero errors on the first real deploy

    We shipped v4 to the client that had been in production since April with the 204 bug fully intact. Rule we followed to the letter: audit the real code before touching anything — not the docs, the file actually deployed.

    // deploy protocol — no step skipped
    1. live schema check before migrating
    2. additive migration ADD COLUMN IF NOT EXISTS
    3. surgical merge on real code never a rewrite
    4. deploy via CLI only never inline content
    // an inline deploy once published "PLACEHOLDER" over
    // production for 53s. expensive lesson, learned once

    After deploy, four live checks — never just trusting the CLI's “success” message: the endpoint answers the verification challenge, a wrong token gets rejected, the retry route without the secret is blocked, the retry route with the right secret returns the expected JSON. Plus an out-of-band confirmation, straight from the API, that the new function was actually live.

    Result: zero errors post-deploy. And not a single line of the client's business logic was touched — two historic UTM quirks and the routing of 9 WhatsApp numbers across 4 different accounts stayed exactly as they were.

    // 03

    The rule: hardening never overrides the business

    Reliability is orthogonal to business rules. If a client decided utm_campaign belongs to another system, or that a specific WhatsApp number routes to a specific team, that doesn't change because we improved error handling underneath.

    There's a case in the queue right now with one extra wrinkle: a client whose conversion event to Meta only fires after the lead is found in the CRM — meaning it never fires for leads that take a while to show up. Decoupling the two makes technical sense. But it also changes the volume of data feeding an active campaign's optimization.

    So we split it into two phases: the technical hardening ships now, without touching a single line of business behavior. The change that affects campaign data gets documented, measured before it's decided, and only ships with explicit approval — separate from the reliability deploy.

    Rule: if the change only makes the system more reliable, it's neutral and can ship. If it also changes what the system decides or reports, it's a business decision disguised as a bug fix — and needs to be treated as one.

    // 04

    Summary: from lesson to pattern

    the problem

    the same silent-failure bug (mishandled HTTP 204) got fixed from scratch in two different clients, and was still untouched in a third one four months later.

    the synthesis

    the two partial fixes became a single 4-piece package: hardened 204, phone cascade, exceptions always logged, automatic retry queue.

    the deploy

    audit of the real code (not the docs), additive migration, surgical merge, CLI-only deploy, 4 live checks. Zero errors post-deploy.

    what didn't change

    no client business rule was touched. Hardening is orthogonal to the decision — never an excuse to force clients into one shape.

    // the main rule

    A bug fixed twice in two different places isn't bad luck — it's a signal that a pattern is missing. The lesson becomes a post; the pattern becomes code every new client is born with.

    written by
    POSTEP Digital
    ← see all posts