One of the first languages we took a look at was Elm. Bogdan Zaharia, a member of our team, is a huge proponent of functional programming in its purest form. He was the one that introduced this language to our team. Elm was inspired by Haskell, and offers a robust, typed alternative to JavaScript. While attractive, we couldn’t make the transition to it easily. We could not find a simple way to gradually adopt Elm into our code base. Additionally, it’s not very newbie friendly. It requires developers to understand some non-trivial concepts from the functional programming world. We also had to consider how easy it would be to onboard new developers in our team to Elm. After weighing in all these drawbacks, we decided to keep looking.

Other options that we analysed:

Dart: this language is becoming popular again due to Google investing in Fuchsia. Dart is its de-facto UI tech stack. The lack of a transparent roadmap and uncertain future meant a very clear no go.

PureScript: this is Haskell for front-end development. Same arguments as using Elm. To be honest, it has an even steeper learning curve than Elm.

We had two candidates left on our list: Flow and TypeScript.

It was not easy to choose between these two given their similarities. We invested some time in research spikes to see first hand what development looked like in both cases. What we found was that Flow was more difficult to set up than TypeScript, and that the developer experience felt sluggish. This has improved though in the last year, so if we were to analyse them again today, this wouldn’t have made such a big difference.

Both type systems are solid, yet very different. TypeScript uses a structural type system. Flow’s system is both structural and nominal. The difference is key to how they integrate with the greater JavaScript ecosystem. I will summarise what that means, but if you’re interested, this article goes into much more depth. A nominal type system implies that two types, Foo and Bar, are never equal, regardless of their definitions. TypeScript, using only the structural alternative, doesn’t care about what the types are called. It analyses their structure and determines whether it should act like they are equal. In practice this means writing code is less demanding on you. You can easily reuse types and interfaces and you can even define them inline without naming them.

We also looked at the communities around these two projects. Flow is a language that, at least a year ago when we ran this analysis, was being driven by Facebook in a very closed manner. Development was never transparent, there was no public roadmap, and very few people outside of Facebook were contributing to the project. TypeScript, in contrast, embraced open-source development since moving to GitHub a few years ago. They keep an up-to-date roadmap, accept outside contributions, and generally keep a very close relationship to the community. Anders Hejlsberg, a Microsoft fellow and chief architect, speaks at conferences (such as TSConf) throughout the year, keeping everyone in the loop on what features are coming next and how the project is performing.

So as you might have guessed, we chose to go with TypeScript. This was a pretty long intro on why we did it, but it serves as a good template for other teams when deciding to change tech stacks. I’ve seen too many teams do tech switches by starting with the “how”, rather than the “why”. Rather than stating that your team wants to switch over to another language (or stack), start by understanding your problems.

What are our current difficulties? How do we prioritise them? Are they solvable within the current stack?

You should always go through these steps so that you may reach the best possible solution.

Continue with “how?”

At this point we had reached a consensus that we wanted to begin rewriting our code base in TypeScript. The first step we took was to ensure that we could actually write TypeScript code.

We built Hootsuite Analytics on top of modern software practices. We have a continuous integration pipeline that helps us deliver code to production only after changesets pass several checks:

Linting

Formatting (using Prettier)

Unit-tests

Code coverage

Acceptance tests

We have three different environments: development, staging, and, of course, production. When thinking about adding TypeScript, we had to make sure it didn’t interfere with the pipeline. And we also had to make it seamless for developers.

I won’t go too much into the specific technical details of how we integrated TypeScript into our code base. There are far too many excellent articles and tutorials out there that describe the process better than I can, and I suggest you check them out:

I will, however, describe our philosophy, our pain points, and what we’ve learned along the way.