What should a machine check on a website?
A hand-written site has no framework to warn you when something breaks. A checking script fills that gap — and every one of its sections exists because of a real failure.
A framework catches a lot on its own. Mistype a route and the build stops; leave out a component and the compiler says so. Writing HTML by hand means losing that net. In exchange you know exactly what is on the page — but nobody tells you when a link leads nowhere, or when the structured data has drifted away from what is on screen.
The answer isn't to be more careful. It's to write down once what I would have to watch for, and run it after every change.
- 01 editing
- 02 checking
- 03 publishing
nothing ships while anything fails
What a validator cannot look at
Formal checking has ready-made tools. An HTML validator tells you when an element is left unclosed; a link checker tells you when a link returns 404. These matter, and the first sections of the script do exactly that: they confirm that every page has a description and a canonical URL, that language pairs point at each other reciprocally, that the sitemap covers every page, and that the structured data is valid JSON.
The harder part is what no general tool can do, because it doesn't know the site. On this site, for instance, the privacy notice states that the page sends no request to any external server. That is not a matter of taste; it is a legal claim in a document. If someone pulls in a font from a CDN tomorrow, the notice becomes false from that moment on — and no validator will say a word, because syntactically everything is fine.
Hence a separate section that walks every src and
href on every page and fails if any of them points at a
foreign host. It doesn't protect the code. It protects the text.
The checks that guard my own rules
The genuinely useful sections are the ones enforcing a decision I made and would eventually break myself.
One is the expiring time expression. If a case study says
I have been running it for over a year, that sentence is true on the day
it is written, inaccurate six months later, and absurd after two years —
and nobody will update it. In Hungarian the suffix gives it away: one form
anchors to the present, another closes off a period in the past. The latter
never expires. The script hunts for the former with a pattern list.
The other is the item count. A collection page must never
state how many entries it has: “eleven case studies” becomes a lie the
moment the twelfth appears. The same check also compares the
numberOfItems in the structured data against the actual list
length — the kind of mismatch nobody spots by eye, because the two numbers
live in different places.
A third such section is client-name leakage. In the
case studies a client's name appears in exactly one place, and even
there it is marked; the check fails if the name turns up anywhere else:
in a title, a description, link text, or structured data. There's a
separate piece on that: why
noindex isn't the answer.
What a failure taught
None of the sections were designed in advance. Each was added when something had already broken.
Font coverage, for example, came from a Hungarian long ő that didn't exist in the chosen font file, so the browser quietly swapped to another face — in that one word only. On screen you notice this if you happen to be looking at that word. Since then the script compares every character on every page against the character sets of the font files.
The diagram classes section came from a mistyped class name in an SVG figure. The drawing appeared, just without styling: not missing, only wrong. The check now compares class names used in figures against those that exist in the stylesheet.
And placeholders is the simplest and most useful of all:
it looks for leftover TODO, LOREM and my own
markers. A half-finished passage stays in far more easily than one would
think.
What it does not check
The script doesn't tell me whether the writing is any good. It has no idea whether a sentence is pompous, a paragraph redundant, or an explanation unclear. It doesn't measure speed, and it doesn't look at how the page appears. None of that is automatable, and that's fine: a thing doesn't become less important because a machine can't do it — it just means I have to.
One thing is worth watching, though. My first version was too broad: the pattern for expiring time expressions also flagged sentences that were perfectly fine. Within two weeks I had learned to skip that line — and from then on the check protected nothing. A noisy check is worse than a missing one, because it creates confidence without backing it.
Questions on this topic
Why isn't a linter or an HTML validator enough?
Because they look at syntax, not at claims. A validator has no way of knowing that the privacy notice states this site sends no external requests. The substantial part of checking isn't formal but factual: it asks whether what the page claims about itself is still true.
How do I know which checks I need?
From something having already broken. Every section exists because a real failure slipped through. A check you invent in advance usually watches something that never breaks, and produces noise; a check born from a caught failure guards exactly what can break.
How long does it take to write a script like this?
It isn't written in one go. The first version takes a few hours, and after that each section is added separately, when a failure finds it. That slowness is an advantage: only checks with demonstrated need get in.
What if a check raises a false alarm?
Fix it or remove it, and do it immediately. A check that regularly flags something that is fine will, within a few weeks, teach you to stop reading its output — and from then on you won't notice the real failure either.