How I stopped extension rot with a per‑project VS Code extension whitelist

I got tired of juniors' extensions breaking demos and slow installs on office Wi‑Fi. I built a tiny per‑repo extension whitelist + setup script that saved onboarding time — and introduced one annoying failure.

Written by: Arjun Malhotra

Laptop on a desk displaying code in a dark editor
Photo by Brooke Cagle on Unsplash

It was 11:13 on a slow Bengaluru Friday and a new hire was frantically pinging me.

“My Prettier keeps reformatting the test file and CI is failing. I installed some extensions the recruiter suggested, then pulled and everything changed.”

I looked at the Git diff. Indentation changed across 40 files. The culprit: three different formatter extensions, two conflicting editorconfig plugins, and a theme that somehow toggled the editor’s auto‑save behavior. The junior had a cheap 4G dongle and was on the office Wi‑Fi that drops every few minutes. They spent 40 minutes waiting for extensions to download. I spent 40 minutes reverting accidental reformatting. The rest of the team spent another hour debugging “works on my machine” failures.

We had a problem I’d seen in other shops: extension rot. Machines accumulate extensions—some useful, some dangerous, some heavy—and nobody agrees which ones to use for a repo. On slow connections (and yes, most Indian offices and home setups) that friction becomes drag: onboarding takes longer, demos die in meetings, and CI becomes a formatting battleground.

I wanted a small policy that fixes the common cases without being religious about editors.

Why the usual fixes failed

We already had the basic tooling:

I tried heavier enforcement: a pre-commit hook that auto‑installed missing extensions. That worked once (it installed a formatter mid‑commit and rewrote code). Then it failed spectacularly for someone on a 2GB dongle. The hook blocked commits until 400MB of extensions finished. Not acceptable.

The tiny per‑project policy I actually use

I wanted three things from a solution:

So I built a tiny repo script and one gentle git hook. It’s 60 lines of shell and a JSON file. The pieces:

  1. extensions.json (committed)
  1. scripts/setup-editor
  1. scripts/check-extensions (git hook friendly)
  1. Onboarding line in README

Why this works in practice

Predictability comes from a single source of truth in the repo. We enforce only the hard parts: which formatter actually runs during CI, and which extensions are forbidden because they rewrite files without explicit action.

Lightweight onboarding is the key win for India‑style constraints. The script only installs what we deem necessary. The cache and —offline mode saved two new hires from spending their 4G data budgets on a theme pack they didn’t need.

Also: nobody rewrites your code mid‑commit. The setup step is manual. The check-extensions hook doesn’t auto-install. It warns, points to the one-line setup, and moves on.

The failure I learned from

We screwed up once. A developer in Ahmedabad cloned, ran setup, and then forgot to restart VS Code. The installed formatter was present but not active until reload. They edited and committed. CI tripped on style. It was my fault for assuming restart behavior.

The other failure: the forbidden list. Early on I added a popular formatter to “forbidden” because it silently replaced tabs, and one developer legitimately needed it for a side project bundle. The script flagged their environment and blocked a hotfix during a production incident (we had the hook set to block within our internal tooling). We relaxed the hook to warn rather than block for non‑critical repos. The lesson: enforcement should never be the path of least resistance during incidents.

The tradeoffs I accepted

What I actually walked away with

A one-line habit that saves hours: put the canonical extension list in the repo, provide a single setup script, and enforce only the things that rewrite files silently. Make installs optional and cacheable. If someone insists on using a different tool for their side project, fine—just not in our repo’s context.

If you maintain a repo with other humans, especially across slow connections or shared desks, this is the tiny prevention that pays. It won’t fix your culture, but it will stop half the weird demos and one‑third of the CI formatting errors.