Skip to content
Hanso Group

Our Own Website Disagreed With Our Company Registration

Julian Lindner 5 minutes read

Our legal notice page published our company registration number and our founding year in the same document, and they contradicted each other. The registration number is 201937629R. A Singapore UEN encodes the year of registration in its first four digits. The structured data on that same page said we were founded in 2020.

Why a machine cares

Search engines and language models resolve companies as entities rather than as pages. They collect facts about an organisation from every source they can reach, and where sources disagree, confidence drops. That is the mechanism behind a knowledge panel being wrong, or a model answering a question about your company with a shrug.

Our site was one of those disagreeing sources, and it was disagreeing with itself, on one page, in two elements a few hundred bytes apart. One of them was the government-issued number.

Fixing it was one line, because the value already lived in a single module that every template and every API response reads from. That module exists because the same values had drifted before: at one point the registered office unit number differed between the footer and the legal pages.

The same-entity links were worse

Structured data has a field called sameAs. It lists the other places on the internet that are the same entity: your LinkedIn page, your Mastodon account, your profile on a partner’s site. It is how a machine joins your identity across sources.

Ours had two entries. The obvious candidates for the rest were a list somebody had written down: our X handle, our GitHub organisation, our about.me profile, a partner directory listing.

I fetched each one before adding it. Two of them did not exist.

x.com/thehansogroup returns 404. That handle was in a meta tag on all 34 pages, so every link anybody shared attributed the post to an account that is not there. That tag is now gone rather than pointed somewhere else.

The other was a partner listing. I checked the partner’s /partners/ index page, found no mention of us, and wrote down that no such profile existed.

It exists. It is at /partner/hanso-group, singular, and it is a full profile page with our description on it. The check was correct and the conclusion was one URL wide.

The bit I got wrong twice more

I had by then written a note to myself: a guard built on a negative measurement inherits the reach of that measurement. I had also written a test to enforce the rejection, which meant that when the correct URL turned up, the test went red and the test was the thing defending the error.

Then I did the same thing twice more in the same week.

Once on an API, where a field came back empty from the list endpoint and I concluded the system did not record it at all. It records it. The list endpoint is a summary and omits it; the detail endpoint has it.

Once on our own code. I checked our GitHub organisation, found one repository, and concluded we publish essentially nothing. The code is under a different account: seven repositories, MIT licensed, all pushed within the week.

Three times, the same shape. Each time the honest sentence was available and one word longer. Not “no partner profile exists” but “the partners index page does not mention us”. Not “the API does not record who merged this” but “the list endpoint does not return that field”. Not “we publish nothing” but “that organisation has one repository”.

Naming the thing you searched, in the claim, costs one clause and invites the next question. Stating the conclusion instead produces something that has to be argued with, and that somebody may have written a test to protect.

What changed on the site

The founding year now matches the registration number, and a test asserts that relationship rather than the literal, so it fails if either moves.

sameAs gained the entries that resolve and did not gain the ones that do not, with the rejections recorded in the code beside the values, including the URL each was checked at.

The Mastodon link carries rel="me", which lets that profile verify the link back and turns a claim into something checkable from outside.

Each page now describes itself rather than repeating the organisation: services carry Service, products carry SoftwareApplication, the team page carries a Person per member, and nested pages carry breadcrumbs. Before, all 34 pages emitted the same organisation block and nothing else, so a machine reading our product page learned our postal address.

The field we left empty

The structured data had areaServed: "SG" while we work with clients in Europe. The temptation is to correct it immediately. We removed it instead and left it out until the question of which regions we serve had an answer from the person who gets to decide.

An omitted field asserts nothing. A wrong one asserts something false to every machine that reads it, and unlike a wrong sentence on a page, nobody sees it to correct it.

Share this article

Related Articles

Back to all articles