Marketing & Growth

Two Axes, 255 Tags: Designing a Taxonomy That Survives Real Data

Categories describe what a tool is, which is the least useful fact about it. This is the reasoning behind tagging every tool on two independent axes — the job it does and who it was built for — using about 255 predefined values rather than free-form tags, including how to pick granularity, why the vocabulary lives in typed constants, and the three things we'd do differently.

TechLogHub Editorial
July 25, 2026
11 min read
0 views

Share Article

Two Axes, 255 Tags: Designing a Taxonomy That Survives Real Data

Two Axes, 255 Tags: Designing a Taxonomy That Survives Real Data

TL;DR: We tag every tool in our directory on two independent axes — what job it does, and who it was built for — using a fixed vocabulary of about 255 predefined values rather than free-form tags. This is the reasoning behind that, including the parts we got wrong first: why free tags quietly destroy a directory, how to pick granularity without guessing, and why the tag list lives in typed constants rather than in the database.

The problem categories don't solve

Every directory starts with categories. Ours has nine — AI and machine learning, cloud, developer tools, security, data analytics, and so on. They're necessary and they are nowhere near enough.

The failure is arithmetic. Put a few thousand tools into nine buckets and the average bucket holds several hundred entries. "Developer Tools" as a category tells a visitor almost nothing, because it contains an API client, a CI runner, a code formatter, and a database GUI — things that share no user, no job, and no decision. Nobody has ever needed to browse that set.

The instinct is to add subcategories. That helps briefly and then hits a wall, because subcategories are still a tree, and tools don't live in trees. A managed Postgres service is a database, a hosting product, and a backend-as-a-service depending on who's asking. Force it into one branch and you've made it invisible to two-thirds of the people looking for it.

So the real question isn't how to subdivide categories. It's what dimensions actually describe a tool.

Two axes, deliberately independent

We settled on two, and keeping them separate is the single decision that made everything downstream work.

Use case — what job does this do? Not what the product is, but what someone hires it for. "Sends transactional email." "Runs scheduled jobs." "Monitors uptime." Roughly 185 of these.

Audience — who was this built for? Solo builder. Small startup team. Platform team at scale. Data engineer. Mobile developer. Roughly 70.

The reason these are separate axes rather than one merged tag list is that they vary independently, and the interesting information is in the combination. Two tools can share a use case entirely and be wrong for each other's users — one auth provider designed for a solo developer shipping a side project, another designed for a company that will face SAML procurement requirements. Same job. Completely different tool. A single flat tag list cannot express that; two axes express it for free.

It also means a filter query is a genuine intersection: tools that do this job, for someone like me. That's the query people actually have, and neither axis answers it alone.

Why free-form tags lose, even though they feel obviously better

The instinct is to let tags be open text. It's flexible, it's less upfront work, and it seems like the vocabulary will emerge organically from real usage. We tried a version of this. Here's what actually happens.

Synonyms fragment everything. Within a week you have ci, ci-cd, CI/CD, continuous-integration, and pipelines, each holding a handful of tools, none holding the set anyone wanted. Every one of those is somebody's reasonable choice. The fragmentation isn't a discipline failure, it's what unconstrained vocabulary does.

You can't build a page on an unstable value. This is the one that mattered most to us. A predefined tag is a URL: /use-case/transactional-email is a page that can rank, accumulate links, and be linked to from other pages with a stable anchor. A free-form tag is not a page — or rather, it's a page that might vanish when the last tool using it gets retagged, which means a 404 for anything that pointed at it. You cannot build an internal linking structure on values users invent.

The long tail is dead weight. Free tagging produces a distribution where most tags have exactly one tool. Those tags are pure cost: they bloat the filter UI, they generate thin pages, and they never help anyone find anything.

Nothing is enforceable. With open tags, "is this tagged correctly" has no answer, because there's no correct. With a fixed vocabulary the question becomes reviewable, which means quality is something you can actually maintain rather than aspire to.

The tradeoff is real and worth stating plainly: a controlled vocabulary is more work upfront, it's rigid when you meet something genuinely new, and adding a term requires a deliberate decision instead of just typing. We took that trade because a directory's entire value is that its structure is trustworthy. A directory with unreliable tags is a search box with extra steps.

Granularity is the hard part

Deciding to use a fixed vocabulary is easy. Deciding how fine-grained it should be is where the actual difficulty lives, and both failure modes are bad.

Too coarse and the tag doesn't discriminate. "Database" as a use case is useless — it's the category again, wearing a tag's clothes. If a value matches hundreds of tools, filtering by it hasn't narrowed anything.

Too fine and nothing clusters. If a tag applies to one tool, it's a description, not a taxonomy term. A hundred tags with one tool each carry exactly as much organizing power as no tags at all, plus a lot of interface clutter.

The heuristic that worked for us is a population floor and ceiling. A use-case value should plausibly apply to at least three tools and not many more than about thirty. Below three it's a description; above thirty it's a category. When we drafted a term that failed either bound, that was the signal to split it or merge it — not a judgment call about whether it "felt right."

Two rules of thumb followed from that:

  • Split when the audiences diverge. If a single use case is serving two clearly different kinds of user, it's usually two use cases wearing one name. That's the two-axis design telling you something.
  • Merge when nobody would search the difference. Distinctions that are real but that no one would ever type into a search box are engineering pride, not taxonomy.

Name tags in the user's language, not yours

This is where the SEO consideration stops being a separate concern and becomes a design constraint, because it turns out the right answer for search and the right answer for usability are the same answer.

Every tag we publish is a phrase someone might plausibly type. Not the internally precise term — the searched one. If people say "cron jobs" and the technically correct label is "scheduled task execution," the tag says cron jobs. The internal precision buys nothing; matching the user's language buys both comprehension and search visibility.

Three constraints we hold to:

  • The tag reads as a noun phrase a person would say out loud. If it needs explaining, it's wrong.
  • No abbreviations unless the abbreviation is the common form. "API" yes; invented internal shorthand no.
  • The slug is stable and the display label is not. Labels can be rewritten freely. Slugs are URLs, and changing one costs a redirect and whatever authority the old page had.

That last one is worth internalizing early, because it's easy to treat the tag string as a single value and then discover you've coupled your display copy to your URL structure. Separating them from the start costs almost nothing; separating them later is a migration.

Why the vocabulary lives in typed constants

An implementation decision that turned out to matter more than expected: the tag lists are TypeScript constant arrays in the codebase, not rows in a database table.

The obvious argument against is flexibility — a database table means non-engineers can add terms without a deploy. That's a real benefit and we gave it up on purpose.

What you get in exchange is that the vocabulary is type-checked. A mistyped tag anywhere in the codebase — in a seed script, an import, a component prop — is a compile error rather than a silently orphaned record that nobody notices for three months. Every place that consumes a tag gets autocomplete over the legal values. And critically, adding a term becomes a code review: it shows up in a diff, someone looks at it, and the "is this actually distinct from the four similar ones" conversation happens before the term exists rather than after it has fifty tools attached.

The friction is the feature. Taxonomies don't degrade through bad decisions, they degrade through easy ones.

The data itself lives in MongoDB, where tags are stored as arrays of these known values. Documents reference the vocabulary; they don't define it.

What we'd do differently

Three things, honestly.

We designed too much of the vocabulary before tagging anything. Sitting down to enumerate 185 use cases in the abstract produces terms that feel comprehensive and turn out never to apply to a real tool. The better sequence is to tag thirty tools with whatever vocabulary exists, notice where you keep reaching for something that isn't there, and grow from the gaps. Design in the abstract and you'll build for imagined products.

We under-invested in the audience axis at first. Use cases are more fun to enumerate and feel more substantial, so they got the attention. But in practice the audience tag is what makes a recommendation useful — knowing a tool sends transactional email narrows the field to twenty; knowing it's built for a solo developer rather than a platform team narrows it to three. The less glamorous axis was doing more work.

We didn't plan for merges. Splitting a tag is easy — new term, retag some tools. Merging two is where it hurts, because both may already be published URLs with links pointing at them, and now you owe a redirect and a decision about which slug survives. Assume from day one that a third of your vocabulary will eventually merge into something else, and keep the slug-to-page mapping in a layer you can rewrite.

The shorter version

Categories describe what a thing is; that's the least useful fact about it. Tag on the two dimensions that actually predict fit — the job and the intended user — keep those axes independent, fix the vocabulary rather than leaving it open, name every term the way a person would search for it, and put the list somewhere a typo fails loudly.

None of that is specific to developer tools. It's what any directory needs once it's large enough that browsing stops working, which happens sooner than most people expect.

If you want to see the result, it's the structure behind TechLogHub — every tool tagged on both axes, and the comparison pages built from the intersections rather than from feature checkboxes. The reasoning for why feature matrices don't work is a separate piece: how to compare developer tools.

FAQ

What is a content taxonomy?

A content taxonomy is the structured vocabulary a site uses to classify its content — categories, tags, and any other controlled attributes — along with the rules for applying them. For a directory or catalogue it's the difference between content people can navigate and content they can only search. The taxonomy defines what filters exist, what pages can be built, and what questions the site is able to answer.

Should I use free-form tags or a controlled vocabulary?

Use a controlled vocabulary for anything that drives navigation, filtering, or URLs. Free-form tags fragment into synonyms almost immediately, produce a long tail of terms applying to one item each, and can't support stable pages because the values change under you. Free tags are reasonable for internal notes or personal collections where nobody else depends on consistency.

How many tags should a taxonomy have?

Fewer than feels right, and the number matters less than the population per tag. A useful test is that each term should plausibly apply to at least three items and not many more than about thirty. Below the floor it's a description rather than a category; above the ceiling it isn't narrowing anything. Let those bounds tell you when to split or merge rather than guessing at a total.

How do tags affect SEO?

A controlled tag becomes a stable URL that can rank, accumulate links, and serve as a consistent internal anchor — which is why the vocabulary needs to be fixed before you publish pages from it. Two rules matter most: name terms the way people search rather than using internal jargon, and never let a tag page exist with only one or two thin items behind it. Free-form tags undermine both, because the values shift and the long tail generates empty pages.

What is the difference between a category and a tag?

A category is usually a single hierarchical placement — one item, one branch — while tags are multiple and non-exclusive. In practice the more useful distinction is that categories describe what something is and tags can describe anything you choose, including what job it does and who it's for. Categories break down when items legitimately belong in several branches, which is where a tag-based axis handles the same information without forcing a false choice.

Should taxonomy values be stored in the database or in code?

Storing the vocabulary in code as typed constants gives you compile-time validation, autocomplete wherever tags are consumed, and a code review on every addition. The cost is that adding a term requires a deploy and non-engineers can't do it. That friction is usually worth it for a directory, because most taxonomy damage comes from terms being added too easily rather than too slowly. Store the vocabulary in code and the assignments in the database.

How do you handle renaming or merging tags later?

Separate the display label from the slug at the start, so labels can change freely without touching URLs. For merges, pick a surviving slug and permanently redirect the retired one, then retag the affected items. Assume a meaningful share of your vocabulary will eventually merge and keep the slug-to-page mapping in a layer you can edit, rather than deriving URLs directly from labels.

How do you decide what dimensions to tag on?

Start from the questions people actually arrive with, not from the properties the items happen to have. For tools, the real questions are what job it does and who it suits, so those became the two axes. Attributes like pricing model or language support are worth storing as fields, but they're rarely how someone begins a search — and a dimension nobody starts from is a filter nobody uses.


The structure described here is live at TechLogHub — developer tools tagged by job and by intended audience, with comparisons built from the overlap.

Stay Updated

Get the next deep dive in your inbox

Subscribe for product analysis, engineering explainers, and practical guides published on TechLogHub.

See what launched this week

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.