Migrating a 150K LOC codebase to Vite and ESBuild: Why? (part 1/3)
Tags: vitejs, esbuild, react, webpack
Published: 2021-05-26 06:30:35

What is Vite? Why did we consider it? Is it faster than Webpack? Is it mature enough?

---

This is part of a three-article series about migrating our React+TypeScript codebase from Webpack to Vite. [Part 2](https://dev.to/noriste/migrating-a-150k-loc-codebase-to-vite-and-esbuild-how-part-2-3-1c08) is about how we did it, [Part 3](https://dev.to/noriste/migrating-a-150k-loc-codebase-to-vite-and-esbuild-is-it-worthwhile-part-3-3-339k) is about post-mortem considerations.

## From Webpack’s slowness to Vite

It’s always the same story: you start working on a branch, run yarn start **and wait for two minutes**. Then you save a file and wait for 20 or 30 seconds before the app gets refreshed. Then you switch multiple times among the branches you’re working on, and you need to relaunch the dev server again. Such a terrible Developer Experience.

We reduced this problem by moving to **[Vite](https://vitejs.dev/)** and [ESBuild](https://esbuild.github.io/). Keep on reading.

The [WorkWave RouteManager](https://www.workwave.com/route-manager/) UI team works daily on a 150K LOC (and growing, ~250K LOC is the final size) codebase. Due to the nature of the Product and the technical constraints, there is nothing we can do to split the app into little chunks at the moment. 75% of the code is shared and used by all the app sections.

The app is based on **React and TypeScript**, spans a **Web Worker**, uses Webpack, babel-loader, ts-loader, and [fork-ts-checker](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin) to bundle and validate the code. All the dependencies are updated weekly.

Recently, [Vite 2.0](https://dev.to/yyx990803/announcing-vite-2-0-2f0a) has been released. Its core idea is simple: serving the files “as are” to the browser leveraging [ESModules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). When the browser parses the source files and asks the webserver for an imported file, Vite does

* convert NPM dependencies to ESModules and serve them

* transpile your source code through [ESBuild](https://github.com/evanw/esbuild)

* serve the code to the browser

What does it mean? It means that **Vite doesn’t bundle the code in advance**. Instead, when the browser asks for a file, Vite transforms it. A picture is worth more than a thousand words.

![The browser performs thousands of requests for the source files (.tsx, .ts), not for the bundled one as happens with Webpack.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1k2hxsqlp9jgvx7lkdcm.jpg)
*The browser loading the thousands of unbundled .ts and .tsx files.*


The files are cached, but **Vite essentially removes the burden from the build tool, moving it to the browser**. You can read more on [Vite docs](https://vitejs.dev/guide/why.html) or on CSS-Tricks’ [Comparing the New Generation of Build Tools](https://css-tricks.com/comparing-the-new-generation-of-build-tools/) article.

What about the build? Vite leverages Rollup to build the project, no worrying about browser compatibility of the final bundle.

## Vite’s advantages

In contrast to Webpack, Vite is not a generic tool. **Vite supports a few sweet spots**. If your project falls in these spots, Vite offers

* Better performances, thanks to the speed of ESBuild.

* Less configuration.

* Fewer dependencies: everything required for a React+TypeScript project like ours is managed directly by Vite, **React Fast Refresh included**.

But not all that glitters is gold! What **does Vite not include**?

* TypeScript validation: to be short, ESBuild removes type annotations without validating them. You must run tsc on your own to validate types.

* It depends on your configuration, but in our case, ESLint runs thanks to fork-ts-checker. Vite doesn’t care about ESLint.

*Notice: I’m going to talk about these missings in [the 3rd part of this series](https://dev.to/noriste/migrating-a-150k-loc-codebase-to-vite-and-esbuild-is-it-worthwhile-part-3-3-339k), including some objective considerations about Vite and Webpack and the overall Developer Experience.*

Last but not least: **are Vite and its ecosystem mature** enough?

* The ecosystem likes it: for example, [Storybook will support it](https://storybook.js.org/blog/storybook-6-2/), [Cypress has first-class support for Vite](https://www.cypress.io/blog/2021/04/06/introducing-the-cypress-component-test-runner/).

* Vite’s creator, Evan You, is the same as Vue.

* We had just one problem with one dependency with Vite, and [Evan fixed it in less than two days](https://github.com/vitejs/vite/issues/2219).

In the end: betting on Vite is not hazardous. Anyway, we decided to keep the codebase compatible with Webpack to swap between Vite and Webpack in case of troubles.

## Benchmarks

To give you an idea of the performance improvements, take a look at our early benchmarks, the ones I shared with the rest of the team

|     Tool | yarn start | app loads in | React component hot reload \*\* | web-worker change "hot" reload \*\* |
| -------: | ---------- | ------------ | ----------------------- | ----------------------------------- |
| Webpack* | 150s       | 6s           | 13s                     | 17s                                 |
|    Vite* | 6s         | 10s          | 1s                      | 13s                                 |

*\* Early benchmark where Webpack runs both ESLint and TypeScript while Vite doesn't*
*\*\* Means from CTRL+S on a file to when the app is ready*


Then, getting the codebase ESBuild-ready allowed us to put Webpack on a diet too, these are our final benchmarks.

|                                                  Tool | 1st yarn start, app loads in | 2nd yarn start, app loads in | browser reload (with cache), app loads in | React component hot reload \*\* | server-data change "hot" reload \*\* |
| ----------------------------------------------------: | ---------------------------- | ---------------------------- | ----------------------------------------- | ------------------------------- | ------------------------------------ |
| Webpack<br />(babel-loader + ts-loader + fork-ts-checker) | 185s                         | 182s                         | 7s                                        | 10s                             | 18s                                  |
|                              Webpack<br />(esbuild-loader) | 56s                         | 52s                         | 7s                                        | 10s                             | 16s                                  |
|                                                  Vite | 48s                          | 31s \*                       | 11s                                       | 1s                              | 14s                                  |

*\* Vite has an internal cache that speeds up initial loading*
*\*\* Means from CTRL+S on a file to when the app is ready*

## Migrating the codebase

[The 2nd part](https://dev.to/noriste/migrating-a-150k-loc-codebase-to-vite-and-esbuild-how-part-2-3-1c08) of this series deepens the technicalities under the migration of our codebase. [The 3rd part](https://dev.to/noriste/migrating-a-150k-loc-codebase-to-vite-and-esbuild-is-it-worthwhile-part-3-3-339k) is about the conclusions, the DX, and a more fair comparison with Webpack.