Created
February 29, 2024 13:16
-
-
Save aidenybai/1a5b3abbe475eaeb72d10a1c34368b78 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
title: Million Lint is in public beta | |
date: FEB 29, 2024 | |
description: We’d love to have your feedback | |
--- | |
import { Steps, Callout, Tabs, Tab } from 'nextra-theme-docs'; | |
import Image from 'next/image'; | |
<div className="flex flex-col items-center gap-4"> | |
<Image src="/lint/logo.png" alt="Million Lint" height={200} width={400} /> | |
# Million Lint is in public beta | |
<small>[AIDEN BAI](https://twitter.com/aidenybai), [NISARG PATEL](https://twitter.com/nisargptel), [JOHN YANG](https://twitter.com/fiveseveny) – FEB 29 2024</small> | |
</div> | |
--- | |
It’s launch time. After three months & hundreds of commits, we invite you to try out Million Lint. It’s not finished – there are a few known bugs and several missing features – but we're really happy with how it's shaping up and can't wait for you to try it. | |
Get started by running this in any React app: | |
```sh | |
npx @million/lint@latest | |
``` | |
<details> | |
<summary> | |
Installation failed? Click here to see the guide. | |
</summary> | |
<Steps> | |
### Install NPM package | |
<Tabs items={['npm', 'pnpm', 'yarn', 'bun']} storageKey="selected-pkg-manager"> | |
{/* prettier-ignore */} | |
<Tab> | |
```bash copy | |
npm install @million/lint@latest | |
``` | |
</Tab> | |
{/* prettier-ignore */} | |
<Tab> | |
```bash copy | |
pnpm install @million/lint@latest | |
``` | |
</Tab> | |
{/* prettier-ignore */} | |
<Tab> | |
```bash copy | |
yarn add @million/lint@latest | |
``` | |
</Tab> | |
{/* prettier-ignore */} | |
<Tab> | |
```bash copy | |
bun add @million/lint@latest | |
``` | |
</Tab> | |
</Tabs> | |
### Install the VSCode extension | |
Go to the [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=million.million-lint) and install the extension. | |
### Add the compiler to your application | |
<Tabs items={['Next.js', 'Astro', 'Gatsby', 'Vite','Remix', 'Create React App', 'Webpack', 'Rollup']} storageKey="selected-bundler-compiler"> | |
<Tab> | |
Million.js is supported within the `/app` ("use client" components only) and `/pages` | |
```js filename="next.config.mjs" | |
import MillionLint from "@million/lint"; | |
/** @type {import('next').NextConfig} */ | |
const nextConfig = { | |
reactStrictMode: true, | |
}; | |
export default MillionLint.next()(nextConfig); | |
``` | |
</Tab> | |
<Tab> | |
```js filename="astro.config.mjs" | |
import { defineConfig } from "astro/config"; | |
import MillionLint from "@million/lint"; | |
export default defineConfig({ | |
vite: { | |
plugins: [MillionLint.vite()], | |
}, | |
}); | |
``` | |
</Tab> | |
<Tab> | |
```js filename="gatsby-node.js" | |
const MillionLint = require("@million/lint"); | |
exports.onCreateWebpackConfig = ({ actions }) => { | |
actions.setWebpackConfig({ | |
plugins: [MillionLint.webpack()], | |
}); | |
}; | |
``` | |
</Tab> | |
<Tab> | |
```js filename="vite.config.js" | |
import MillionLint from "@million/lint"; | |
import react from "@vitejs/plugin-react"; | |
import { defineConfig } from "vite"; | |
export default defineConfig({ | |
plugins: [MillionLint.vite(), react()], | |
}); | |
``` | |
</Tab> | |
<Tab> | |
```js filename="vite.config.js" | |
import { unstable_vitePlugin as remix } from "@remix-run/dev"; | |
import MillionLint from "@million/lint"; | |
import { defineConfig } from "vite"; | |
export default defineConfig({ | |
plugins: [MillionLint.vite(), remix()], | |
}); | |
``` | |
</Tab> | |
<Tab> | |
<Callout type="warning"> | |
If you are using a [Create React App (CRA)](https://create-react-app.dev/) Setup, you will need to [configure Craco](https://craco.js.org/docs/getting-started/) before proceeding. | |
</Callout> | |
```js filename="craco.config.js" | |
const MillionLint = require("@million/lint"); | |
module.exports = { | |
plugins: [MillionLint.craco({ legacyHmr: true })], | |
}; | |
``` | |
</Tab> | |
<Tab> | |
```js filename="webpack.config.js" | |
const MillionLint = require("@million/lint"); | |
module.exports = { | |
plugins: [MillionLint.webpack()], | |
}; | |
``` | |
</Tab> | |
<Tab> | |
```js filename="rollup.config.js" | |
import MillionLint from "@million/lint"; | |
export default { | |
plugins: [MillionLint.rollup()], | |
}; | |
``` | |
</Tab> | |
</Tabs> | |
### Run your app! | |
</Steps> | |
</details> | |
Let us know if you have any [questions or feedback](https://million.dev/chat)! | |
## What is Million Lint? | |
Million Lint is a VSCode extension that helps make your React website fast. | |
Most developers try to use tools like React Devtools to find slow code. Unfortunately, most lack the knowledge to properly manage the complexity. | |
See the difference between React Devtools and Million Lint: | |
<Tabs items={['React Devtools', 'Million Lint']}> | |
<Tab> | |
_Feeling lost? Yeah, us too (+99% of all devs)_ | |
 | |
</Tab> | |
<Tab> | |
Get profile information within VSCode! | |
<video src="/lint/renders.mp4" controls autoPlay /> | |
</Tab> | |
</Tabs> | |
Complicated tools means developers give up. You insert `console.log` everywhere in a frenzied attempt to figure out the issue. You find some promising leads, but nothing works. Eventually, the slow code never gets fixed, problems pile up, and the end users are hurt. Everyone is left frustrated. | |
With websites getting [slower every year](https://www.debugbear.com/blog/is-the-web-getting-slower), we risk [leaving behind the majority](https://infrequently.org/series/performance-inequality/) of the world, exacerbating the divide between the rich and poor. I personally do not want “You must have the latest MacBook” to ever be the world we live in. | |
The goal of Million Lint is to commoditize web performance. | |
## You vs. the universe | |
Existing instrumentation tools (Sentry, Datadog, etc.) show you the “entire universe” of data. This includes every: function call, network requests, user interactions, core web vitals, screenshots, memory… a dizzying amount of information to process. | |
As such, these tools have interfaces that are overwhelming and make it difficult to find what you need. | |
The same applies with React & Chrome Devtools. They’re terrible if you have no idea what you’re doing (most of us). | |
We realized that this is suboptimal – even anti-developer in many cases. Unless you are an expert, the current state of performance tooling is not well designed for most developers. | |
Our hypothesis: Don’t filter down the “whole universe,” start with the data you need (”an atom”) and build up from there. | |
We do this by designing instrumentation specifically for React apps. | |
## Compiler or runtime? | |
JavaScript compilers enable you to perform static analysis on source code. One advantage is you can provide better authoring experience or runtime performance. | |
In the context of performance, static analysis can be very powerful – you get access to the entire source code. This is why ESLint is so great – you can create a wide variety of rules and processes to get what you want. | |
The tradeoff is it’s very difficult to predict how slow an application will be without running it, making it impossible to implement Million Lint solely as a compiler. | |
Another approach is processing this data at runtime. The advantage here is you can directly run the app and get rendering information (via [React Fiber](https://blog.logrocket.com/deep-dive-react-fiber/)). Many of the complexity challenges with static analysis are instantly solved. | |
The tradeoff with runtime is you don’t have the original source (unless you use source maps, which are painful and slow). Which hook or prop caused this change? Which part of the component re-rendered? | |
## In search of a better way | |
We went back to the drawing board. We wanted to create something that didn’t hog build time or runtime, and simultaneously helped developers figure out performance problems fast. Over-reliance on either methodology and eating the downsides felt backwards. | |
Our answer to these questions is dynamic analysis – using both static analysis and runtime analysis where it makes sense. | |
## Dynamic analysis | |
The main idea about dynamic analysis is you can share and balance processing cost across compile and runtime, while inheriting the advantages of both. | |
The role of the compiler is to inject handlers: | |
<Tabs items={['Compiled', 'Raw source']}> | |
<Tab> | |
```jsx /Million.capture/ | |
function App({ start }) { | |
Million.capture({ start }); // ✨ inject | |
const [count, setCount] = Million.capture(useState)(start); // ✨ inject | |
useEffect( | |
() => { | |
console.log('double: ', count * 2); | |
}, | |
Million.capture([count]), // ✨ inject | |
); | |
return ( | |
Million.capture( // ✨ inject | |
<Button onClick={() => setCount(count + 1)}>{count}</Button>, | |
) | |
); | |
} | |
``` | |
</Tab> | |
<Tab> | |
```jsx | |
function App({ start }) { | |
const [count, setCount] = useState(start); | |
useEffect(() => { | |
console.log('double: ', count * 2); | |
}, [count]); | |
return <Button onClick={() => setCount(count + 1)}>{count}</Button>; | |
} | |
``` | |
</Tab> | |
</Tabs> | |
During runtime, they capture render, timing, and metadata as you interact with your web app. You get runtime profiling from running the app, and have access to the source code thanks to the compiler! | |
```jsx | |
// psuedo code of collected data: | |
[ | |
{ | |
kind: 'state', | |
count: 1, | |
time: 0.1, | |
changes: [{ prev: 0, next: 1 }], | |
}, | |
// so on... | |
]; | |
``` | |
Additionally, the compiler can asynchronously collect bundle, network, and state manager information without build time overhead. | |
This allows us to show in-editor annotations, like: | |
<Tabs items={['Render information', 'Bundle sizes']}> | |
<Tab> | |
<video src="/lint/renders.mp4" controls autoPlay /> | |
</Tab> | |
<Tab> | |
<video src="/lint/bundles.mp4" controls autoPlay /> | |
</Tab> | |
</Tabs> | |
In addition, you can use **Lint++**, which feeds the collected information into a language model to discover optimization opportunities. So even if you don’t know what you’re doing – Million Lint will show you how! | |
[video of lint++] | |
## How good is it? | |
Measuring an "objective" improvement here is hard (it's supposed to help you find issues!). We haven't done any real benchmarking yet, but I played around with it on [an example app](https://github.com/3perf/react-workshop-ra). | |
Using Million Lint is very intuitive! This is me speeding up a notes editor app by 3x with Million Lint after ~20 or so minutes of work. | |
<video src="/lint/comparison.mp4" controls /> | |
The types of performance suggestions from **Lint++** I saw: | |
- Unstable reference as prop optimization | |
- Memoizing expensive components | |
- Caching inline functions | |
- Suggesting virtualization | |
- Suggesting `use-context-selector` > useContext | |
- Moving context providers up the tree | |
This is just a preview of the capabilities! We’re hoping to expand to more than just rendering – try and let us know what to add! | |
## How will we make money from this? | |
We don’t yet. We raised enough capital from an amazing set of investors that actually care about developer tools. | |
We will make money selling software (that makes your software faster) for more than they cost to make and maintain. | |
Million Lint follows a freemium model. In-editor annotations will remain forever free (+ open source soon!). Linting will start with a 1 month free trial, and then $20/month after. | |
If we do succeed, we are one step closer to solving the current state of web performance. Our final goal with Million is to enable web apps to self optimize without the need of humans to intervene. We want to empower developers to ship features and fix bugs faster, without needing to think about performance. | |
## The road to 1.0 | |
We have a lot of work to do. We’re starting by helping you fix rendering issues now (but we understand this is [not a problem](https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024) for long), and moving on to handling application code of the React ecosystem: such as state managers, animations, bundle sizes, waterfalls, etc. | |
Most importantly though, [we need your feedback](https://million.dev/chat) to help us make the best possible web performance developer tool. Try it out, and let us know which pieces are missing. | |
Once we’re ready, we’ll open source for the benefit of the community. | |
## How can I help? | |
Try Million Lint out today! [Let us know your feedback!](https://million.dev/chat) | |
We are looking for talented frontend (dev tools) and pl/ml engineers to join us in the Bay Area. | |
We need to build tools that make it easy to deliver fast applications, regardless of the framework, platform, or the size of your engineering team. | |
At Million, we have a simple thesis for software performance – we can build tools that make *existing* tooling fast and easy to use. Developers should think only of shipping features and fixing bugs – not keeping their apps fast. We plan to start with React, then extend to the roader frontend, backend, and other platforms. | |
If you feel like you missed the beginning of Million.js, get in on this. The fun of the beginning is how much you get to shape 🔜 [My email](mailto:[email protected]) | |
<br /> | |
<div className="flex flex-col items-center gap-4"> | |
<Image src="/lint/team.png" alt="Million team" height={300} width={400} /> | |
<small> | |
_The Million team + [Ivan Akulov](https://twitter.com/iamakulov) fixing React performance one step at a time. Join us!_ | |
</small> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment