Procedures

Written as procedures someone else can repeat, not as adjectives. For each one I write down what it actually settled, and what it does not catch.

1Draw the boundary with 400 and 405

When to use it

"It worked yesterday and it does not work today." When there are several candidate causes, this is what I use to cut the search space in half before digging through logs.

The procedure

A status code does not only say "it failed." It says how far the request got.

curl -s -o /dev/null -w '%{http_code}\n' \
  -X POST https://<host>/api/<path> \
  -H 'Content-Type: application/json' -d '{}'
ResponseWhat it meansWhere to look next
400The path exists and the handler ran. The body was rejected for not matching the schemaRequest body, validation rules
405The path exists but that method does not. The handler is missing from the deploymentBuild output, bundler config
404The path itself does not existRouting, file layout
200It arrived and was processed. If there is still no result, the problem is further downStorage, notifications

I send an empty body {} on purpose. A healthy endpoint must reject it, so the way it rejects becomes the diagnosis.

What it settled

A live site had a report that enquiries were not coming through. The page looked fine. I sent the same request at two deployments.

deploy from 2 days ago   POST /api/lead → 400   handler alive
deploy from today        POST /api/lead → 405   handler gone

Two lines settled "since when, and because of what." A static-only deploy had dropped the server function entirely, and intake had been down for about four hours. After the fix I froze the verification into the deploy procedure — 400 means healthy, 405 means the deploy failed.

What this does not catch

2Decide the source of truth with hashes

When to use it

"Where is that file?" / "I fixed it, then the deploy brought the old one back." When there are several copies, you cannot tell by looking which one is live.

The procedure

Hash what you pull from live and hash the local candidates the same way. Do not compare by eye.

LIVE=$(curl -s "https://<host>/?cb=$RANDOM" | shasum -a 256 | cut -d' ' -f1)
for f in <candidate1> <candidate2> <candidate3>; do
  printf '%-40s %s\n' "$f" \
    "$([ "$(shasum -a 256 "$f" | cut -d' ' -f1)" = "$LIVE" ] && echo match || echo differs)"
done

Always add the cache buster. Without it, a cached old response matches an old copy and you reach the wrong conclusion.

What it settled

The source of a live site was in no git repository anywhere. I compared eight local candidates against live.

8 candidates  →  matching live: 0

None of them was the source of truth. Files had been uploaded directly with no git integration, so no history existed. In that state, anything anyone fixes can disappear on the next deploy. I verified the live output by hash, pinned it into the repository as the canonical copy, and made the deploy one-directional.

What this does not catch

3Count broken word wraps

When to use it

"Something feels off and I cannot say what." Usually it is not the content, it is the fundamentals. The most common one is Korean breaking mid-word at the end of a line.

저희는 사이트를 점검하고 개선 합니다

Before the reader judges the content, they feel "nobody proofread this."

Why it happens

The browser default word-break: normal treats CJK as breakable at any syllable. Latin script only breaks at spaces; Korean breaks between any two characters.

body { word-break: keep-all; overflow-wrap: break-word; }

overflow-wrap: break-word is the escape hatch for unbreakable long tokens such as URLs. Using anywhere overrides keep-all and the text breaks again.

The procedure

Do not count by eye. Split the text into words with Intl.Segmenter, then check whether each word's getClientRects() spans two lines. If it does, it is broken.

(() => {
  const seg = new Intl.Segmenter('ko', { granularity: 'word' });
  let checked = 0, broken = [];
  const tw = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
  let n;
  while ((n = tw.nextNode())) {
    const t = n.nodeValue;
    if (!t || !/[가-힣]/.test(t)) continue;
    const p = n.parentElement;
    if (!p || !p.offsetParent) continue;
    const r0 = p.getBoundingClientRect();
    if (r0.right < 0 || r0.width <= 1) continue;      // skip offscreen and honeypots
    for (const s of seg.segment(t)) {
      if (!s.isWordLike || !/[가-힣]/.test(s.segment) || s.segment.length < 2) continue;
      checked++;
      const r = document.createRange();
      r.setStart(n, s.index);
      r.setEnd(n, s.index + s.segment.length);
      if (r.getClientRects().length > 1) broken.push(s.segment);
    }
  }
  return { checked, broken };
})()

It passes only at 0 across all four widths — 390, 768, 1280 and 1440px. Narrow screens expose it first.

What it settled

I found and cleared this to zero across four live sites and 25 showcase pages. Pages that declared keep-all had zero; pages without it always had some. There were no exceptions.

What this does not catch

What all three taught me

When a measurement reports a failure, suspect the instrument first. Using these three procedures I produced wrong verdicts several times, and most of them were faults in how I was measuring, not in the code or the site.

Before reporting a result, I check once more how that number was produced.