DEV Community

Daniel Pertu
Daniel Pertu

Posted on

Our content plan said 63 pages, its own tables said 65, and the live sitemap agrees with the tables

Last month our marketing site had eight indexable URLs. Three of them were auth screens that were only in the sitemap because they happened to be reachable, so in practice the site had one page that could answer a search: the homepage. The price, the feature list and the explanation of how the thing works were #pricing, #features and #how-it-works, anchors into that one page.

So I wrote a plan. It is a 330-line markdown file in docs/, and the first thing it says is:

Status: proposed, nothing below is built yet.

All of it is now built. The status line is still there, which is the most honest thing in the document. This post is the audit I should have done when I deleted that line, except I never deleted it.

The plan could not add up its own pages

The plan proposes six topic clusters, each a hub page with spokes underneath. Its tree diagram looks like this:

/features/…          (1 + 9)    what the product does
/solutions/…         (1 + 10)   who it is for
/guides/…            (1 + 13)   how to run a quiz night
/quiz-questions/…    (1 + 14)   free questions, the link magnet
/compare/…           (1 + 7)    alternatives and comparisons
/tools/…             (1 + 4)    free tools
Enter fullscreen mode Exit fullscreen mode

Under it, in bold: 63 pages in total.

Add the diagram up and you get 63, so that is consistent. The problem is further down, where each cluster gets a section with a table of the actual URLs and the query each one targets. Count those tables and /solutions has eleven rows, not ten, and /guides has fourteen, not thirteen. Two cluster headings had counted their spokes and forgotten their own hub page.

Then at the very end there is a phase table, five shippable phases with a page count each: 9, 20, 13, 18, 7. That sums to 67.

One document, three totals. 63 in the diagram, 65 in the tables, 67 in the delivery plan. Nobody noticed because nobody adds up a plan twice.

The deployed site settles it. Pull the sitemap and count:

curl -s https://pub-trivia.app/sitemap.xml | grep -c "<loc>"
# 72
Enter fullscreen mode Exit fullscreen mode

Seventy-two, which is the 65 cluster pages the tables listed, plus the homepage, /pricing, /faq, /about and three legal documents. The tables were right and the headline number was wrong. If you want to see the clusters as a reader rather than as a count, the guides hub is the largest one, fifteen pages including its own index.

Seven technical commitments, checked against the response

The plan ends with a list of technical work. A month later, every item on it is either observable in a response header or in the HTML, which is the only reason this audit took an afternoon instead of a week.

Static rendering, with one exception. The plan said every content page is prerendered and /pricing is the exception, because a visitor in Germany has to see euros.

curl -sI https://pub-trivia.app/features | grep -i x-nextjs-prerender
# x-nextjs-prerender: 1
curl -sI https://pub-trivia.app/pricing | grep -i cache-control
# cache-control: private, no-cache, no-store, max-age=0, must-revalidate
Enter fullscreen mode Exit fullscreen mode

Held. One page opted out of the CDN on purpose and says so in its own headers.

lastModified from a content registry, not the build clock. The old sitemap stamped every URL with the time the build ran, which tells a crawler that the terms of service changed this morning.

curl -s https://pub-trivia.app/sitemap.xml | grep -o '<lastmod>[^<]*' | sort | uniq -c
#   68 <lastmod>2026-09-14T00:00:00.000Z
#    3 <lastmod>2026-10-12T13:21:05.911Z
#    1 <lastmod>2026-10-03T00:00:00.000Z
Enter fullscreen mode Exit fullscreen mode

Sixty-nine dates come from the registry node for that page. Three come from the build clock, because the three legal documents have no registry node to read a date from.

Structured data that describes what is visibly on the page. The plan asked for HowTo on the how-to guides, FAQPage on the FAQ, SoftwareApplication and Offer on /features and /pricing, breadcrumbs throughout.

curl -s https://pub-trivia.app/guides/how-to-host-a-pub-quiz | grep -o '"@type":"HowToStep"' | wc -l
# 8
Enter fullscreen mode Exit fullscreen mode

Eight steps, and the page has eight visible steps. The FAQ carries sixteen Question nodes against sixteen questions you can read. The features hub carries one SoftwareApplication and two Offer nodes. All checkable with view-source, which is the point of writing the rule down as "only mark up what is on the page".

The auth screens leave the sitemap and stay crawlable. The plan was specific: /login, /signup and /forgot-password are reachable pages and should stay crawlable, but a sitemap is a statement about what you want ranked, and those three are not. They are gone from the sitemap. They also still answer with <meta name="robots" content="index, follow">, which is not wrong, just unfinished thinking. A page you have deliberately removed from your own list of what matters is a page that should probably say so itself.

One URL changed its name, for a good reason

The feature cluster shipped exactly as planned except for one row. The plan had:

URL Primary query What is on the page
/features/results-and-export quiz results export Session history, CSV export, settling a disputed answer after the fact

There is no CSV export. There was never going to be one in that timeframe, and writing the page would have meant either describing a feature that does not exist or writing a page whose URL promises a thing its body does not mention. So the URL became /features/session-history, and the body now argues the opposite case, that there is nothing to export in the first place:

Nothing has to be exported or filed at the end of the night.

That is the only diff between the planned URL list and the live one. A plan that survives a month with one renamed URL is not evidence that the plan was good, it is evidence that the writing was the hard part and the plan was mostly a list of titles.

A second rename happened inside the code rather than in the URL space: the plan names an ArticleLayout component, and what shipped is ContentPage, which 67 pages render through. Same component, better name, nobody updated the document.

One promise deliberately broken

The plan asked for "Article with a named author" on the guides. The deployed pages carry Article, and the author is this:

"author": { "@id": "https://pub-trivia.app/#organization" }
Enter fullscreen mode Exit fullscreen mode

A reference to the organization node, not a person. Naming an individual author in structured data is a claim that a specific human wrote and stands behind the page, and on a small product site the honest version of that claim is the company. So the field is populated, the type is right, and the plan's instruction was ignored on purpose.

What the plan refused to do, which is the part that aged best

Three sections of the document are about pages that do not exist:

  • No city pages. "Pub quiz software London" and forty more are the fastest way to inflate a page count and the textbook definition of a doorway page. The product is used from behind a bar anywhere, so there is nothing true to say that differs by city.
  • No blog. A dated post stream commits you to a schedule and ages badly. The guides are undated evergreen pages that get revised in place.
  • No thin variants. "Quiz app for pubs" and "pub quiz app" are the same page, and shipping both splits one ranking across two URLs that compete with each other.

You can check all three in one command:

curl -s https://pub-trivia.app/sitemap.xml | grep -ciE "/blog|london|manchester"
# 0
Enter fullscreen mode Exit fullscreen mode

A year from now the thing I will be glad about is not the 65 pages. It is that the document which could have justified 400 of them says, in writing, why it did not.

Top comments (0)