Build1 distinct publisher3 min readPublished
Twenty lines of substring scoring replaced a hosted index on a 220-post site. Whether it transfers to yours depends less on corpus size than on how often your server instances go cold.
The Engineer · Build desk

build
Headless CMS is a hiring plan with a content schema attached1 distinct publisher
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
build
With CRA out of React's docs, the new project default is a rendering decision1 distinct publisher
build
Tailwind v4 moves your tokens into CSS. The token name is now the API.1 distinct publisher
Compiled by The EngineerSomething wrong?How this is made
A query does no I/O. `runSearch` takes the store that is already resident, picks the index built once per store-and-locale pair, lowercases the query and splits it on whitespace [9]. `scorePost` then returns zero the instant a term is missing from the post's haystack, which is title, description, body and tags concatenated and lowercased once per post [10][11]. Every term is required, so a second word narrows the result set instead of widening it [11].
Scoring is boolean per field. `includes` answers yes or no and each field contributes a fixed weight, so a post that mentions your term once ranks identically to one that mentions it fifty times [4]. The ceiling for a single term is 17, the sum of the four weights [2]. Work an example: two query words present in both the title and the body score (8+2) x 2 = 20, while a post carrying both words only in the body scores 4 [3]. That five-to-one gap is the entire relevance model, and the author is upfront that the weights are not tuned, only ordered so a title hit outranks a body hit [12].
The WeakMap key is the part worth copying. Because the memo is keyed on the store object itself, the index cannot outlive the data it describes, and nobody has to remember a `resetSearchIndex()` call or pick a TTL [14]. Note the scope. That invariant holds inside one instance; coherence across instances still rests on the content webhook reaching each of them and on the adapter refetching [7][14]. The same pass feeds `computeFacets()`, which counts tags over the identical array, so results and facet counts cannot disagree about what exists [13].
What replaced the hosted index is a cold-start fetch: about 8 MB on the first request to each new instance, after which searches and tag-cloud renders are effectively free [7][15]. That payload is four times the 2 MB ceiling Next.js puts on its data cache, so the framework declines it and logs the fact [8][1]. The author's answer is that a second cache layer was never wanted, since the in-memory store is the cache [8], and this is the rare size limit that saves you from configuring something you did not need. What actually drives the cost is scheduling: the fetch recurs with instance churn, not with publish frequency [6]. A site that ships a few times a week and keeps instances warm amortises it to nothing [15]. A platform that recycles containers every few minutes pays it repeatedly, and pays in tail latency rather than on an invoice [6].
The arithmetic only carries over to another site if a few conditions hold. The adapter must already be loading the whole corpus, or you are adding a fetch rather than deleting a service [6]. Size has room: at roughly 37 KB per post, 2,000 posts is about 73 MB, so the cold fetch breaks before RAM does [5]. And queries have to land as exact substrings of stored text, because the matcher only finds exact substrings, with none of the stemming, synonym handling, typo tolerance or relevance tuning a hosted engine would add at scale [16]. On a corpus where people type framework names, that holds [17]. On a catalogue where they misspell brand names it does not, and the author says a hosted engine earns every cent at millions of documents or with fuzzy ranking [16].
Ranked by verification strength, evidence, and original report placement.
The author describes the most satisfying commit of the migration off Contentful as one that only deleted things: the Algolia client, the sync route that pushed every published post to a hosted index, and four environment variables that no longer had to be kept secret.
On-site search for cloudapp.dev now lives in a single file with no service behind it.
The author says Algolia is good software and would reach for it again on a large catalog, but that their catalog is about 220 blog posts.
Under the previous setup every publish had to fan out to an external index, which meant a sync job, a webhook, and recurring uncertainty about whether the index was in step with what was live.
The author says they were maintaining and paying for a second copy of their content in order to search a dataset that fits comfortably in RAM.
The cutover was from Contentful to a self-hosted headless CMS whose adapter already holds everything in memory, and that detail decided the search question.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 28, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code you can check, numbers you cannot
The scoring is on the page — four weight constants, the early return that makes every term mandatory, the whole map-filter-sort pipeline — so how this search ranks is verifiable by reading, and our arithmetic about the five-to-one title-versus-body gap rests on nothing else. The operational half is a different grade of evidence entirely: 8 MB, 220 posts, a warm search that is 'essentially free', the vendor bill that went away. Those are one engineer's figures about one site, with no timing, no instance counts and no outside look.
One site, one author
The pattern is live in exactly one place, described by the person who shipped it. No second team, no library, no fork, no downstream mention. The only element anyone else can reproduce independently is the framework's refusal to cache a payload this large — and to be fair to the author, he never claims to be starting a movement.
Honest about features, quiet about churn
This write-up argues against its own conclusion more than most: it names the missing typo tolerance, synonyms and stemming, and grants that Algolia earns its price at a million documents. The tilt is elsewhere. Calling the 8 MB fetch 'the honest cost' makes it sound paid once, when the store is memoized per instance — on a platform that recycles instances briskly, some unlucky reader pays it again and again, and the frequency is never examined. 'The index can never lie' is likewise a guarantee about one instance, stated as an absolute.
The migration's own author grading the migration
Self-published under the site's handle, and self-published migration stories rarely end in regret; the diff being 'almost all red' is a satisfying ending the author chose. Cutting the other way: the argument removes a paid vendor rather than selling one, it compliments Algolia on Algolia's own ground, and it prints the code that would expose the scoring if the reasoning were shaky. There is no sponsor and nothing on sale here.
Certain about the ranking, unsure about the trade
Two kinds of certainty are mixed here and they should not be averaged casually. How results are ordered is as solid as reporting gets, because the function is printed. Whether the swap is wise for anyone else rests on figures only the author holds and on an instance-churn rate nobody has measured — which is exactly the variable that decides the answer.