Dependency Upgrade Policies
Core packages such as
@babeletc. should be kept up with. These packages have minimal dependencies and typically have well documented and thoughtful upgrade paths. Updating core dependencies is non negotiable because of the increasing marginal cost of major version updates. For packages like this it may also be a good idea to delay major upgrades until a month or two have passed.
Avoid upgrading packages that are constantly releasing patch versions. Prefer upgrading once the package has stabilized.
Upgrades that drop sub-dependencies are a win! The fewer packages installed the better. This means less code to ship and less possibility for library specific bugs.
Upgrades that reduce the number of discrepant shared sub-dependencies are a win! If upgrading a package minor version means that it will share the same version dependency with another top-level package this reduces the amount of code that is shipped.
Upgrades for non-critical packages such as
prettiercan be done as time permits. e.g., if all tests pass and everything looks OK, upgrading is a no brainer.
When in doubt, discuss upgrades in the Frontend Technical Steering Committee (TSC)
Understand that upgrading is a double-edged sword.
If you let everything stagnate and upgrade nothing, simply because it all works, inevitably you will have to upgrade a package for a new security patch, a feature to help make code more maintainable, or in the worst case, so you can upgrade another dependency.
This can become a massive amount of work, as the package is maybe many minor versions or even multiple major versions out of date. If it becomes difficult enough to upgrade, the engineer may choose to avoid upgrading altogether and implement a hack to work around the need. This of course is doubly bad, as when you do absolutely need to upgrade, not only is it difficult to upgrade a very outdated package, you now have to understand and reverse the hack.
By being proactive and keeping up with major and minor versions, we will typically need to only consider a small set of changes in the package. This is safer as it is less likely for usages of the package to break since there is simply less to validate. This also helps to keep up with the latest performance updates, bug fixes, security patches. And helps to ensure when a developer is looking for documentation on the usage of a package, they won’t have to hunt for older versions of the documentation.
On the other hand, choosing to constantly keep packages updated to the absolute most recent versions can have numerous consequences.
Upgrading can in some cases be a non-trivial effort for marginal gains. Validating even small upgrades takes time, often when really nothing has changed. (This is of course a slippery slope, wait too long and packages can become exponentially more work to upgrade)
There is a cost to too much
package.jsonchurn such as developers having to constantly run
yarn installon every git pull.
A security vulnerability may be introduced in a new version that was not previously present. If we had waited to upgrade until the patch we would not be vulnerable.
New versions of the package may include new defects introduced by the maintainers, which may be difficult to detect during validation.
Overall, our approach to upgrading packages is a balanced one, taking into account both the potential benefits and drawbacks of upgrading. Carefully consider each upgrade decision to ensure that it is the right choice for our codebase. This guideline allows us to keep our codebase up-to-date and secure, while avoiding unnecessary effort and potential risks.
yarn outdated --color | sort
yarn upgrade --latest [package-name] [...]
If you would like to upgrade a group of packages (for example
@babel) you can use
yarn upgrade --latest $(yarn outdated | cut -d' ' -f1 | grep [group-name])
Depending on what category of package you’ve upgraded there are a number of steps you’ll need to take to validate that the upgrade is safe and that nothing has broken. The first step is to consider what categories the package being upgraded falls into.
The general category of frontend dependencies we have are
Feature specific dependencies
This is a package that is used for a particular feature in the application. For example, in our group comment box we use a package to handle @ mentions. This dependency is highly isolated to this feature and is not generally used anywhere else.
Framework style dependencies
These are dependencies which are generally globally used throughout the application. Things like
momentetc. These can be difficult to validate when there is lots of usage across the application, and will depend on what has changed in the new version of the library.
These are almost always the easiest to validate, since passing tests in CI essentially guarantees that the upgrade will not be problematic.
Application build dependencies
These are dependencies used when running the application in development. Such as
fork-ts-checker-webpack-plugin. Care should still be taken when upgrading these dependencies so we are not breaking the development environment, as that can slow everyone down. However, a breakage in these is not nearly as critical as a package that affects production.
Read the CHANGELOG of the package
This will give you context into what you need to look out for. If the package has explicit breaking changes it’s important to be aware of those so you can validate that our usages is not affected by those changes. If we are affected changes will have to be made to Sentry’s usage of that dependency to make it compatible.
Is CI passing?
This is a really good first indicator of if the change is safe. If CI is failing there’s a good chance the application itself is actually broken. You will need to fix failing tests before you can merge a package upgrade.
However, DO NOT rely on CI passing to guarantee that the package was successfully upgraded! Our test coverage is good, but it is not perfect.
When upgrading Testing dependencies, at this point may also want to check the test logs and make sure there’s no unusual output, ensure the number of tests run has not changed, and even check that the performance of the test runs has not regressed.
Does the application work?
This is where it begins to become important to understand the category of the dependency. It’s very useful to use the Vercel Frontend Previews during this step to see how the application acts with the upgraded package in a production build.
For feature-specific dependencies, you can specifically check that the features using the package are correctly working as expected.
For framework dependencies, you’ll want to spot-check as much as you can. You should take into consideration the changes in the new version of the package and validate that whatever was changed behaves as expected. Looking for “odd” usage of APIs can be helpful here (e.g., are we doing manual
ReactDOMcalls anywhere? etc)
For application build dependencies we should already be feeling pretty good at this step, considering the application built and passed CI. But it’s still worth checking the change log to make sure nothing in the build pipeline may have affected the application. However, problems with the application build here may be hard to spot as they will likely be subtle if CI did not catch them.
For test and development dependencies, it’s highly likely the application should be working fine. The only scenario where the application could be broken is if the dependency DID have some effect on the application code (in which case it should not be categorized as a pure test or dev dependency).
This may also be a good time to check if the size-limit report bot has left a comment indicating a change in bundle size. If the bundle has increased dramatically it’s worth investigating (likewise if it’s decreased in size dramatically)
Does the development environment work
If you’ve upgraded a build or development dependency, you’ll definitely want to make sure the development environment is still working as expected.
sentry devserverstill correctly run the client-side application?
yarn dev-uistill run the client-only version of the application?
Depending on what package you’re upgrading, you’ll what to consider what to test. For example, if you upgraded
fork-ts-checker-webpack-pluginyou’ll want to validate that types are still being checked in development.
Upgrade the package in getsentry if applicable
Developer tooling and build packages are currently duplicated in
package.json. For those packages, it is important to remember that you will need to upgrade the package in both places.
It’s good to understand exactly what has changed with the upgrade. It’s generally a bad thing if upgrading has caused a discrepancy in shared sub dependency versions. For example, if you upgrade
lodashbut another top level package specifies that it needs and older version of
lodashwe will now have two versions of
lodashinstalled. That means two separate versions of
lodashwill be shipped!
Generally you will want to make sure nothing has duplicated versions, in that case you may need to use
[yarn-deduplicate](https://www.npmjs.com/package/yarn-deduplicate)or in the worst case you may need to upgrade the package which is pulling in the offending older versions.
See The Ultimate Guide to yarn.lock Lockfiles for an in-depth look on how these work.
Validate in production after the upgrade is merged
Finally, in some cases, it may be worth checking that the application is operating as expected in production. In some rare cases a package may behave differently in production (though this is very rare, as checking the frontend preview is very close to the same production bundle).
We use GitHub’s Dependabot to automatically open pull requests for package upgrades.
It has two primary modes of operation
Creating security vulnerability PRs
These package version bumps are critical as they fix known CVE vulnerabilities reported in the GitHub Advisory Database. These should be merged ASAP. Learn more about Dependabot alerts on GitHub. Again, keeping dependencies up to date helps to ensure that when a vulnerability is discovered, we’re able to upgrade that package without having to upgrade across multiple minor or even major versions.
Opening version upgrade PRs
The PRs this mode creates is equivalent to manually doing upgrades of packages in the
package.json. This is convenient because we can at a glance see if CI is passing for the upgrade. Dependabot also is smart about upgrading groups of packages, for example, it will upgrade
@type/*packages alongside the library.
Both of these modes are currently in use for
getsentry/sentry. You can look at the dependabot.yml for more specifics on the interval for how often it will automatically open PRs and how many it will open at once.
To tackle a dependabot PR there are a few steps to take
To get CI to finish running you will need to do two things
/gcbrunto trigger off the
Label the PR with
trigger-getsentry-externalto trigger the
getsentry / frontendcheck.
Why do I have to do this?
dependabotuser is considered an outside contributor so the same precautions must be taken with these PRs as would be with an outside contributor. It’s important to note that this is an explicit action you’re taking to indicate that the upgrade is generally considered safe. Since it is theoretically possible for a package to exfiltrate secrets from the
getsentry/getsentryGitHub actions run by including malicious code in the new package. This situation however is highly unlikely and would need to be a coordinated supply chain attack by one of our already vetted dependencies.
Very conveniently dependabot includes a section in the PR description for the upgrade with the relevant release notes for the versions being upgraded. Read this! If the release notes are not included you may need to track down the CHANGELOG file in the packages repository itself to understand what has changed.
All steps in Steps to validate a successful upgrade should be followed to validate that the upgrade will not break anything.
Approve and merge the PR. Ideally, you should get a second pair of eyes on the PR and have a second person review it. But as of now, we have no explicit requirement other than yourself reviewing it.
- Continuous dependency updates: Improving processes by front-loading pain | Snyk
- When should dependencies be updated?