Editorial workflow showing Drupal content mapping, a small test, validation and a recorded import recovery path

Editorial illustration; the stages describe an import workflow and are not a live Drupal administration screen.

A content migration rarely fails because a CSV cannot be read. It fails because a field was interpreted differently, a reference points to the wrong entity, an alias collides with an existing page, or a second run creates duplicates that nobody expected.

Drupal’s Migrate API gives you a structured way to move content from a source into destination entities. The important operational habit is to treat a migration as a rehearsable process. Before production, you should be able to explain what one source row becomes, how a failed row is found, and what you will do if the result is wrong.

Summary

  • Map source fields and relationships before writing a large import.
  • Test with a small, representative fixture and inspect the resulting entities manually.
  • Keep identifiers, status, logs and a recovery note so the import can be rerun or rolled back deliberately.

What this workflow solves

Use this approach when moving content from another CMS, a spreadsheet export, an old database or a custom feed into Drupal. It is most useful when the source has more than one content type, referenced authors or categories, rich text, files or existing destination content.

It does not promise a universal migration recipe. The source schema, destination model and deployment process decide the details. The workflow gives you checkpoints that expose wrong assumptions before a large run.

1. Describe the destination before the source

List the Drupal content types, fields, media types, users, taxonomy vocabularies and URL patterns that the import must create or connect to. If the destination model is still changing, pause the migration design. A script cannot make an unsettled content model stable.

For each source field, write its destination and treatment:

Source value Destination Decision
title Article title Required; trim whitespace
body_html Body field Convert or validate allowed markup
category_name Term reference Match existing term or create by rule
old_path Path alias Check collision before saving

The decisions matter more than the table format. “Import body” is not a decision until you know how links, images, embedded markup and empty values are handled.

2. Choose a stable source identifier

Every imported row needs a way to be recognised on later runs. Use an identifier that remains stable in the source, such as an original record ID, rather than a title that an editor can change.

This identifier helps the migration update the right destination entity, report a failed row and distinguish a correction from a duplicate. If the source has no stable ID, create one before importing and document how it was derived.

Do not discard the old identifier after a successful import. It is part of the audit trail and often the quickest way to answer “which source record produced this page?”

3. Map dependencies in an order you can explain

Referenced content introduces an order problem. An article may refer to an author, category, image or landing page that must exist first. Draw the dependency chain:

  1. users or owners;
  2. taxonomy terms;
  3. media and files;
  4. referenced pages;
  5. articles and other dependent content.

The exact order depends on the project. The useful test is whether you can explain what happens when a reference is missing. Choose a deliberate policy: create a controlled placeholder, leave the reference empty and report it, or fail the row. Silent “best guesses” are difficult to correct later.

4. Build a small representative fixture

Do not start with the whole export. Create a fixture containing an ordinary row, a row with missing optional values, a row with long text, a row with references, an unpublished row and a row with awkward characters or an old link.

The fixture is not meant to be statistically perfect. It is meant to exercise the decisions that can break the destination. Keep it small enough that you can inspect every resulting entity.

Run the migration in a disposable environment or a database snapshot that can be restored. A dry run is useful, but it is not the same as inspecting saved entities and rendered pages.

5. Check text, markup and files separately

Source HTML may contain tags, classes, embeds or links that do not belong in the destination field. Decide whether to convert, allow, remove or manually review each class of markup. Do not assume that a successful database write means the rendered content is correct.

Files need their own checks: filename collisions, missing source files, public or private storage, image dimensions, alt text and references from the imported body. If the import creates a node but loses its images, the migration is only partially successful.

Render a sample page as an anonymous visitor. Check headings, links, images, captions and line breaks. This is where a source-to-destination mapping becomes a reader-visible outcome.

6. Make reruns safe to reason about

Decide what happens when you run the migration twice. A rerun may update an existing entity, skip it, create a duplicate or fail. The right choice depends on whether the source is authoritative and whether editors may have changed the destination.

Write the rule down before testing. Then run the fixture once, change one source value, run it again and inspect the result. Check that the stable identifier points to the same destination entity and that an unrelated manual edit is not overwritten accidentally.

This test is more valuable than a successful first import because most real migrations need a correction pass.

7. Validate counts and relationships

After a run, compare more than the total number of rows. Check counts by content type and publication state. Sample references, terms, aliases, files and unmapped source records. Review the migration messages and logs for warnings, not only fatal errors.

Create a short acceptance sheet:

  • expected source rows and imported rows;
  • rows intentionally skipped and why;
  • missing or unresolved references;
  • duplicate or conflicting aliases;
  • files copied and files rejected;
  • pages rendered and links tested.

If a number does not match, stop and investigate. “Almost all imported” is not a meaningful success criterion until the missing set is understood.

8. Test permissions and publication state

An imported page can be correct for an administrator and wrong for an ordinary editor or visitor. Check access, workflow state, author attribution, moderation requirements and cache behaviour with the roles that matter to the site.

Do not publish everything just to make validation easier. If the source includes drafts, archived material or private records, preserve that distinction in the destination and test it explicitly.

9. Write the recovery note before production

Record the database backup or snapshot, source export location, migration command or deployment entry point, date, expected counts and the person responsible for the run. State how to stop, restore, or remove imported entities if the acceptance checks fail.

Recovery is not always “delete everything.” If editors have started working in Drupal, a blanket deletion can destroy legitimate changes. A safer plan may be to restore a pre-import snapshot, revert a controlled set of entities, or correct the mapping and rerun only the affected records.

If the project uses configuration in code, keep the migration definition with the project and review it through the same process as other deployment changes. Drupal Configuration Changes Should Travel as Code explains the related discipline.

10. Run production in a bounded window

Choose a time when the team can observe the run and respond. Take the agreed backup, record the starting state, run a bounded batch if the project supports it, and compare the results with the acceptance sheet.

Keep the import output and logs. Note the first unexpected result, the action taken and the final verification. If you need to retry, change one thing at a time so the next result remains interpretable.

Common mistakes

  • Designing mappings before agreeing on the Drupal content model.
  • Using titles as identifiers and creating duplicates on reruns.
  • Testing only ordinary rows and missing the awkward cases.
  • Checking database counts without rendering pages or testing access.
  • Treating a backup as recovery without deciding what restoration would mean.

My Take

The most reliable migration is usually the one that feels a little slow at the beginning. A small fixture and a written acceptance sheet force the team to make decisions while the cost of being wrong is low. Once the mapping is understood, the larger import becomes a controlled repetition rather than a leap of faith.

Conclusion

Model the destination, choose stable identifiers, map dependencies, test a representative fixture, and validate the rendered result. Before production, record the expected outcome and a recovery path that respects edits made after the import. The Migrate API can move data, but the rehearsal is what makes the move operable.

Sources