Build1 distinct publisher3 min readUpdated
A 1,699-record Next.js site ran on flat files for eight months without paging anyone. What failed was not read speed but a derived primary key that quietly hid real records.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The mechanism here has little to do with file systems. A slug assembled from course, semester, subject name and year is a primary key derived from content, and the author's university voids the assumption that made it look unique: one subject is issued under a different code per branch, so KCS078 for computer science, KDS078 for data science and KOT076 as an open elective all reduce to the same string [5]. The three PDFs had different hashes and different byte counts, and one address between them [7].
In a relational store that key gets declared once and the second insert fails while the schema is still fresh in someone's memory. That is the author's own reading of what the flat file cost him: with no constraint to violate, the duplicate row stays perfectly valid and permanently unreachable [9].
The proportions are worth writing down. 147 records were in a slug collision, which is 8.7 percent of the file [2], and the unreachable subset comes to 5.6 percent [3]. Across 1,699 records and 699 distinct subject codes, that is about 2.4 papers per code [6], so the namespace was always tight enough for this to happen.
The second half is the part that travels. The fix builds an index of which record owns each base slug and hands the rest a code-qualified URL [10], but the 147 colliding records were two different populations: 95 with genuinely distinct subject codes, and 52 duplicate uploads and placeholder rows whose IDs end in -dummy, sharing the same code as the record they collided with [12]. For the second group the code suffix disambiguates nothing, and because the matcher still honours a legacy code-first URL for backwards compatibility, the original record answers the new URL, wins the lookup, and 301s the visitor back where they started [13].
Note the detection ratio. The bad deploy presented as 0.06 percent of the swept surface [4]: fifty-two broken records, one visible symptom [7]. Nothing inside the application reports it, because the sections render and the pages load; the only party keeping score is Google, collecting 52 URLs that bounce [14].
There is also a number the post does not reconcile. The sweep checked 1,615 URLs against 1,699 records on disk, leaving 84 unaccounted for [5]. The sweep covers the sitemap, not the dataset, so the audit that found this class of bug is not the audit that proves the remaining records are addressable. Meanwhile the read path, the thing every argument about flat files is about, never featured: the file is under a megabyte, the workload is append-mostly and read-heavy, and a full parse per request has never woken him up [4].
His conclusion, that a full sweep is non-optional after any URL-shaped change and that the belief is not specific to flat files [15], holds. Any application computing slugs in code carries the same failure mode. Postgres only helps to the degree that the derived key is actually declared to it as unique.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
ryzenstudy.com runs on flat JSON files with 1,699 exam paper records, 699 distinct subject codes, five academic programmes and zero database servers.
The site is a Next.js 15 App Router application on one VPS, PM2 in fork mode behind Nginx.
The data layer is fs.readFileSync plus JSON.parse; every query is Array.prototype.filter, every write is writeFileSync, and there is no connection pool.
The data is append-mostly and read-heavy, the whole file is under a megabyte, and the author says fs.readFileSync has never once paged him.
Slugs are generated as aktu-course-semester-subjectName-year; the same subject is issued under different codes per branch, so seventh-semester Deep Learning is KCS078 for computer science, KDS078 for data science and KOT076 as an open elective, producing three separate papers with one slug.
The setup worked for about eight months before the author, looking for something else, found three bugs stacked on top of each other, all consequences of not having a database.
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.
Detailed but wholly self-reported
The mechanism is documented unusually concretely for a personal post — actual generateSlug code before and after, PDF hashes and byte sizes, population counts (147 = 95 + 52), and a sweep result of one 308 in 1,615 URLs. But every figure comes from one author about one site he owns, with no logs, dashboards, third-party crawl data or independent confirmation, and the central comparative claim about databases is an untested counterfactual.
One self-reported hobby-scale site
Adoption evidence is limited to the author's own deployment: a single VPS serving 1,699 records for about eight months, plus one fix deploy and sweep. No other users, teams, downloads, forks, or organizations are reported, and no traffic or usage metrics are disclosed for the site itself.
Slightly understated
The post is scoped and self-critical: it does not claim flat files are generally correct, it names its own missed check, and it quantifies the symptom conservatively. If anything the framing undersells the generalizable finding — that derived keys without uniqueness enforcement can hide 5.6 percent of a corpus indefinitely, and that a 0.06 percent visible symptom rate masked an 8.7 percent data defect — which the author explicitly says is not specific to flat files.
Personal-brand incentive, no vendor stake
The author writes on a developer-audience platform about a site he owns and links, so there is a reputational and traffic incentive to publish a striking 'JSON instead of a database' framing. Offsetting that, the piece discloses his own bug and shipped mistake, and the supplied material shows no vendor sponsorship, product being sold, or funding relationship.
Moderate for mechanism, low for generalization
Confidence is reasonable that the described collision, the 95 unreachable records and the redirect-advertising sitemap occurred as reported, because the account is internally consistent and artifact-specific. Confidence is low that the conclusions generalize or that the database counterfactual holds, given one publisher, one self-interested author, one hobby-scale dataset, and an unreconciled coverage gap in the verification sweep.
build
Your "Index Only Scan" Did 2,847 Heap Fetches: Covering Indexes Are a Vacuum Problem1 distinct publisher
build
A NetworkPolicy in another repo broke invoicing while every dashboard reported success1 distinct publisher
build
The optional EntityManager is the bug: moving the transaction boundary into AsyncLocalStorage1 distinct publisher
build
Three attackers hide behind one connect button, and encryption only stops one of them1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 22, 2026