In "Infrastructure as Code: From the Iron Age to the Cloud Age," Kief Morris describes the “iron age” as a time when “infrastructure growth was limited by the hardware purchasing cycle. Since it could take weeks for a new server to arrive, there was little pressure to rapidly install and configure an operating system on it.”
But now, in the “cloud age”, systems are deployed using cloud services. Many of these services, including Contentful, provide friendly UIs where users can configure environments. Unfortunately, as the number of services that teams operate grows, it becomes unmanageable to maintain each one by pointing and clicking through the UI. “Infrastructure as code” is an approach to deal with this issue: configuration of each service is automated, and the configuration is stored along with the source code for the application in version control.
Monolithic CMS platforms commonly used today don’t work well for teams that employ agile development practices such as infrastructure as code and continuous delivery. Provisioning, migrating and testing those platforms is inherently difficult to automate.
Contentful, on the other hand, is a content platform built for the cloud age.
In the iron age, software development was built around the idea of boxed software, with new versions released once a year, or even less. Expensive and time-consuming release processes were common, with build everything, then test everything and then release everything being the way it was done.
In the cloud age, agile teams release frequently, often several times a week, so expensive and time-consuming release processes can’t be used without grinding deployments to a halt. For this reason, many agile teams have adopted continuous delivery.
Teams that employ continuous delivery have a deployment pipeline, a set of validations through which a new version of software must pass on its way to production. The pipeline has different stages, each of which has its own environment for running the software.
This often happens on developers’ personal computers, where they author new versions of the software. When developers want to deploy a new version, they commit their latest code to a version control system and push it to a shared repository. This initiates the pipeline.
This depends on a build script that is capable of creating the running version of the software from the source code, performing steps like installing dependencies, compiling or transpiling code and verifying that the build script runs successfully.
This depends on automated tests being available to verify that the software works as expected, and that new versions have not broken functionality that worked in previous versions.
Developers and QAs use the software and perform more testing, ensuring that the acceptance criteria for the most recent changes have been met and the software is ready for production.
The software is uploaded to production servers. The new version is now live.
Teams using Contentful can provision environments, migrate content and write tests using APIs, allowing content management to fit into agile development practices such as Infrastructure as Code and Continuous Delivery, enabling software development teams to create high-quality software and to deploy quickly.
While adopting Continuous Delivery might require some upfront investment and change in team culture, the long-term business benefits outweigh any associated pains:
Faster time to market: Continuous Delivery facilitates and encourages shorter delivery cycles.
Improved engineering practices: When the business demands more regular release cycles, technology teams are incentivized to leverage higher levels of automation and to adopt practices such as test-driven development.
Reduced risk: By having small and more focused deployments, it is less likely to have something go wrong than in bigger and more complex releases.
Releasing is a business decision: Taking away the complexities of deployment from developers also means that the release of a new feature can happen independently from them, leaving business stakeholders in charge of pushing the button.
Exposed inefficiencies: A continuous delivery system can shift focus from the development inefficiencies to the real problems that are causing them, such as (for instance) unclear requirements.
For the software to run in each environment, it requires not only the source code for the software, but also infrastructure, such as the services and attached resources that it depends on. A common example is a database. If the software requires a database to be available, then one will need to be provisioned for each environment.
Many teams developing software that depend on databases are familiar with this situation. They have dev, QA and production databases set up with provisioning and migration scripts. These scripts can create databases for the correct schema for the current version of the software, updating it from a previous version to the current one. The scripts can also update actual data, when the data model changes in such a way that existing data is no longer valid according to the new schema. Finally, the team will also create integration tests that validate that the database, schema, and sometimes data are correct for the current version of the software.
This is also true for Software as a Service (SaaS): if the software makes use of a SaaS product, then this service needs to be available and configured for the given environment. SaaS is infrastructure, and to work with continuous delivery, SaaS needs to be treated as code.
Contentful allows teams to create spaces, which are like databases for content. As with the databases example above, teams will often have dev, QA and production spaces set up in Contentful.
In order to provide content infrastructure for each environment in your deployment pipeline, Contentful spaces provide multiple space environments: in production environments, where your editors and authors maintain your production content and where your apps and websites query that content, and in several sandbox environments, one for each stage in your deployment pipeline. Environments are built into each stage, similar to the way branches are built into each repo in git.
A further challenge for content management is that there are two life cycles happening at the same time, both requiring authoring, verification, and release: one is the software deployment pipeline, and the other is the content creation workflow. While software developers are authoring and releasing software versions, content creators are authoring and releasing content at the same time.
Contentful provides a basic workflow built-in, with “draft” and “published” stages that control when content is available to your live systems, a preview API that can be used for content review, as well as features to build your own, more advanced workflows. Content creation happens using just the production environment in the customer’s space: content creators create drafts, which are reviewed by way of the preview API, and then published. Other environments, such as dev or QA are only used as part of the software deployment pipeline.
Contentful provides a content platform as a service which can be treated as code and included in your deployment pipeline. To accomplish this, you need to have provisioning scripts, which can configure a space environment with your content model, and migration scripts, which can migrate a space environment from an older content model to the current one and, when needed, modify entries so that they are valid according to the current model. Teams should also write tests that can be run to verify that the current software works with the current content model and latest content.
Implementing CMS as Code requires saving the content model in your version control repository along with the rest of your source code, so that the version of the software revision matches the version of the content model. This is done by using Contentful’s Migration CLI, to define the configuration of all content types that have been added to the dev environment.
The initial migration can be automatically created using the generate migration command. Subsequent migrations are created by developers as the content model evolves.
When new environments are created, they are created as a copy of the production environment.
Databases and platforms such as Ruby on Rails have well-developed migration strategies. For CMS as code, the migration scripts need to be written by the team. Contentful’s migration tool, can help in the creation of such scripts.
Traditional Rails-style migrations have up and down functions for each version of their saved content models. The up function should be able to migrate a space environment to the latest version from the previous one, while the down function should do the reverse.
Real-world experience shows that while teams spend many hours maintaining down functions, they frequently run into problems when they try to use them. There is often no real way to down migrate content without resorting to backups. For instance, old content may have been dropped, or relationships that were formerly one-to-one, may now be one-to-many, meaning that reverting would lose content. A forward-only plan, along with good backups, is generally a better option.
A forward-only migration plan should use an expand-and-contract pattern to safely make backward incompatible changes to your content model, instead of writing down functions.
The expand phase should add new fields and content types, and transform content entries such that the changes do not prevent the currently released version of the software from running. By leaving the existing content in place, while augmenting the content types and content, the new version of the software can also run. The content model can now support both the current version and the new version of your software in parallel. Your software can now be released to your QA users, and rolled out to the rest of your user base. If a problem occurs at any point, the rollout can be stopped and reverted.
Once all users are successfully using your new version, the contract phase of the migration is run to remove unneeded content types, fields, and content.
To illustrate this process with a simple example, consider a blog system that only has one content type, a post. The post content type has an author field in it, which is a text field where editors can enter the author’s name. In the next release of the software, new requirements result in the need for the author field to be an independent content type that is referenced from post.
The expand phase does not alter the author field or its content. Rather, it adds a new field called, for example,
The migration would iterate through each post entry, and create a new author entry for each of the old author fields, and set
AuthorRef as a reference to this new author entry.
Your editorial teams can now add additional information to the author entries, while the existing software continues to run, as it depends only on the old author field.
When the content is ready, you can begin to roll out your new software. If problems occur at any point, you can revert the rollout.
Once the rollout has successfully completed, your contract phase can now run and remove the old author field permanently. If you prefer to use the name of the old field instead of the name used in the expand phase, you can do this as another migration, with its own expand and contract phases. If you can just use the new name, then your migration is complete.
If you find yourself in a situation where you really do need to revert, it’s a good idea to have a backup to recover from. In any case, you can create a new forward migration to return to the old content model.