Photo by Joshua Aragon on Unsplash

Upgrading Flow Codebases

After releasing v0.85.0, we’ve been receiving a lot of feedback from developers about the difficulty of upgrading Flow in their codebases. This is unfortunate — since then, we’ve added a few new type system features, fixed soundness bugs, improved performance, and improved the Flow developer experience, so staying on old versions is keeping these developers from having a much better Flow experience. In order to help everyone keep their Flow versions up-to-date, we wanted to go over our internal upgrading strategy so that you can figure out the best way for you to upgrade Flow in your repos. In this blog post, we will:

  1. Explain how we upgrade Flow in our giant internal monorepos at Facebook
  2. Justify our upgrade process, which adds a lot of error suppressions
  3. Go over some of the drawbacks of error suppressions
  4. Present resources for learning how to fix errors on ReactRedux’s connect function

Other than the advice on fixing missing annotation errors in v0.85.0, you can apply our upgrade process described here every time you need to upgrade Flow.

Automatically Suppressing New Errors

When we upgrade Flow internally, we add suppressions to all of the new error sites using a tool in the Flow repo. Before using the tool, you’ll need to add a suppress_comment regex to your .flowconfig.

To use the tool that adds suppressions, you’ll need to clone the Flow repo, navigate to packages/flow-dev-tools and run yarn install. After that, you can use the tool from the root of the Flow repo with ./tool. flow-dev-tools has a few subcommands, but the one that we are interested in is add-comments. For more information on add-comments, run ./tool add-comments --help.

When we upgrade our Flow version, we run:

$ cd /path/to/project
$ /path/to/tool add-comments --all --bin /path/to/flow --check check --comment "\$FlowFixMe This comment suppresses an error found when upgrading Flow to v0.xx.0. To view the error, delete this comment and run Flow." .

This command adds a comment with the specified text to every error site in the project. After the command runs, Flow will report no errors.

For example, let’s say your suppression_comment is configured to be:

.flowconfig
...
[options]
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
...

Suppose you have this foo.js file:

foo.js
// @flow
const x: number = "string";

When you run Flow, you get an error pointing to the erroneous assignment. After running the suppression tool, your file would look like this:

foo.js
// @flow
/* $FlowFixMe This comment suppresses an error found when upgrading Flow */
const x: number = "string";

… and now running Flow will give you no errors on this file.

Note that flow-dev-tools has a similar command for removing unused error suppressions. We also run this with every release. I recommend running ./tool remove-comments --help for more information on its usage.

Justifying the Coverage Loss

Adding error suppressions instead of fixing the new errors that Flow finds will definitely decrease coverage in your project. However, there are good reasons to make this tradeoff:

  1. New errors are usually caused by an improvement in Flow’s inference that finds more bugs. v0.85.0 is no different in this respect, and I recommend reading our previous blog post about v0.85.0 for more details. If Flow is not reporting a legitimate error, the coverage is just an illusion. It is better to be aware of the errors than to refrain from upgrading because of the coverage loss.
    That said, Flow errors usually cause an any to be inferred. Flow does this for performance considerations, and to prevent your IDE from getting polluted by downstream errors caused by the bad type. If a new error causes a semi-accurate type to become any, you may lose some actual coverage.
  2. Flow gets better with each release, so staying behind will keep you from reaping the new benefits. A few concrete changes that have landed (with blog posts) since v0.85.0 were improved React support via React.AbstractComponent and a dramatic improvement to the Flow dev experience with parallelizable commands.
    It’s also worth noting that the annotations required by v0.85.0 will be necessary to power the eventual replacement for gen-flow-files.
  3. Delaying an upgrade doesn’t make it easier. Most new Flow versions come with new errors, so staying behind will only enlarge the set of errors you need to address in order to get caught up.

And remember, Flow was built for incremental adoption, so partial coverage is something Flow is good at!

The Downside of Suppressions

In uncommon cases, error suppressions can suppress more than one error, which can cause future errors to be missed. Here’s a few ways for that to happen:

  1. If a suppression appears directly above any of the lines mentioned in an error message (not just the main one), then the error is suppressed. If the line under a suppression is mentioned in the blame of a new error later, then the new error will also be suppressed.
  2. Suppressions will suppress every error on the line directly below it. So if you suppress an error and then make a change that causes a new error on the same line, then the suppression may hide this new error from you.

These bad side-effects are rare, but they do happen in practice. The Flow team has started to discuss changes to how suppressions work in order to prevent these side-effects.

Fixing Errors with ReactRedux’s connect Function

Internally, the majority of the errors we suppressed were on React components that were wrapped by ReactRedux’s connect function. Since then, the people at flow-typed discussed new options for the library definition and ultimately settled on a PR put up by Peter Leonov.

Later, Ville Saukkonen, one of the maintainers of flow-typed, made instructions to fix errors on connected components. His repo is using ReactRedux 5 and Redux 3.6, but his advice applies to ReactRedux 4.x.x as well. You can check out those instructions in this gist. When Ville was upgrading Smartly.io’s codebases to v0.85.0, he included a shortened link to his gist in the suppression comment so that developers at Smartly.io could easily get instructions to fix the errors — you may want to consider doing the same! Incentivizing developers to fix Flow suppressions can be hard, so making it as easy as possible to fix them will be worth the investment.

That’s All!

I hope this insight into how we upgrade Flow internally will help you keep up with Flow’s new releases in your own projects. Flow has a lot of exciting changes on the horizon, so you’re going to want to keep up!

Further Reading