DEV Community

Daniel Pertu
Daniel Pertu

Posted on

Eight entries in one content array, 1,041 to 2,034 words each, and the optional field that has exactly one user

Munchable covers eight digestive conditions, and each one has a guide page. All eight are entries in a single array of a single interface, server-rendered at build time, so the index and every guide under it come out of one file.

That makes them comparable, which is the only reason this post exists. Content you can measure against itself tells you things that content in a CMS never will.

The inventory

I counted the source entries and then counted the live pages. Sorted by the live word count:

guide                      src lines  sections  FAQs   words   links in HTML   answers linking in
coeliac disease                   87         4     5   2,034         125                88
bile acid malabsorption           54         3     3   1,543         103                67
low FODMAP and IBS                55         3     3   1,338         100                64
lactose intolerance               47         2     3   1,257          88                52
SIBO                              41         2     2   1,168         100                64
IBD                               49         3     2   1,126          84                48
GERD and reflux                   51         3     2   1,075          80                44
gastroparesis                     44         2     2   1,041          70                34
Enter fullscreen mode Exit fullscreen mode

The links column counts every internal link in the page's HTML, so about 30 of each row is the header and footer that all 516 of our pages share. The last column is the number of ingredient answer pages that link back to that guide.

The top row is 95 per cent longer than the bottom row. It has twice as many source lines, twice as many sections, and more than twice the FAQ entries. It was also the last one written, which is the part that matters, because nothing in the type or the tests asked it to be bigger. It is bigger because it was written on a day when we knew more about writing these than we did for the first one.

The other seven do not form any pattern at all. They are not ordered by how common the condition is, or by how old the page is, or by anything else I can find. They are each as long as the afternoon they were written, which is what a content set with no floor looks like when you finally line it up.

The optional field

Here is the part of the interface that interests me:

export interface ConditionPage {
  slug: string;
  condition: ConditionId;
  name: string;
  h1: string;
  metaTitle: string;
  metaDescription: string;
  lede: string;
  summary: string;
  sections: ConditionSection[];
  checks: string[];
  faqs: { q: string; a: string }[];
  furtherReading?: {
    href: string;
    label: string;
    inline: string;
    blurb: string;
  }[];
}
Enter fullscreen mode Exit fullscreen mode

Eleven required fields and one optional one. The optional one has exactly one user out of eight entries, and it was introduced by the entry that needed it: coeliac disease has head terms that are a vocabulary rather than a question, so it got a reference page of its own, and the guide needed somewhere to point at it from.

That is a perfectly reasonable thing to do, and it is also the exact mechanism by which content types rot. A required field is a question every entry has to answer. An optional field is a note that one entry had something extra to say. Add enough of them and the type describes the union of everything anybody ever wanted to publish, no single entry is ever incomplete, and nothing fails when a page is thin.

The alternative shape would be a separate registry keyed by condition, which makes the asymmetry visible in a file of its own rather than invisible as an absent property. I do not think that is better here, because further reading genuinely belongs next to the guide it extends. But it is worth naming the trade, because "make it optional" is the default move and it has a cost that arrives later, in a column of numbers like the one above.

The thinnest page is also the least linked

Look at the last two columns together. Each of the 461 ingredient answer pages links back to the guide for its condition, so the inbound count is a property of how many ingredient questions that condition has pages for. The spread is 88 down to 34.

Those two columns are nearly the same ranking. The condition with the most to say got the longest guide and 88 internal links pointing at it. The one with the least got the shortest guide and 34. Nobody decided that, and it compounds in the wrong direction: the page most in need of help getting found has the least help, and the page that already reads best is the one the rest of the site keeps recommending.

This is the thing I would not have noticed from inside the editor. Both numbers look fine on their own. It is only when you print them in the same table that you see the site has a quiet preference.

What a floor would actually look like

The temptation is a word count minimum, and it is a trap. A test that fails under 1,200 words produces 1,200 words, and the last 160 of them will be padding that makes the page worse.

A structural floor is defensible: every guide has at least three sections and at least three FAQ entries. Those are not quality, but they are shape, and shape is the thing that drifted. We already enforce an upper bound on the other end, a title and description budget read out of the source files by a test, so the machinery for this exists and is one assertion long.

The real remedy is not a test, though. It is going back to the first seven pages with what we learned writing the eighth. The useful output of an audit like this is not a CI rule. It is a list, in order, of which page to fix first, and that list is just the table above read from the bottom up.

If you want to see the two ends of it, the longest is the coeliac guide and the shortest is the gastroparesis one. Same template, same type, same build, 993 words apart.

Top comments (0)