AI-assisted Drupal ↔ Drupal migrations
Or: how I saved myself a "migraine" with LLMs and helper scripts.
A few weeks ago, during my time at Reading Room, I took on the task of writing the Drupal 7 to Drupal 10 migrations on an upgrade job for a UK healthcare site.
If you are familiar with this sort of thing, you’ll know this crosses the D7/8 boundary, so typically would involve building the new site fresh, and then using core’s Migration API to lift-and-shift content from old to new.
This time was no exception.
In fact, the fantastic team had already pretty-much built the new site and planned out the data mapping in some detail. I just needed to connect the dots with (in the end) about fifty migrations. Roughly one-per-entity-type/bundle. Each defined in a nice tidy YAML configuration file.
Well.
(Thought I)
This sounds like a job for AI!
After all, I already have most of the mapping logic written in human words (on tickets). I just need to transform it into Drupal migration YAML text files. And one thing LLMs are really good at, is rewriting text from one genre to another.
Right?
At this point let me give a thank you and general shout out to the fabulous Reading Room, the team behind this project and one of the driving forces behind the recent, wildly successful DrupalCamp England 2025. I’ve been working with them for a while now, and can attest to their significant Drupal expertise and Digital Transformation know-how.
My migration-making workflow
The basic idea is to create a markdown file that describes the migration. It has the following structure:
# INSTRUCTIONS
Write a Drupal 7 to Drupal 10 migrate_plus migration yml into config/sync/ for the migration specified in this file.
...(add more instructions here, specific to your use case and preferences)
## Entity Mapping
Source: node (article)
Destination: node (article)
## Source Field Details
| FIELD NAME | TYPE | CARDINALITY |
| ----------------------------- | ------------------- | ----------- |
| nid | integer | 1 |
| body | text_with_summary | 1 |
...(etc)
## Destination Field Details
| FIELD NAME | TYPE | CARDINALITY |
| ----------------------------- | ------------------- | ----------- |
| nid | integer | 1 |
| body | text_with_summary | 1 |
...(etc)
## Field mapping
...Unambiguous mapping instructions written in whatever way makes sense to you and yields good results from your LLM of choice. Bullet points? A table? Up to you.Obviously, those “Field Details” sections can be auto-generated from examining the configuration of the source/destination sites. Which is where Migraine comes in. Assuming you’re set up with Migraine, you can ask it to generate that prompt file for a certain migration:
mise run mig:prompt node_articleThis creates a prompt file in .migraine/prompts/node_article.yml. Open that up and edit it until you’re happy.
Then stick some example migrations into .migraine/templates/migrations and pass it all to Aider to generate the first draft of your migration yaml with this:
mise run mig:aider:migrate node_articleThis is a wrapper for the following aider command:
aider \
--map-tokens=0 \
--read=".migraine/prompts/node_article.md" \
--read=".migraine/templates/migrations" \
--message="Read .migraine/prompts/node_article.md and follow all instructions."Configuring Aider is beyond the scope of this article, but I give an example of the configuration I used here. I liked architect mode with OpenAI o1 as the chat model, and Anthropic Claude Sonnet as the editor model.
Perhaps you’re adding to, or changing, an existing migration? Perhaps you used Migrate Upgrade to generate your migrations and you exported the config? Pass the file path and aider will update that file:
mise run mig:aider:migrate node_article \
config/sync/migrate_plus.migration.node_article.ymlAfter all this you’ll typically want to (re-)import that migration file and re-run the migration to test it. This sort of thing:
drush cis "../config/sync/migrate_plus.migration.node_article.yml" -y
drush migrate:reset-status node_article
drush migrate:rollback node_article
drush migrate:import node_article(The drush cis command is provided by the Config Import Single module).
The Migraine scripts are a little “rough-and-ready”, but there are lots of things you can do with them (including dumping a full description of all your entity types, which is quite useful to gain a quick understanding of any site). For more details check out the project page on Github.
And if you find Migraine helpful, please do raise PRs for improvements.
The conclusion: Was AI worth it?
Yes. I think so. Just about. For making the first draft of a migration yaml definition file in particular.
You’ve got to think about it in the right way. You’re still gonna go through each migration—each field mapping—one by one. You’re still gonna have to understand the content models, the data structures, migrations, the yaml definitions. You’re still gonna inspect databases and your destination site to verify imports. There is no free lunch here.
But.
The question you need to ask is this: Would you rather start from a blank canvas (or Drupal’s auto-generated base) only? Or would you rather start from something guessed by an LLM?
I found that, as long as I sent good examples that exhibited the patterns I wanted to use—as long as I used a good reasoning model (I used Open AI’s o1, which was slow but good)—then getting an LLM to generate the first draft wasn’t perfect, but it generally got me going faster than otherwise.
Also, I quickly realised, the process of preparing the AI prompt document was more-or-less identical to the process of marshalling my own understanding of the migration. Cross-referencing JIRA tickets, documentation, asking questions of other developers, other stakeholders, analysing the codebase, the database. I needed to do all that anyway. So why not write it all down in a single Markdown file?
And, once I’d done that, why not send that over to an LLM to have a go at making the first draft?
I did find, towards the end of the project I used AI less and less, because I’d got to the point where I’d learnt everything important. So it was quicker to go into this or that migration yaml and copy and paste this or that snippet.
But, that’s the point of AI isn’t it? It helps us make more progress while we’re still learning, and takes a back seat once we’ve improved. So, I’m not surprised by this, and I don’t think it’s a bad thing.
What’s next?
Well. I’ve finished that migration project now. And the AI space is moving fast. It’s difficult to say what comes next.
But if you found any of this helpful—or if you know of better ways—please reach out to me and let’s discuss our experiences. I’m available on LinkedIn here: https://www.linkedin.com/in/jamsilver/
I am convinced that intelligent incorporation of LLMs has the power to make software development better. More fun, even. Gives us all a leg up and gets us to the interesting bits, the important bits faster. With more of our brain cells still intact.
Thanks for reading.



