Converting Codecademy to TypeScript 1: Converting Ourselves

Josh Goldberg
Codecademy Engineering
8 min readMar 30, 2020
Codecademy logo, a heart emoji, and the TypeScript logo

TypeScript! TypeScript! TypeScript!

Much has been said about TypeScript, the fancy open source superset of JavaScript first released by Microsoft in 2012 and now one of the most prolific and fastest growing languages on GitHub. If you’re not convinced that TypeScript catches bugs, improves code clarity, and speeds up development, then, well, this post might not convince you. But if you’re interested in how a relatively unfamiliar engineering team took an existing codebase (and itself) from nothing to TypeScript glory, this is for you!

If you’re uninterested in organization setup and just want the technical juice, skip to Part Two: Technical Changes and Part Three: Learnings and Next Steps.

Preplanning

Before embarking on a potential change, it’s important to understand the benefits and drawbacks so you can justify the shift to yourself and your peers. Is the change something the team would learn to appreciate? Out of all the alternatives, why that one? Are its benefits worth the risk and time?

No organizational change comes without its own hurdles and difficulties. Overhauls such as switching languages or base frameworks that require touching many files come with multiple costs: implementation time, training overhead, and resultant bugs.

That’s right: virtually every platform-level change will cause bugs. Any time a developer changes a line of code, there’s a small chance of a bug being created. Touching every file means running that risk at a massive scale. We caught many more existing bugs during the TypeScript conversion than we caused, but it still caused a few. Your plans and timelines will need to accommodate for fixing the bugs you cause.

Champions

Freddie Mercury performing with an adoring audience
How it feels to evangelize the latest new web trend. [source]

Organizational efforts are much more likely to succeed if at least one good “area expert” and a few additional “cheerleader” team members are excited about starting it. They champion the technology as the team adopts it.

Area Experts

An area expert is someone who is very familiar with the technology, is able to evangelize it and its best practices, and can save the team from the common pitfalls of onboarding it. All of those behaviors are crucial to ensuring project success. Adopting a new technology without already having an area expert on board is both an increased time expenditure and a great way to sour developers’ opinions of a technology.

I acted as area expert for us because I was already very familiar with TypeScript — I contribute to the language itself; I helped maintain the partially-deprecated TSLint; and I later gave a talk at TSConf 2019. That’s probably more than you really need. Anybody with experience using TypeScript and a good understanding of TypeScript’s build system integrations, compiler usage, and type checking quirks would likely be sufficient.

Cheerleaders

Cheerleaders, or team members with less experience with the new technology but still-high enthusiasm, are also key to pushing the technology through the team. You need grassroots support for any technology to have it really catch on; more mouths talking about it gets you more visibility. It’s also great to see how the technology is received by various people — a newcomer to the area likely has a very different reaction, problem set, and motivations than someone with years of experience.

We were fortunate to have a variety of cheerleader archetypes: one grizzled veteran who set up much of our frontend infrastructure, two broadly knowledgeable senior engineers, and one excited intern-then-junior-developer. None of them explicitly volunteered to evangelize TypeScript… but they were all excited about it from the start and thus receive the label. 📣

Robot Chicken action figure of Ronald McDonald the clown riding in circles on a track, with the words “HYPE TRAIN” flashing
Get your team hyped, and they’ll be excited to make the leap! [source]

RFC: Migrating to TypeScript

As a language, TypeScript is used to provide better developer utilities, static error-checking, and automated code modifications to JavaScript projects. Integrations with common build libraries, including Babel and Webpack, are available, and its compiler options can be configured for a range of loose to strict adherence to typed patterns.

Before we decided to take the leap, I (the area expert) created an initial “RFC” (Request For Comments) briefly describing what the new technology is, why we want it, and how we would switch. We had an already-established practice as a team for sending RFCs before making major changes to ensure everybody had a chance to provide input — or at the very least not be surprised when pull requests got merged.

Our particular RFC proposed, in order:

  1. The area expert would give a brown bag on TypeScript to increase team awareness
  2. We would try out converting our Gamut design system to TypeScript as an initial test
  3. If all went well, we would add support for TypeScript to our build system in our main codebase, then split up file conversions

The initial RFC was very different from the plan of attack we chose for our main code base. It contained optimistic time estimates for conversion, usage of --checkJs to allow type checking of JavaScript files, a recommendation to prefer // @ts-ignore over truly fixing difficult errors., and a different set of initial compiler flags.

In retrospect, estimating 12 hours to convert 2,000 files to TypeScript was not a particularly well-informed decision. The actual time was probably three or four times the original prediction. A change like this will likely take you longer than you expected and diverge from your original plans, so make sure to keep that in consideration!

Still, the Gamut conversion was successful and received favorably by the few engineers who worked in it. We received particularly high marks for standardizing the type declarations for components shared across our projects. Even engineers working in plain JavaScript files benefited thanks to VS Code’s JavaScript IntelliSense. All aboard for TypeScript!

Knowledge Sharing

Uncle Sam propaganda poster with the caption “I want you to learn TypeScript”

We started our internal TypeScript propaganda machine with two lunch presentations:

  1. Static Analysis in JavaScript: a general introduction to tools that analyze code without running it, mostly focused on Prettier and ESLint
  2. ✨TypeScript!✨: a full on introduction to TypeScript, starting from the basics of a type system and ending with discriminated unions

My favorite slide from those presentations was in the very beginning, where we pointed out the “Bug Acquisition Funnel” as an idea of how bugs are removed through the development process. Preventing bugs and improving website stability was one of the big team ambitions at the beginning of 2019, and we maneuvered TypeScript into those plans nicely.

“Bug Acquisition Funnel” showing the flow of process development as reducing bugs, with “Static Analysis” circled.

Lesson learned: if your team recognizes a point of improvement, maneuver your goals to seem to match with it. You’ll be amazed how receptive leadership will be! 😉

We also introduced a regular series of TypeScript topics to our biweekly Lightning Talks series wherein team members give 5 minute mini-presentations on whatever subjects they’d like. Those presentations covered more esoteric topics such as generics and mapped conditional types.

Game Plan: Early Stages

Easing a team into a new language slowly can be very useful. Unless you want to spend multiple business days with little-to-no-productivity (who has the time!?), it’s likely that most of the team won’t launch into the new tech appropriately prepared with its best practices and common gotchas.

We started off the process with a few high-level behavioral bullets:

  • Developers were encouraged to write new files in TypeScript once they were comfortable with it
  • Team experts and cheerleaders would dedicate time to converting existing files to TypeScript
  • TypeScript resources in the form of documentation, internal presentations, and external resources were emphasized as suggested attendance & reading

Early Adopters

Some team members jumped on with gusto and actively tried it out. Spotting pull requests come in with voluntary TypeScript additions and conversions was a wonderfully validating moment as a project champion. Seeing the issues faced by these early adopters, almost all of whom had never seriously used TypeScript before, was also quite useful. We used their struggles to inform the documentation and resources we sent out to the team, as well as some of the lighting talks.

Most of the early adopter struggles were around type declarations, particularly around third party libraries and how to we would use them to declare React component prop types. React types were a great problem to have: once a usage pattern was asked about and subsequently clarified in our internal documentation, virtually nobody asked the same question again.

Incorrectly written third party types were more painful. The best mitigation technique we came up with was to do all the .d.ts declarations on our own time and send the pull requests to affected team members for visibility. In a few cases, we ended up not adding the @types/ package at all, and lived with the implicit anys.

Holdouts

Some team members chose not to involve themselves in the conversion from the start. Totally reasonable! Participating in the early stages of a technological adoption without prior experience is a tough ask, and everybody has different motivations and goals.

Those team members were also valuable to extract feedback from. Why weren’t they jubilant about this shift the way we were? Was it a passive reluctance? …active disagreement? …both? …neither? Understanding the reasoning behind others’ lack of excitement helped inform both the perception and technological efforts we made.

  • For team members concerned about it slowing down development time, we showed how VS Code’s improved refactorings and navigation sped up development.
  • For team members who didn’t see static typing as useful in catching bugs, we publicized every production bug or unused piece of code it found.
  • For team members nervous about the cognitive overhead of a different language, we evangelized coding principles that espoused minimal TypeScript-only features, and showcased how our React components were easier to understand and more precise with TypeScript types instead of prop-types.

Game Plan: Late Stages

Once we hit over 50% conversion, we switched to the assumption that a majority of team members had at least moderate experience for the switch. That led us to an altered set of behavioral bullets:

  • Developers were encouraged to write new files in TypeScript once they were comfortable with it
  • Team experts and cheerleaders would dedicate time to converting existing files to TypeScript Remaining work for converted the codebase was split into granular items and assigned to various team members
  • TypeScript resources in the form of documentation, internal presentations, and external sites were emphasized as suggested attendance & reading expected to be understood

Spreading Knowledge

Splitting up remaining work in a conversion is great for spreading knowledge among team members. We needed everyone working in our frontend code to understand TypeScript and our usage of it, and the only reliable way to get someone to understand was to have them use it. One could say that we were converting ourselves to TypeScript. One could visualize progress by the percentage of team members who’d created or converted multiple TypeScript files. By the time we’d reached 100% file coverage, we’d long reached 100% team coverage.

--

--