Safe and convenient CMS migrations with scripted migrations and sandbox environments

Updating your content models manually is risky and inconvenient. You can accidentally update the wrong field, or even remove it. Not only freaking out your content managers, but also yourself, as you’ll be working on damage control for the next few hours. Worry no more. Scripted migrations are here to save you.

Problems with manual content model changes

Let’s step back and look at the problems with changing content models manually. At De Voorhoede we usually work with DatoCMS. DatoCMS only supports scripted migrations recently. Until then, we used to run into problems having our content models get out of sync with our code until merging and deploying the code changes.

Adding fields

For example, imagine you’re working on a feature to add a new field to add a body text to a component. You add a new field to that component’s model. However, while you’re still working on the feature, the field is already available for content managers. But until the code to use that field is merged and deployed, the field doesn’t do anything on production. This is confusing.

Removing or renaming fields

How would it work to remove this button from the page and therefore also remove the field in the CMS?

Adding new fields is actually the least of your problems. Removing or renaming fields is more painful. Fields can only be removed or renamed once they are not used anymore in the code. Otherwise your query will simply fail. Therefore, you end up only deleting fields once the code is merged and released to not use the old fields anymore. But by this time you have probably forgotten which field should be deleted.

How you would delete a field manually

You can imagine this will one day end with production broken, you adding the field again, to find out the content of the records having this field filled in previously is now gone. Or you’re afraid of deleting the incorrect field, breaking production and therefore don’t delete it at all. This will become messy over time.

Scripted migrations to the rescue!

Scripted migrations solve those issues. They are like recipes for a CMS, containing all the steps needed to get from the previous state to the new content model structure. The scripts are stored in your codebase, so now your content modeling is tightly coupled with your code change.

For instance, a migration script for the removal of a field would look like this:

// migrations/1669119024_removeField.ts
import { Client } from ‘@datocms/cli/lib/cma-client-node’;

export default async function (client: Client) {
  await client.fields.destroy('home::cases_overview_page_link_label');

On top of that, sandbox environments, which are supported by both DatoCMS and Contentful, provide a nice way to test out content model changes and migration scripts during development. Content modeling is hard. And therefore being able to test content model changes in isolation is huge!

Sandboxes for preview deployments

Sandboxes are also useful for preview deployments. You can use a rule in your team to use your branch name for your sandbox environment (with `/` replaced by `-`, as slashes are not supported by DatoCMS). Since your branch name is available in about every CI tool, you can now easily run preview deployments on pull requests using the new migration scripts from that pull request in isolation.

Going to production

Once a pull request gets merged, it’s time to get the content model changes on the primary environment. 

You could promote the sandbox you just created for the preview deployment. However, I don’t recommend this. After you created that sandbox, another pull request might have been merged. This pull request could contain migrations that are not yet applied on your sandbox. Therefore it’s best to always run the migrations on a fresh sandbox environment, based on your primary environment. 

For clarity I recommend to use the version number of your code for environments created for production. Another tip is to keep the previous environment around after a production deployment. This way you can easily recover from disaster.

Full backup capabilities

Recovering from disaster also gets much more reliable for long term recovery when using scripted migrations. 

Often we set up daily backups for DatoCMS, exporting all the contents as JSON to an S3 bucket. But without scripted migrations, you still don’t have the history of your content models. So if something has gone wrong a month ago, you have all the content from then, but it’s pretty useless. Because since then, your content models have changed. Now your backed up content doesn’t match your models anymore, leaving you still unable to recover. 

With scripted migrations you can recover your content models up to that point, as you can spin up a new CMS instance and run the migrations created until a month ago. Now, your content backup is in sync with your content models!

Generated migrations

The biggest downside of scripted migrations was that it was time consuming to write scripted migrations. Luckily, that’s now in the past, since DatoCMS can generate migration scripts for you:

npx @datocms/cli migrations:new '<name of migration>’ --autogenerate=<environment name to check diff for to main environment>

That’s it for now! Happy content modeling!

You might also like

← All blog posts