An experiment in flow
I love types
Types let you express important things in a program.
They can add a layer of semantics; a mere String becomes a JobTitle, Username or a CountryName.
I would have liked to go straight for something like elm or purescript, but in a fairly large team you need to introduce changes gradually and I didn’t want to cause delays for our ongoing feature work. So flow types seemed to be obvious choice, as flow allows you to gradually add static types to individual files.
When used with babel the type annotations are completely removed at build time and so doesn’t change the untyped code which means the behaviour of the code remain the same. Essentially it can be used as a stronger kind of linter.
I decided to try using flow on our React/Redux single page app on one of our lab days. Lab days are time set aside for non feature cards, where we’re given time to experiment and investigate interesting tech.
I expected this change to be controversial, but the team was very open to it as an experiment.
I started with a couple of small PRs which included:
- Integrating with babel
- Finding an atom plugin for flow
- Creating a new version of our development environment docker image
- Adding a flow type check step to our travis CI tests
- Some example uses of flow
The example uses were for some of our core domain objects, Redux state and the related React components.
One potential use of flow types is to replace React PropTypes. The only real difference being flow types enforces the types statically rather than at run time.
We decided to gradually add flow to files as we worked on them so as to increasingly cover more and more of the app with type annotations.
The team discussions
Initially flow seemed to be going well, but at each retro more and more issues were popping up.
One of the desirable features was type based code completion. Another was being able to hover over a variable or expression and have the derived type shown; but neither really worked consistently or as expected.
Trying to find the correct type for React/Redux components and our own higher order components proved to be more difficult that I expected. Without being able to see the derived types it was difficult to know what part of the type was wrong.
Another source of confusion was trying to represent the types of our existing code. Using union and intersection types helped but made the types much more complicated. I think if we’d done some refactoring to make the code fit the types this could have been simplified, as there are real differences in the way you write code with statically typing vs dynamic typing. This also would have required the team to change the way they write and structure their code; which as a fairly new team member, I didn’t feel confident suggesting at the time.
The consensus was that the team were finding they were spending too much time and effort on the types, rather than implementing their features. They also felt that the benefits did not outweigh that extra effort.
After quite a few back and forth retro discussions, we decided to remove flow.
At first it was going to be one file at a time; a reversal of the way it was introduced, but a team member found a way to successfully remove most of it in a single PR.
I’m still disappointed that we no longer have any static typing in our client side code, especially on those core domain objects which I found confusing without any type annotations.
While I’m sad that flow types is no longer in our code base, I’m happy that the team was at least willing to try the experiment. Being able to re-examine and experiment with the technology and processes within a team is the only way to make real improvements.
I think my next experiment will be to try introducing some static typing into our ruby back end.