Contententful have a wonderful headless CMS. It has a UI for the content editors but the developers get a stream of JSON. This means that the consuming application is free to do anything with the output – which is a major contrast to other CMS systems.
Contentful have a CLI tool that allows migrations to be applied to the schemas (and content). This is not the approach that my team has taken (largely because the migration tool did not exist when we needed it).
Our approach is to have a declarative content schema in code. On deployment it compares this to the environments current state and attempts to upgrade it.
Upgrades happen on a field level of each content type. Once a content type has been upgraded then the widget type for each field is set.
We don’t attempt to remove fields or migrate data as we have not in two years had the need to automate this (we have manually removed a few fields or redundant content types).
This approach is closer to desired state configuration than migrations. It has the benefit of acting as it’s own complete documentation.