---
title: Static Types In Medias Res
teaser: 'Adding static types after the fact can be a boon to your React Native codebase.

  '
tags: react native,javascript,types,refactoring
author: Sid Raval
published_on: 2018-10-08
---

Several colleagues and I recently wrapped up a 5 month long
[React Native](https://facebook.github.io/react-native/) project.
The client had come to thoughtbot with an idea, and went through our
[product design sprint](https://thoughtbot.com/product-design-sprint) process
to come up with an MVP that we would start designing and developing.

One constraint the team faced was a hard 8 week deadline, due to an external
event that could not be rescheduled. Additionally, part of the point of an MVP
is to figure out if there is a strong market for your product. With these
constraints in mind, the thoughtbot developers strategically chose to take on
technical debt while building the MVP in the interest of getting something out
the door.

One concrete form this technical debt took was starting the codebase _without_
a static type checker for our JavaScript. Not all the team members were familiar
with statically typed code, and so we chose to forego a typechecker so each
developer could hit the ground running.

To everyone's delight the initial launch of the product showed promise, and so
our client chose to continue our relationship and work on the next version of
the application. Given that we were now building an application with some
longevity, the developers chose to look towards maintainability of the
application before working on new features. We spoke with the client who agreed
to two weeks of refactoring and bug squashing.

Based on the good experience two of the developers had in a past TypeScript +
React Native codebase and the many well known benefits of statically typed code,
the team decided to retrofit the codebase with a static type checker.

We chose to move forward with [Flow](https://flow.org) because it proved
much easier (than [TypeScript](https://www.typescriptlang.org/))
to introduce to a project mid-life. Sadly, TypeScript is a much more fully
featured language that we would have preferred using.

![Flow example](https://images.thoughtbot.com/blog-vellum-image-uploads/bgRDz3pSNis4iunONTEm_carbon(1).png)

## Benefits & Learnings

The holy grail of a well-typed Redux + React Native codebase is type checking all
the way from the Redux store through your connected components. This ensures that
changes to your data modeling (e.g. changes to reducers or changes to the store's
shape) are propagated to your components. With this goal in mind, we started our
retroactive typing process by by adding types to our Redux modules, one by one. 
This proved to be a good choice for a few reasons:

* Modeling the data at the state/reducer level laid the groundwork for typing our
  React components.
* The types at the Redux action/reducer/state level are quite simple, generally
  product types that can be modeled via Flow's
  [object types](https://flow.org/en/docs/types/objects/). For developers on
  our team that were new to static typing, starting with simple types was more
  manageable than, for instance,
  [the type of the connect function](https://github.com/flow-typed/flow-typed/blob/master/definitions/npm/react-redux_v5.x.x/flow_v0.68.0-/react-redux_v5.x.x.js#L110)
  in `react-redux`.

In addition to all the usual benefits one gets from type safety, the team felt a
few that were particular to adding types as part of a 'refactoring' workflow:

* We started by modifying the application code as little as possible, i.e.
  adding types that reflected the _current_ codebase, rather than
  both modifying the code and adding types simultaneously. This made reviewing
  the pull requests much less scary, and allowed us to notice and document
  'weird' types as something to revisit.
* As we added types to the components, we uncovered possible states & code paths
  we were not taking care of. The team knew these existed, since we consciously
  ignored some states because they were unlikely or affected a small amount of
  the userbase. Adding the types revealed edge cases and possible sources of bugs
  that we were able to add to our backlog or squash as they popped up.
* Adding types revealed sub-par data modeling. There were too many places we were
  passing JavaScript objects around with no idea of what properties to expect on
  the objects.
* Adding types to code we'd written automatically gave us time
  to review the codebase and reevaluate some architectural decisions we'd made.
  This allowed us time to document pain points and open up discussion on how to
  improve the code.

## Pain Points & Downsides

We encountered several pain points throughout the process, though we felt they were
outweighed by the upsides:

* Third party type definitions. We used
  [flow-typed](https://github.com/flow-typed/flow-typed)
  for type annotations for some of our dependencies. Since these annotations
  are decoupled from the actual implementation of the libraries, there are
  occasional warts and inconsistencies that we had to deal with. For example, the
  team had to be diligent about ensuring the versions matched between our
  dependencies and the type definitions of those dependencies.
* Increased toolchain complexity. We had to configure our continuous integration
  environment to type check as part of its steps. We had to ensure everyone's 
  editors were configured to deal with flow type annotations properly. The team 
  consisted of emacs/vim users, but most of us temporarily switched to Visual 
  Studio Code for its seamless flow integration.

## Final Thoughts

The team considered this experiment a success, and put in a similar scenario we
would again advocate for types-after-the-fact as an effective tool for
refactoring, establishing long term maintainability, bug catching, and ensuring
correctness of application code.
