
Duncan Walter
Duncan is a senior software program engineer on HubSpot’s Frontend Platform staff. He works to empower fellow engineers with ergonomic tooling and highly effective automation. At HubSpot, he focuses on introducing TypeScript in addition to enhancing editor, code mod, code gen and lint tooling.
TypeScript is a superb addition to the JavaScript toolbox. Static varieties mitigate friction as tasks scale, and JavaScript tasks are scaling quick. Now greater than ever, customers anticipate subtle and pleasant browser experiences that push the boundaries of accessible know-how. Since 2018, HubSpot’s infrastructure groups have used typed JavaScript to remain forward of evolving buyer expectations, tech debt and the competitors at massive.
There’s a catch, sadly. Whereas infrastructure groups like ours profit from TypeScript, our product engineers have been left behind. Suffice it to say, TypeScript is essentially incompatible with the evergreen, “batteries included” tooling our product engineers use and revel in to construct frontend merchandise.
We now have excessive expectations for the infrastructure we offer. Our position is to empower engineers within the happiest, best staff doable. Certainly one of our obligations is facilitating entry to fashionable instruments like TypeScript. So, we’ve been arduous at work pulling down boundaries so our engineers can benefit from the glorious TypeScript infrastructure they deserve. This has been an thrilling yr for our staff as a result of our work on TypeScript is lastly paying off. HubSpot’s product group is changing to TypeScript this yr.
This migration is an ongoing effort with many aspects; constructing code in TypeScript is just step one. From an organizational perspective, we’re ramping up our funding in inside TypeScript schooling and help. On the technical aspect, we’ve additionally shipped new tooling for migrating to, enhancing, and sustaining TypeScript code. As we speak, we’d prefer to share among the issues and options we’ve been engaged on to take TypeScript the place it may by no means go earlier than.
Migrating with Compassion

George Kemp
George is a Senior Software program Engineer on HubSpot’s Infrastructure Engineering staff. At HubSpot, he’s enthusiastic about constructing an excellent developer expertise and a stable basis of tooling. George is enthusiastic about all issues JavaScript, however is at the moment specializing in serving to carry TypeScript to HubSpot’s frontend. When not on the keyboard, he’s an avid aviation fanatic and newly minted non-public pilot. George holds a BS in Mechanical Engineering from the College of Colorado Denver.
This yr, our staff’s major focus has been guaranteeing that our TypeScript migration isn’t disruptive to product engineers. Switching to TypeScript is an impactful change, even beneath ideally suited circumstances, and circumstances are sometimes not ideally suited. For example, a lot of our engineers have little to no expertise with TypeScript (or any statically typed language). Sustaining staff autonomy is one other level of concern. Our frontend product groups are dynamic. Some groups could also be targeted on efficiency and tooling whereas others are actively creating new options. We perceive that TypeScript migrations gained’t be a direct precedence for each staff. With that in thoughts, our strategy has been to supply versatile, self-service migration sources. With the best instruments in hand, groups can create a TypeScript migration technique appropriate with their mission and accessible sources.
Schooling and help are key given our distributed migration technique. Working with unfamiliar or poorly documented tooling all day is unsustainably exhausting. It’s crucial that engineers have free entry to TypeScript studying supplies and management the tempo of their very own TypeScript adoption. For instance, we now present engineers free entry to TypeScript-focused, subscription-based on-line studying instruments along with the usual studying advantages all HubSpotters take pleasure in. Even with entry to those sources, studying takes time. Anticipate and instruct engineers to dedicate work hours to TypeScript studying.
Do not forget that engineers are sometimes as enthusiastic about TypeScript as we’re. We can not overstate how impactful enthusiasm is throughout a migration. Infrastructure groups’ position sometimes isn’t advocating for TypeScript. As an alternative, concentrate on eradicating boundaries to entry. Each little bit helps. Automate busy work like renaming information and including varieties for props utilizing ts-migrate
or an equal. Configure TypeScript and migrate a single, small file to TypeScript in every undertaking so groups don’t want to fret about configuration steps themselves. Present instruments to determine which modules are ripe for migration or would be the most impactful when migrated. As soon as.
That mentioned, it’s necessary to hunt out and keep a wholesome dialogue with TypeScript skeptics as nicely. Either side of the dialog have a lot to realize. Skeptics can usually pinpoint weak spots in a migration technique early, which gives priceless further time for triage and mitigation. Switching seats, it’s important that engineers know they’re heard and have a say of their tooling. Even when they’ve been outvoted in a basic sense, skeptics deserve a compassionate migration course of that accommodates their present workflows and preferences the place doable. In our expertise, skeptics may even change into advocates when their considerations are taken severely.
Behind the Scenes
On the floor, HubSpotters migrating to TypeScript will see the identical habits they’d anticipate anyplace else. TypeScript powers editor options, runs throughout every construct and is erased at compile time. However beneath the hood, our TypeScript infrastructure is exclusive. Now that we’ve seen HubSpot’s TypeScript migration from a human perspective, I’d like to drag the curtain again to disclose how we achieved this migration at a technical degree.
First, some context. HubSpot has opinionated, built-in tooling that abstracts away frequent engineering wants like native growth servers, linter configuration, transpilation, bundling, check automation and deployment. Our tooling is persistently a high cause that engineers love engaged on our product. Opinionated tooling does include some upkeep prices, however the worth we get in change is irreplaceable.
For the needs of TypeScript help, the important thing wrinkle in our tooling is that we don’t retailer dependencies in node_modules
. That’s a giant deal as a result of TypeScript solely is aware of the right way to discover dependencies in the event that they’re positioned in node_modules
. Additionally, not like most different instruments within the JavaScript ecosystem, TypeScript deliberately doesn’t help configuring alternate module decision methods. Luckily, when there’s a will, there’s a manner. We determined to fork TypeScript. Whereas not a undertaking to sort out frivolously, we’ve discovered it takes a surprisingly small set of modifications to help customized decision methods in TypeScript.
As beforehand talked about, most JavaScript instruments help customized decision methods, so there’s an abundance of prior artwork to work with. Virtually all of those instruments enable customers to supply a resolve
perform. Sometimes resolve
takes a file path and an import specifier, after which returns the trail to the requested dependency. That is the center of any module decision technique, and TypeScript is not any totally different beneath the hood. Step one in all patching TypeScript is making resolve
configurable. resolve
can be the one a part of our TypeScript patch which impacts sort checking, which suggests it’s the one a part of the patch which wants to stay secure over time.
// resolve(‘…/module.ts’, ‘react’) -> ‘…/react/index.ts’ perform resolve(fromPath: string, identifier: string): string { // return the trail of the resolved module specifier return ‘…’; } |
TypeScript wants a few different capabilities to help key editor options like auto imports. First, the other of the resolve perform: getModuleIdentifier. In the identical manner that resolve
tells TypeScript the place to discover a dependency given an import assertion, getModuleIdentifier tells TypeScript the right way to import a dependency given its location. It takes the trail of two modules and returns an import specifier. Particularly, it returns the import specifier wanted to import the second module from the primary. Final, TypeScript wants a getAutoImportableModules perform which accepts the trail to a undertaking’s root and returns a group of module path-specifier pairs.
// getModuleIdentifier(‘…/module.ts’, ‘…/react/index.ts’) -> ‘react’ perform getModuleIdentifier(fromPath: string, toPath: string): string { // return the identifier for referring to toPath from fromPath return ‘…’; }
sort ImportableModule = { path: string; identifier?: string; };
perform getImportableModules(projectPath: string): ImportableModule[] { return […]; } |
These three capabilities are drop-in replacements for present logic inside TypeScript. As soon as they’re made configurable, exchange TypeScript’s default logic with the configured model to stand up and operating with a patched model of TypeScript. This patch is mild and customarily secure throughout TypeScript variations, so it may be rebased onto new launch branches of TypeScript as they change into accessible.
Wanting into the Future
We see many alternatives to reinforce our present instruments now that TypeScript is in our toolbox. We will bridge the divide between frontend and backend tasks with evergreen-generated API varieties. From there, we’re enthusiastic about constructing new editor options on high of TypeScript’s present help, analyzing the kind well being of packages and including sort data to our code mod stack. TypeScript has a vibrant future at HubSpot.
Supply hyperlink