Blog

  • Clients do not leave over code

    I have watched a few accounts end. In almost none of them was the code the reason.

    The build was fine. Late sometimes, imperfect sometimes, but fine. What went wrong was that the client stopped being able to predict what was happening, and prediction is most of what they are paying for.

    The shape it takes

    It rarely starts with a complaint. It starts with the client asking for an update slightly more often than they used to.

    That is the signal, and it is easy to misread as a demanding client. It is not. It is someone whose confidence has dropped below the level where they can wait for the scheduled update, so they are topping it up manually. The extra ask is them compensating for something your process stopped providing.

    Left alone it goes one way. Updates get requested more often. They get answered with more reassurance and less specifics, because the specifics are uncomfortable. The client starts copying more people in. Then there is a call about “how we’re working together” rather than about the work.

    Late is survivable. Late and unexplained is not

    Clients absorb bad news far better than most teams expect. A date moving two weeks, flagged early with a reason and a revised plan, is a normal conversation. It is a project, things move.

    The same two weeks discovered by the client at the point it becomes undeniable is a different event entirely. Not because the delay is worse, but because they have just learned that their view of the project was wrong — and once that is established, every future update is discounted. They stop asking “is this on track” and start asking “how would I know”.

    That is much harder to come back from than a slipped date.

    Why good teams still get this wrong

    Not through negligence. Because bad news has a natural delay built into it.

    Nobody wants to escalate something that might resolve itself by Thursday. So the reasonable instinct is to wait until it is certain — and by the time it is certain, it is also late, and now the conversation is about both the problem and why it was not mentioned sooner.

    The fix is not to try harder. It is to remove the judgement call. If a defined condition means a client hears about it — a task past its date by some threshold, a dependency unanswered for some number of days — then nobody has to decide whether this one is worth raising. The threshold decided already.

    Teams resist this because it feels like it will generate noise. In practice it generates fewer, earlier, smaller conversations instead of one large one, and it takes the emotional weight out of escalating — which is the actual thing that was blocking it.

  • The form saved every lead and showed visitors a 404

    A form on my site had a field called name. Reasonable field name. It broke the page in a way that took me a while to understand, because it broke it in both directions at once.

    What the visitor saw

    They filled in the form, pressed submit, and got “Page not found.”

    What actually happened

    The submission was received. It was validated. It was stored. Everything worked.

    WordPress reserves a set of public query variables — name, author, cat, paged, order and a couple of dozen others. When a request arrives carrying one of them, WordPress tries to resolve a post with that slug. There was no post called “Brij Raj Singh”, so it decided the request was a 404 and picked the 404 template.

    But the hook my handler runs on fires after that decision and before the template renders. So the lead was saved perfectly, and then the visitor was shown a page telling them nothing existed.

    Why this one is nastier than a normal bug

    If the form had simply failed, I would have found it in a day. Someone would have said “your form is broken” and the fix would have been obvious.

    Instead the two halves disagreed. My database said the form worked — leads were arriving, so from the inside there was no problem to investigate. The visitor’s screen said it did not. And the people best placed to report it were the ones who had just been told the page did not exist, which is not a state that inspires anyone to write in and mention it.

    Any lead that came from someone who did not already know me would have been lost at exactly the moment they were most willing to talk.

    The fix, and the wider point

    Every field on the site now carries a prefix, generated through one helper so nobody can add a field that skips it. It sidesteps the entire class of collision rather than fixing the one field that happened to hit it.

    The wider point is about how I now test things. It is not enough to confirm the record arrived. You have to check what the person on the other side actually saw, in a browser, as a stranger — not logged in, not with your own cookies, not by reading the database and concluding it must have worked.

    Same principle as any delivery process. “The report was sent” and “the client understood where the project is” are different claims, and only one of them is usually measured.

  • My buy button 404d for a week and nothing told me

    I renamed my Gumroad account to tidy up the URL. The old one had a number in it that looked like a placeholder, and the new one matched the brand. It took about ten seconds.

    Every buy button on my site pointed at the old address. Gumroad does not redirect a renamed vanity URL. So every one of them started returning a 404.

    Nothing told me

    This is the part worth dwelling on. The site did not error. Analytics showed clicks on the buy button, exactly as before. Uptime monitoring was green, because my site was fine — it was the destination that had gone.

    From every angle I was set up to look at, things were working. The only signal available was an absence: no sales. And no sales is indistinguishable from a slow week when you are starting out.

    The class of failure this belongs to

    Loud failures are cheap. A 500 page gets noticed in an hour, because it is obvious and someone complains.

    Expensive failures are the ones where the system reports success. The click fires. The form submits. The page loads. Something further down the chain quietly does not happen, and every dashboard you own is looking at the wrong end of it.

    Delivery work has exactly the same shape. A status report that goes out on time but to a stale distribution list. An escalation raised in a channel nobody watches. A handover document written and never opened. The process ran. The outcome did not happen. And because the process ran, nothing flags.

    What I changed

    Two things, both small.

    Commerce destinations are no longer written into pages. They live in one setting, referenced by name, so changing where a button points is a single edit rather than a hunt through content. That is a fix for the specific bug.

    The more useful change was the habit: when I change an address, I now check the thing at the other end of it. Not the click. The arrival.

    It is a short list of places where that applies — payment links, form actions, webhook endpoints, redirect targets, notification addresses. Worth writing down once for whatever you run, because in every one of those cases the failure is silent and the metric you would normally trust keeps reporting green.

  • Engineering is rarely what is slipping

    When a project slips, the first question is almost always technical. What is blocking it, who is stuck, do we need another pair of hands.

    Sometimes that is the right question. In my experience it usually is not.

    What slipping usually looks like up close

    Work sat waiting for a decision nobody realised was needed. A requirement meant one thing to the client and another to the person building it, and the gap surfaced at review rather than at kickoff. Something was flagged on a Tuesday in a channel the person who could act on it does not read. Two people solved the same problem in different directions because neither knew the other was on it.

    None of those are engineering failures. They are coordination failures, and they are wearing engineering’s clothes because engineering is where they eventually become visible.

    Why we look at the wrong thing

    Because engineering is measurable and coordination is not. Tickets have states. Builds have durations. You can point at a branch.

    The three days a decision spent waiting for someone to notice it was needed do not appear anywhere. There is no field for it. So when the retrospective asks what went wrong, the honest answer — “the work was fine, the space around the work was slow” — has nowhere to be written down, and something more tractable gets written instead.

    Then the fix matches the diagnosis. More capacity, better estimates, a stricter definition of done. And the same thing happens again next quarter, because the actual bottleneck was never named.

    The test I use

    When something has slipped, walk it backwards and mark every point where the work was not being worked on. Waiting for a reply. Waiting for access. Waiting for a decision. Waiting for someone to read a message.

    Then compare that total against the time the work was genuinely in progress and genuinely hard.

    If the waiting is bigger — and it often is — then hiring another developer will not help. You have a communication problem with an engineering symptom, and the two need different fixes.

    This is not an argument against good engineering

    Bad architecture is real and it is expensive. Technical debt is real. Some things slip because the problem was genuinely harder than anyone thought, and that is a legitimate answer.

    The argument is narrower: before you conclude the engineering is the problem, check how much of the elapsed time the engineering was even happening. That number is usually available, nobody usually looks at it, and it changes what you do next.

  • The same status update gets typed four times

    Ask a delivery team where their week goes and they will tell you about the work. Ask them to track it for five days and something else shows up.

    The same piece of status gets handled four or five times before it reaches the person who asked for it.

    Follow one update through a week

    A developer finishes a task on Tuesday. Here is what typically happens to that single fact:

    • They move the ticket. That is the system of record, and it is the only step anyone would call “reporting”.
    • They mention it in standup, because nobody trusts the board to be current.
    • The PM types it into a running notes doc, because the board does not explain why it took longer than expected.
    • On Thursday the PM copies it into the client’s status template, reworded, because that client wants outcomes rather than tickets.
    • On Friday it gets summarised again for an internal delivery review, this time next to four other projects.

    Five handlings. One fact. And every handling is a chance for it to drift out of sync with the other four.

    Why it never shows up in a plan

    Because no single instance of it is expensive. Moving a ticket takes seconds. Typing a line into a doc takes a minute. Nobody has ever put “reformat Tuesday’s update for the client” on a sprint board, and nobody ever will.

    It is spread thin enough across the week that it feels like the texture of the job rather than a task. That is exactly what makes it durable. Costs that are visible get argued about and eventually fixed. Costs that are invisible just get absorbed, and they get absorbed by whoever has the most context — which is your most experienced person.

    The duplication is the problem, not the reporting

    This is where most attempts to fix it go wrong. A team decides reporting is the problem, so they add a template, or a new board view, or a weekly form. Now there are six handlings instead of five.

    The thing worth attacking is not that status gets reported. Clients need status. It is that the same information is entered by hand in more than one place, in a different shape each time, by someone senior enough to make the judgement calls about wording.

    Once you see it that way the question changes. Not “how do we report better” but “where is the single place this gets captured, and what can generate the other four from it”.

    A way to see it in your own team

    You do not need a tool for this. Take one project and one week, and write down every place a piece of status was typed, pasted or reworded by a person. Do not estimate it from memory — memory is what has been hiding it.

    Two things usually come out of that exercise. The count is higher than anyone guessed, and at least one of the destinations turns out to be something nobody actually reads.

    That second one is free time. You can stop doing it this week, before you automate anything at all.