Notes
TechPrufer NotesTaxonomyTechPrufer

Folksonomy explosion: 24,917 labels, 12 desks

Extract minting was free, so 73% of 24,917 labels are hapax. Map the 374 frequent labels that already cover 72% of 13,724 posts. Do not re-read the bodies.

Engineering•Sep 13, 2026•11 min read
Many extract labels collapsing into a small set of desks

Extract minted a catalog row for every novel phrase. That produced 24,917 labels on 13,724 tagged posts, 18,152 of them used once (73% hapax), and eight umbrellas that parent 51 of 24,917 rows. The public vocabulary exploded.

Create was free, so the catalog filled with synonyms and hashed children like observability-cms25vur. Folksonomy (Thomas Vander Wal, 2004) is useful when minting is expensive. The explosion starts when it is not. The named failure is folksonomy explosion. The fallacy is treating that pile as a taxonomy, shipping it as feeds, then sending every body back through the model. The second pass mints more phrases. The posts were never missing.

Why a hapax cannot be a product

A tag that exists once cannot be a feed. /feed?topic=rag-systems misses rag. Three clones of “Observability” are three different URLs. That is why the 51-parent count matters: the umbrellas we actually wrote never became the public layer, because auto-create turned one umbrella into thousands of unique slugs. Broken recall is the cost. So is a 25k-node graph nobody can browse, and junk-drawer desks named Backend / Web / Product that would swallow half the corpus.

Microsoft Academic Graph ran the same experiment at library scale: ~65,000 Wikipedia-like concepts, six levels, parents firing whenever a child matched. High recall, low precision. They froze it. That is the warning, not a system we shipped. Our 25k extract tags are MAG in miniature.

What the catalog already says

Cheap keyword buckets on existing tags (no LLM, no re-extract) already desk most of the corpus. A post can land in more than one bucket, so the rows do not sum to 13,724. This table is evidence the mass already clusters. It is not the closed vocabulary we will ship.

Domain signalPosts (of 13,724)
AI / ML3,918
Infra / SRE / DevOps2,575
Security2,276
Networking / edge1,522
Data platform852
Storage / databases810
Developer tools / DX443
Mobile381
Frontend329
Compilers / runtimes265
Unmatched by this regex3,740

The unmatched bucket holds backend / distributed systems phrases: Performance, Event-driven, Microservices, Scalability, Durable execution, Consistency. Plus some DX (Visual Studio, Azure Boards) that a slightly better alias list catches. That is why we split Distributed systems instead of dumping 3,740 posts into one drawer.

374 tags that appear at least 20 times already cover 9,941 / 13,724 posts (72%). One remap of a tag updates every post that uses it. That is why the work is 374 short labels, not 13k article bodies. The other ~24,500 tags are noise and aliases.

How the systems that survived actually stopped it

The closest analog is MAG to OpenAlex. We did not ship either. They closed the public vocabulary, kept a mapping layer, and stopped treating the long tail as navigation. They did not re-tag every document.

OpenAlex, 2024: replace MAG's frozen concept tree with a fixed 4-level aboutness hierarchy (4 domains, 26 fields, 252 subfields, ~4,500 topics). New papers do not invent a concept. They get classified into the closed set.

Stack Overflow adds a different mechanism: raise the cost of creating a tag, auto-cull hapax, synonym map so js / java-script become javascript on write. Merge remaps existing rows once. Synonym is what stops the bleed. Observe what people typed. Do not predict every variant in advance.

arXiv: pick from a closed ~150 categories. ACM CCS: pick 1-3 nodes from a fixed tree. Netflix: independent facets, then rules over those facets. A RAG-on-Kafka post is AI and data. Forcing a single parent is how you get the junk drawer.

Three layers. Only the top two are public.

Proposed shape, not a claim that the feeds already look like this. Think like OpenAlex plus Netflix facets, not like one tag graph as navigation.

L0 folksonomy · 25k phrases · internal
↓
L1 topic · ~150-400 canonical
L2 domain · 12 closed desks
L3 role feed · lookup, not extract

Layer 0 never becomes a chip. Hashed children were how auto-create turned one umbrella into thousands of unique slugs. Public chips are canonical topics. Desks are a closed enum. Roles are a view over desks.

The 12 desks

A post may have 1-2 domains, not 10. Bound it so it cannot explode. We drop “Web” as its own desk (HTTP/CDN is Networking; React is Frontend) and “Product” as a PM bucket (the engineering-post gate already excludes culture/changelog; A/B belongs under Experimentation). Backend is too vague. Split it or half the corpus lands in one drawer. That set is from this catalog's mass plus the eight seed umbrellas, not an industry standard we imported.

DomainIn
AI / MLmodels, agents, RAG, inference, evals
Datapipelines, warehouses, streaming, analytics eng
StoragePostgres, sharding, caches, object storage
Distributed systemsmicroservices, events, consistency, durable execution
Infra / SREk8s, CI/CD, observability, incidents
Securityauth, zero trust, vuln, privacy
NetworkingDNS, CDN, edge, QUIC, load balancing
Frontend / webUI, design systems, browsers
MobileiOS / Android / RN
Runtimescompilers, JVM, WASM
Developer toolsDX, IDEs, codegen
ExperimentationA/B, growth measurement

Those desks belong in company_blog_domains, not as kind: 'domain' on the interview catalog. Observability is already an interview umbrella. Sharing one table is how the two vocabularies collided.

Role feeds are a lookup

We do not tag “AI Engineer” on the post. We tag domain ai-ml. /feed?role=ai-engineer is posts in that domain. Roles are views. Domains are facts. That is the facet rule, applied here: do not extract a job title the catalog never wrote.

Role feedDomains that fill it
AI engineerAI / ML
MLEAI / ML ∩ inference, training, evals, MLOps
Data engineerData (+ storage when it is warehouses / pipelines)
SREInfra / SRE
FrontendFrontend / web
SecuritySecurity

No FDE feed yet. Zero extract tags look like “forward deployed.” We will not invent that desk until there is a written inclusion rule (Foundry / ontology / customer-deploy), because the data does not support it.

Work on tags, then inherit

Same job as Stack Overflow raising tag-create reputation, applied to this catalog. Extract can no longer mint catalog rows unattended. Map the label once. Every post that already carries it inherits the desk.

  1. Stop the bleed. If resolve hits an umbrella, attach one child under it (observability-general). Never mint observability-{cuid} again. Novel phrases go to company_blog_topic_candidates (pending / approved / merged). Hide hapax from public chips.
  2. Merge exact clones. 317 “Observability” IDs → 1 leaf. Same for Reliability, Rate limiting, Realtime, Messaging. Remap company_blog_post_topics, delete orphans. No LLM.
  3. Map the 374 common tags → canonical topic + domain. Observe what extract actually wrote. Alias to rag, vector-search, sre. One LLM pass over 374 short labels is allowed. 13k articles are not. Covers 72% of posts immediately.
  4. Graph only as a mapper. Tags that share a postId are a co-occurrence graph. If a rare tag sits on posts that are 80% ai-ml, inherit ai-ml. That is OpenAlex's citation-cluster idea, with “appeared on the same post” as the edge. Analog for the mapping step. Do not ship a 25k-node tag graph as navigation.
  5. Vectors on labels, optional, leftover only. interview_topics.embedding exists. proposeUmbrellaParents exists. 0 rows have embeddings. The cheap vector use is embedding tag strings (a few tokens each) and nearest-neighboring them to the 12 domain prototypes, after measuring unmatched posts from steps 1-3. Full-post embeddings wait until coverage is still bad.

One post, no re-extract

A post already has extract tags: RAG Systems, Vector Database Integration, LLM inference optimization. The body does not need another model pass for the desk to exist.

TodayAfter mapping
3 orphan catalog rows, 3 different feedsaliases → rag, vector-search, llm-inference
/feed?topic=rag-systems misses rag/feed?topic=rag hits all three aliases
no domaindomain ai-ml
no rolerole feed ai-engineer via the domain rule

The post body is never sent to an LLM again.

What we will not do

  • A knowledge graph of 25k tags as the information architecture. Graphs are good for mapping. They explode as browsing. OpenAlex abandoned that shape.
  • Re-extract 13k posts to add audienceRoles. Roles are derived.
  • One giant Backend, Web, or Product domain. They become junk drawers. See unmatched: Performance, Microservices, Scalability.
  • FDE as a first-class domain until there is a written inclusion rule. The extract does not support it yet.

Takeaway

Folksonomy explosion is the named failure. The repair that has worked in the wild is a closed public vocabulary plus a synonym table. Ours is ~12 desks, ~300 canonical topics, and the 24k phrases as aliases. Measure unmatched posts after the clone-merge and the 374 aliases. Then spend on label embeddings.

References & Further Reading