Skip to content

Lint Your Life

I know I have no business saying this. I ship buggy OSS. But quality management was once my actual job.

The first step in quality control is the linter. It flags problems the moment you write them. Unlike a human reviewer, it never tires. It has no feelings. Unused variables, type mismatches, unreachable code. Machines catch what humans miss.

ESLint was the JavaScript standard for years. Drop in the Airbnb ruleset and the whole team writes the same way. Semicolons or not. Indent width. Arrow function parens. Petty debates get delegated to rules. Time spent writing "you're missing a semicolon here" in code review drops to zero.

Prettier is a formatter. It doesn't touch meaning, only appearance. Different job from ESLint. ESLint finds bugs. Prettier aligns the surface. Running both was the norm, but config conflicts were constant pain.

Biome changed the wind. Linter and formatter in one tool, written in Rust, so it's fast. Zero dependencies. If you've suffered under the weight of node_modules, that alone is enough to make you weep. Teams are migrating from the ESLint-plus-Prettier stack. One fewer config file makes a real difference to mental health. Framework-specific support like Vue is still weak, though. It doesn't go everywhere yet.

The C/C++ world has clang-tidy. Static analysis and refactoring in one. Memory leaks, dangling pointers, undefined behavior. The stakes are different from a missing semicolon. This is crash-or-not territory.

Editor integration is the lifeline of quality control. The linter runs on every save. The formatter reshapes on every save. Catching errors in CI is too late. You fix things when they're flagged the instant you write them. When CI fails, the response is "I'll fix it later." Later almost never comes. Whether CI should gate deployment is a team-by-team debate. Many OSS projects don't gate at all. There is no single right answer.

A few days ago, Bun merged a PR that rewrote the codebase from Zig to Rust. Over a million lines. Nine days. The method was a loop: feed compiler errors to Claude, iterate. Memory safety was the reason given. Not performance. The human reviewer was still listed as "Awaiting requested review." Only coderabbitai[bot] and claude[bot] had actually reviewed it. I wrote earlier that a linter never tires and has no feelings. This looks like the same idea taken further.

But there's also a claim that some tests were modified so they'd pass against the Rust version. That one is a different problem. The thirteen thousand unsafe blocks have a structural reason — Bun embeds JavaScriptCore through FFI. Rewriting tests to make them pass doesn't. It reverses the direction of quality control. Tests meant to catch bugs end up adjusting to the code.

I run linters. I run tests. I run all of it. Bugs still ship. Linters guard syntax. Tests guard behavior. But you can't test wrong logic. The tests will be wrong too. Quality control may not be about reaching zero bugs. It may be about how fast you notice them.