Nx Migrate to Solve Upgrade Problem
Introduction
Recently, I added an automatic package update feature to the new Nx Monorepo project via GitHub Action. The motivation for implementing this feature comes from the difficulties faced due to neglecting updates in old projects, so I hope to have a more automated way to update dependencies and frameworks in the new architecture.
Nx has a dedicated nx migrate
command to help us achieve this, and there are some very interesting mechanisms and concepts behind it.
Developers Should Keep Up with the Times, It’s Not Contradict Long-Term Product Goals
Developers enjoy engaging with shiny new things because they often bring higher productivity and better development experiences. While experimental side projects are always fully utilized, real work environments often present various obstacles, leading to a gradual abandonment of keeping up with new technologies.
We always assign the task of upgrading and maintenance the lowest priority, which is a reasonable decision, as the core goal of the product is to meet user needs rather than to please developers. However, the relationship between the two is complementary.
It is not to assert that “new things” are absolutely better than “old things,” but rather that new things usually bring new ways of thinking and solutions to problems, allowing functionalities to be built in a more elegant manner, especially in the front-end ecosystem, which is unavoidable.
When you stagnate in updating technology, future upgrades will incur higher costs and face greater challenges.
Keeping Tools Evergreen
The best way to keep the tools at hand from rusting and becoming outdated is to continuously and frequently execute updates of all sizes in a simple manner. I have noticed that when using Nx, they are striving to reduce the pain points of version iteration:
- Nx borrowed the concept of
ng update
from Angular to providenx migrate
, which not only automatically updates npm packages but also runs migration scripts when significant changes occur to migrate code and configuration files to the new version. - It advocates for the Single Version Policy to ensure that all packages in the entire project are of the same version, avoiding compatibility issues between different versions.
Nx’s Multi-Step Update
For example, if you are working on a large repo (possibly even within a monorepo), there may be countless PRs waiting to be merged… In this case, migration needs to occur simultaneously with development. Nx has considered this issue and provided a solution: nx migrate
.
The goal of nx migrate
is to automate the process to a certain extent and then allow developers to take over the remaining parts, enabling them to control the migration process. After execution, the following processes will be initiated:
- Trigger an analysis of the local working environment to determine which packages need to be updated.
- Update the versions in
package.json
, but do not install them. - Generate a
migration.json
file that contains pointers to scripts that need to be executed to automate the migration of code and configuration files to the new version.
At this point, the upgrade process will pause, allowing developers to review and adjust changes to package.json
and the contents of migrations.json
as needed. If everything is okay, continue with:
npm install
And execute the automatic migration script:
nx migrate --run-migrations=migrations.json
The migration script looks like this:
{ "migrations": [ { "version": "11.0.0-beta.0", "description": "Rename emotion packages to match new 11.0.0 package names", "factory": "./src/migrations/update-11-0-0/rename-emotion-packages-11-0-0", "package": "@nrwl/react", "name": "rename-emotion-packages-11-0.0" }, { "version": "11.0.0-beta.0", "description": "Update libraries", "factory": "./src/migrations/update-11-0-0/update-11-0-0", "package": "@nrwl/react", "name": "update-11-0.0" }, { "version": "10.2.1-beta.1", "description": "Adjusts the tsconfig mapping", "factory": "./src/migrations/update-10-2-1/update-10-2-1", "package": "@nrwl/storybook", "name": "update-10-2-1" }, { "version": "10.3.1-beta.1", "description": "Add missing storybook config to lint target", "factory": "./src/migrations/update-10-3-0/update-10-3-0", "package": "@nrwl/storybook", "name": "update-10-3-1" }, { "version": "11.0.12", "description": "Update storybook if installed and above 6", "factory": "./src/migrations/update-11-0-12/update-storybook", "package": "@nrwl/storybook", "name": "update-11-0-12" } ]}
The benefit of doing this is that nx migrate --run-migrations=migrations.json
can be executed multiple times, which is very useful when migrating the overall project version across different branches. All PRs and branches can rebase
with the latest trunk to obtain migrations.json
and update in their respective environments before merging, reducing the upgrade difficulties caused by differences between branches.