Search

Find an article

← Back to articles
CMS Migration 9 min read

Taxonomy Migration Between CMS Platforms: Why It Never Maps

Learn why taxonomy migration between CMS platforms breaks categories, tags, hierarchy, and post assignments—and how to map and verify them correctly.

Classification is the part of a CMS migration that arrives looking complete and is not. Platforms model it differently enough that a clean one-to-one mapping usually does not exist: one system may offer two parallel schemes where the other offers one, and nesting that was structural on the old platform may be something the new one expects you to build deliberately. The result is that every term can appear in the destination while the relationships between them, and the assignments joining them to your posts, quietly do not.

Before moving any of it, the backup and rollback plan needs to be verified, and for this specific job it needs one thing added: export a plain list of your terms and their assignments before you start, independent of the migration file. A term list is small, it is trivially readable, and it is the only thing that lets you tell afterward whether a post lost its classification or never had one. Reconstructing that from a backup after the fact is possible but slow, and the list costs nothing to produce now. The rollback remains what it always is in this cluster: the source platform stays running and untouched until the destination has been checked.

Why classification never maps one to one

Every CMS makes a set of decisions about how content gets sorted, and those decisions are not the same decisions.

Some platforms ship two parallel systems. WordPress is the familiar example: categories, which nest inside each other, and tags, which are flat. These are genuinely different structures used for genuinely different purposes, and a site that has been running for years usually has years of editorial convention built on the distinction between them.

Many platforms ship one. A single flat set of terms that content can reference, with any grouping or nesting something you model yourself if you want it. Most headless platforms take this approach, because they are built to let you define your own structures rather than provide a fixed pair.

Moving between the two shapes forces a decision, and there is no neutral answer.

Two systems into one collapses a distinction you were relying on. If your categories and your tags both become plain terms in a single list, the difference between “this post belongs in this section” and “this post mentions this subject” disappears. Everything is still there. What it meant is not. The archive is now a flat pile of terms with no indication of which ones were structural and which ones were descriptive.

One system into two forces you to invent a distinction that never existed. Coming the other way, something has to decide which of your terms become the nesting kind and which become the flat kind. If nobody decides, the migration decides, usually by putting everything in one bucket, and you inherit a structure nobody designed.

Neither of these is a bug in either platform. It is what happens when two reasonable but different models meet, and it is the reason this is a decision to make before the move rather than a problem to discover after it.

Hierarchy is a reference, not a property

This is the mechanical reason nesting is the part that most reliably arrives flat, and it is worth understanding because it explains why the failure is so quiet.

A nested term does not carry its position as a piece of information about itself. It carries a pointer to its parent, and that pointer is the parent’s identifier on the old platform. When the term arrives on the new platform, it gets a new identifier, assigned by the new platform. So does its parent. The pointer inside the child now refers to an identifier that means nothing in the destination, or worse, means something else entirely.

Handling this correctly means translating every parent reference from the old identifier to the new one, which requires knowing the full mapping between the two, which requires all the terms to exist before any of the relationships can be set. It is a two-pass job by nature, and a migration path that does it in one pass will move every term successfully and set no relationships at all.

The symptom is characteristic: every term is present, the count is correct, and the structure is gone. A three-level hierarchy arrives as a flat list of terms, all at the top level, in no particular order. Because nothing is missing, a count check passes, and because the terms are all there, a casual look at the term list looks fine.

Check nesting explicitly and separately from checking that terms exist. They are two different checks and only one of them is usually run.

The failure that passes every count: orphaned content

Here is the one that costs the most and is hardest to see.

Classification is really two things: the terms themselves, and the assignments joining each post to its terms. Those assignments are separate records. A migration can carry the terms perfectly and carry few or none of the assignments, and the result is a term list that looks complete sitting above an archive where nothing is classified as anything.

You will not find this by looking at your terms, because your terms are fine. You will not find it by looking at your posts, because your posts are all there. It shows up only if you look at a post and check what it is filed under, or if you open a term archive and find it empty.

The same failure occurs partially and more often: assignments survive for the terms that migrated cleanly and are lost for the ones that did not, so a subset of the archive becomes unclassified. That subset is usually the older content, for the same reason described in the 12 things that break in every CMS migration, and older content is exactly what nobody opens after a migration.

Unclassified content is not merely untidy. It is content that no longer appears anywhere except in a chronological list, which for most sites means it stops being reachable by anyone browsing. Whether those archive pages should exist at all, and what any of this means for how pages perform in search, is a separate question with a separate owner; the point here is simply that content which was findable before the move is not findable after it.

Decide what your terms mean before you move them

The mismatch is going to force a decision. Making it deliberately takes an afternoon and making it accidentally takes months to undo.

Print your term list and read it. Not scroll it. Read it. Most sites that have been running for a few years have terms created once and never used again, terms that are near-duplicates of each other, and terms created by mistake. A migration is the natural moment to deal with them, and it is much cheaper to deal with them before the move than after.

Sort every term into one of three piles. Structural, meaning it defines a section of the site and content genuinely belongs inside it. Descriptive, meaning it says what a post is about. Or neither, meaning it is a leftover. The first two are what determine how your terms should be modeled in the destination, and the third pile should not be migrated at all.

Write down the mapping explicitly, term by term. For each source term, name the destination term. Where the destination model differs, say what happens: this category becomes a term with a parent reference, this tag becomes a plain term, these three near-duplicates merge into one. A written mapping is the only way to verify the result afterward, and it is the artifact you will want when something is wrong three weeks later.

Decide what happens to merged terms’ content. When two terms become one, the posts assigned to both need to end up assigned to the survivor. This is the step most likely to be skipped, and its symptom is a post that lost half its classification.

Rehearse it on a sample. Move a small, deliberately awkward selection: a deeply nested term, a term with a single post, a merged pair, and an old post carrying several terms at once. Check the result against your written mapping before running the full move.

If your destination is a headless platform, this decision interacts with how content is modeled there generally, and what moving to a headless stack changes is worth reading alongside this, because in that case the classification structure is something you are defining rather than inheriting.

Verifying it afterward

Four checks, in this order, because each one catches something the previous one cannot:

  1. Term count. Does the destination have the number of terms your written mapping says it should? This catches wholesale loss and nothing else, but it is instant.
  2. Nesting. Open the terms that had parents and confirm they still do. This is the check that is usually skipped and it is where the most common silent failure lives.
  3. Assignments, from the content side. Open a sample of posts across the full age range of the archive and check what each is filed under. Include the oldest posts deliberately.
  4. Assignments, from the term side. Open a sample of term archives and confirm they are not empty. A term with no content behind it is either a term that lost its assignments or one that should not have been migrated.

Term archive pages are also addresses, so if their paths changed in the move, links pointing at them are broken in exactly the same way as any other link, and repairing internal links after a migration covers that half of the job. The migration guide sets out where this step belongs in the overall sequence.

FAQ

The new platform has no tags. What do I do with mine?
Decide what each tag was doing. Descriptive tags usually become plain terms in the destination’s single system. Tags being used structurally, to define sections, should be modeled the way that platform models structure. The decision is yours to make before the move, because if you do not make it the migration will.

All my terms migrated but the nesting is gone. Why?
Because a term’s position is stored as a reference to its parent’s identifier on the old platform, and identifiers change in the destination. Setting relationships correctly requires a second pass after every term exists. A one-pass migration moves all the terms and none of the structure.

My term list looks complete but posts are not filed under anything.
The terms migrated and the assignments joining them to posts did not. These are separate records and they can move independently. Check from the content side, post by post on a sample, rather than from the term list.

Should I clean up my categories and tags before migrating or after?
Before. You have to write a term-by-term mapping anyway, which means reading the whole list, and deleting a leftover term at that point costs nothing. After the move it means editing content in a platform you are still learning.

How do I check nesting without opening every term?
Take the terms your written mapping says should have parents, and check those specifically. You do not need every term; you need the ones where the failure would show, and your mapping already tells you which those are.

Written July 2026. Platform taxonomy models and import behavior change over time; verify the specifics of your own source and destination platforms before relying on any detail above.

Leave a Reply

Your email address will not be published. Required fields are marked *