I used to have 37 VS Code extensions installed. I uninstalled 32 of them. These 5 do the work of all 32 combined — and my editor is faster than it has been in years.

The extension bloat problem is real. Every developer I talk to has the same story: you install an extension for one feature, it works for a week, you forget about it, and two years later your editor takes 8 seconds to start up and you have no idea why. At CODERCOPS, we did an audit of our team's VS Code setups and found that the average developer had 28 extensions installed but actively used only 6.

So we ran an experiment. Every developer on the team stripped down to zero extensions and added them back one at a time, only when they genuinely missed something. After two weeks, we converged on the same 5 extensions. These are the survivors. Each one replaced at least one external tool, and together they cover 95% of what we need outside of basic code editing.

VS Code Setup A clean VS Code setup with only the extensions that earn their keep

1. Error Lens — See Problems Before You Hover

Extension ID: usernamehw.errorlens What it replaced: Constant hovering over red squigglies, missing non-obvious warnings Install size: ~200 KB (negligible)

This extension sounds underwhelming when you describe it: "It shows error and warning messages inline, right next to the line that caused them." That does not sound like a game-changer. Then you install it and realize you have been wasting 20 minutes a day hovering over squiggly lines like a detective searching for clues.

Why It Matters More Than You Think

Without Error Lens, VS Code shows you a red or yellow underline. To see the actual error message, you hover over it. On a file with 5 errors, that is 5 hovers, 5 pauses to read a tooltip, and 5 moments where you break your flow. Multiply that across a day of development.

With Error Lens, every error, warning, info message, and hint appears as colored text at the end of the offending line. You see the problem the instant you type it. No hovering. No guessing. It turns error discovery from a reactive process (notice underline, hover, read) to a passive one (just look at the screen).

The Settings That Make It Great

Most people install Error Lens with defaults and call it a day. The defaults are fine, but with a few tweaks, it becomes significantly more useful:

{
  "errorLens.enabledDiagnosticLevels": ["error", "warning"],
  "errorLens.excludeBySource": ["cSpell"],
  "errorLens.messageMaxChars": 120,
  "errorLens.fontStyleItalic": true,
  "errorLens.gutterIconsEnabled": true,
  "errorLens.gutterIconSet": "defaultOutline",
  "errorLens.messageBackgroundMode": "message",
  "errorLens.statusBarMessageEnabled": true,
  "errorLens.delay": 500
}

Key settings explained:

  • enabledDiagnosticLevels: Set to ["error", "warning"] to skip info and hint messages. Without this, your screen gets noisy fast — especially in TypeScript files where every unused import shows a hint.
  • excludeBySource: If you use a spell checker like cSpell, exclude it here. Otherwise every "misspelled" variable name gets an inline annotation.
  • messageMaxChars: Cap at 120 characters. Some TypeScript errors produce novel-length messages that wrap across your entire screen.
  • delay: Set to 500ms. Without a delay, the error messages flicker as you type, which is distracting. The 500ms delay means messages only appear once you pause typing.

The Non-Obvious Trick

Custom color coding by severity. Add this to your settings.json to make errors jump out while keeping warnings subtle:

{
  "errorLens.errorForeground": "#ff6b6b",
  "errorLens.errorBackground": "#ff6b6b15",
  "errorLens.warningForeground": "#f5a623",
  "errorLens.warningBackground": "#f5a62310"
}

The low-opacity backgrounds (15 and 10 in hex) create a subtle highlight without making your code look like a Christmas tree. Errors are red and immediately visible. Warnings are orange and noticeable but not screaming at you.

Before vs After

Before Error Lens: Write code, see squiggly line, hover, read tooltip "Type 'string' is not assignable to type 'number'", fix it, repeat.

After Error Lens: Write code, immediately see Type 'string' is not assignable to type 'number' in red at the end of the line, fix it, move on. The feedback loop drops from ~5 seconds to ~1 second per error.


2. Thunder Client — Postman Is Dead to Us

Extension ID: rangav.vscode-thunder-client What it replaced: Postman (entire desktop application) Install size: ~5 MB

Postman was a 400 MB Electron application that we ran alongside VS Code (another Electron application). Two Electron apps. Both eating RAM. One of them (Postman) requiring a login, wanting us to pay for "collaboration features," and nagging us about their AI features every time we opened it.

Thunder Client does everything we used Postman for — and it lives inside VS Code. No context switching. No extra RAM. No login required.

What It Actually Does

Thunder Client is a full REST API client with:

  • Request builder with every HTTP method, headers, body types (JSON, form-data, XML, GraphQL)
  • Collections for organizing related requests
  • Environments for switching between dev/staging/prod
  • Authentication (Bearer, Basic, OAuth 2.0, API Key)
  • Response visualization with JSON formatting, response time, status codes
  • Test scripting for automated response validation
  • Import/export for Postman collections, cURL commands, OpenAPI specs

Setting Up Environment Variables

This is where Thunder Client really shines. Create environments for each of your deployment targets:

Development Environment:

{
  "baseUrl": "http://localhost:3000",
  "apiKey": "dev_key_xxxxx",
  "dbHost": "localhost:5432"
}

Production Environment:

{
  "baseUrl": "https://api.yourapp.com",
  "apiKey": "{{vault:prod_api_key}}",
  "dbHost": "{{vault:prod_db_host}}"
}

Then in your requests, use {{baseUrl}}/api/users and switch environments with one click. No more editing URLs manually when you switch between local and production testing.

Collection Testing

Set up automated tests for your API endpoints. In the "Tests" tab of any request:

// Check status code
tc.test("Status is 200", function() {
  expect(tc.response.status).to.equal(200);
});

// Check response body
tc.test("Response has users array", function() {
  const data = tc.response.json;
  expect(data.users).to.be.an("array");
  expect(data.users.length).to.be.greaterThan(0);
});

// Check response time
tc.test("Response time is under 500ms", function() {
  expect(tc.response.time).to.be.below(500);
});

Run the entire collection with one click to smoke-test your API. We run this before every deployment as a sanity check.

The Non-Obvious Trick

Import cURL commands directly. When you are debugging an API issue and someone shares a cURL command in Slack, you do not need to manually recreate it in Thunder Client. Click the "Import Curl" button, paste the command, and it auto-populates the method, URL, headers, and body.

Even better: VS Code REST Client syntax. Thunder Client supports .http files. Create a file called api-tests.http:

### Get all users
GET {{baseUrl}}/api/users
Authorization: Bearer {{apiKey}}

### Create a user
POST {{baseUrl}}/api/users
Content-Type: application/json

{
  "name": "Test User",
  "email": "test@example.com"
}

These files are version-controllable. We commit them to our repos so the entire team has the same API test suite.

Why Not Insomnia or Hoppscotch?

We tried both. Insomnia went through the same bloat trajectory as Postman — login requirements, paid features, unnecessary complexity. Hoppscotch is great as a web app but does not integrate into VS Code. Thunder Client wins because it is lightweight, lives in the editor, and does not try to be more than an API client.


3. GitLens — You Are Using 10% of It

Extension ID: eamodio.gitlens What it replaced: GitHub Desktop (for history browsing), standalone git GUI tools Install size: ~12 MB

Yes, everyone knows GitLens. Yes, everyone uses the blame annotations. No, almost nobody uses the features that make GitLens genuinely powerful. After watching our team use GitLens for months, I realized most developers only scratch the surface. Here are the features that changed how we work with Git.

Beyond Blame Annotations: The Power Features

Feature 1: Interactive Rebase Editor

GitLens adds a visual interactive rebase interface. Instead of memorizing git rebase -i HEAD~5 and editing a text file with pick, squash, fixup, you get a visual editor where you:

  • Drag and drop commits to reorder them
  • Click to squash, fixup, edit, or drop commits
  • See the commit messages and diffs before deciding

Access it from the Command Palette: GitLens: Git Rebase...

We use this at least once a day to clean up messy commit histories before creating pull requests.

Feature 2: File History Timeline

Right-click any file and select "Open File History." You get a visual timeline showing every commit that touched that file, with diffs at each point. This is invaluable when debugging: "When did this function change? Who changed it? What was the commit message?"

The timeline view is faster than git log --follow -- path/to/file and shows you the actual diff inline.

Feature 3: Commit Graph

GitLens: Show Commit Graph opens a visual branch graph — similar to what GitKraken or Tower provide, but inside VS Code. You can see branch relationships, merge points, and commit history at a glance.

For teams working on multiple feature branches simultaneously, this replaces a standalone Git GUI. We stopped paying for GitKraken licenses after switching to GitLens's commit graph.

Feature 4: Comparison Views

Compare any two branches, tags, or commits side by side. This is not just git diff — it is a visual comparison with file-by-file navigation, inline diffs, and the ability to cherry-pick individual changes.

Access it: Command Palette, "GitLens: Compare References..."

We use this constantly before merging feature branches: "What exactly will change in main when we merge this branch?"

The Settings That Matter

{
  "gitlens.codeLens.enabled": false,
  "gitlens.currentLine.enabled": true,
  "gitlens.currentLine.pullRequests.enabled": true,
  "gitlens.hovers.currentLine.over": "line",
  "gitlens.statusBar.enabled": true,
  "gitlens.views.repositories.branches.layout": "tree",
  "gitlens.graph.layout": "editor",
  "gitlens.blame.format": "${author|20} ${date} ${message|50}"
}

Why we disable CodeLens: GitLens CodeLens adds "X authors, Y changes" annotations above every function. On large files, this creates visual noise. We prefer the current-line blame (bottom of the screen) which shows the same info without cluttering the code.

The Non-Obvious Trick

PR integration. With gitlens.currentLine.pullRequests.enabled, hovering over a line's blame annotation shows you the pull request that introduced the change — not just the commit. This is incredibly useful for understanding why a change was made, because PR descriptions typically have more context than commit messages.


4. Todo Tree — Turn Comments Into a Dashboard

Extension ID: Gruntfuggly.todo-tree What it replaced: Manually searching for TODOs, losing track of technical debt Install size: ~1 MB

Every codebase has TODO comments scattered through it. Most teams have no idea how many there are or where they live. Todo Tree scans your entire codebase and presents every TODO, FIXME, HACK, and custom tag in a navigable tree view in the sidebar.

Why This Matters

We did an audit on a client project we inherited. There were 147 TODO comments scattered across 89 files. Some were two years old. Some referenced features that had already been built. Some were genuinely important bugs that nobody had tracked because the TODO was buried in a file nobody opened.

Todo Tree makes technical debt visible. You cannot fix what you cannot see.

Custom Tags: Our Team Convention

The default TODO and FIXME are too generic. We use custom tags that communicate priority and category:

{
  "todo-tree.general.tags": [
    "TODO",
    "FIXME",
    "HACK",
    "PERF",
    "SECURITY",
    "DEBT",
    "NOTE",
    "REVIEW"
  ],
  "todo-tree.highlights.customHighlight": {
    "TODO": {
      "icon": "checklist",
      "foreground": "#fff",
      "background": "#f5a623",
      "iconColour": "#f5a623",
      "type": "tag"
    },
    "FIXME": {
      "icon": "alert",
      "foreground": "#fff",
      "background": "#ff6b6b",
      "iconColour": "#ff6b6b",
      "type": "tag"
    },
    "HACK": {
      "icon": "tools",
      "foreground": "#fff",
      "background": "#e74c3c",
      "iconColour": "#e74c3c",
      "type": "tag"
    },
    "PERF": {
      "icon": "rocket",
      "foreground": "#fff",
      "background": "#9b59b6",
      "iconColour": "#9b59b6",
      "type": "tag"
    },
    "SECURITY": {
      "icon": "shield",
      "foreground": "#fff",
      "background": "#c0392b",
      "iconColour": "#c0392b",
      "type": "tag"
    },
    "DEBT": {
      "icon": "credit-card",
      "foreground": "#fff",
      "background": "#3498db",
      "iconColour": "#3498db",
      "type": "tag"
    },
    "NOTE": {
      "icon": "info",
      "foreground": "#fff",
      "background": "#2ecc71",
      "iconColour": "#2ecc71",
      "type": "tag"
    },
    "REVIEW": {
      "icon": "eye",
      "foreground": "#fff",
      "background": "#1abc9c",
      "iconColour": "#1abc9c",
      "type": "tag"
    }
  },
  "todo-tree.filtering.excludeGlobs": [
    "**/node_modules/**",
    "**/dist/**",
    "**/.next/**",
    "**/coverage/**"
  ],
  "todo-tree.general.statusBar": "tags",
  "todo-tree.tree.groupedByTag": true
}

How we use each tag:

  • TODO: Standard task that needs to be done. Non-urgent.
  • FIXME: Known bug that needs fixing. Higher priority than TODO.
  • HACK: Temporary workaround. Must be replaced before next major release.
  • PERF: Performance improvement opportunity. Profile before optimizing.
  • SECURITY: Security concern. Review before deployment. Highest priority.
  • DEBT: Technical debt. Track it, plan to address it in maintenance sprints.
  • NOTE: Explanation for future developers. Not actionable, just context.
  • REVIEW: Needs code review or second opinion from another developer.

How We Use It In Practice

// SECURITY: Validate user input before SQL query — parameterized queries only
const user = await db.query("SELECT * FROM users WHERE id = $1", [userId]);

// PERF: This N+1 query should be replaced with a JOIN
for (const order of orders) {
  const items = await db.query("SELECT * FROM items WHERE order_id = $1", [order.id]);
}

// HACK: Temporary fix for timezone offset bug. Remove after migrating to date-fns
const adjustedDate = new Date(date.getTime() + 5.5 * 60 * 60 * 1000);

// DEBT: This component is 400 lines. Split into smaller components.
export function MegaComponent() { /* ... */ }

Every sprint planning session, we open the Todo Tree sidebar and review the SECURITY and FIXME tags. This has caught issues that would have otherwise been forgotten.

The Non-Obvious Trick

Count badge in the status bar. With "todo-tree.general.statusBar": "tags", the VS Code status bar shows the count of each tag type across your project. Seeing "SECURITY: 3" in your status bar is a constant reminder that something needs attention. We have a team rule: SECURITY count must be zero before any production deployment.


5. Continue — Open-Source AI That Replaced GitHub Copilot

Extension ID: continue.continue What it replaced: GitHub Copilot ($10/month/developer), ChatGPT browser tab for code questions Install size: ~50 MB

This is the big one. We canceled our GitHub Copilot licenses for the entire team and switched to Continue. It saved us $480/year across 4 developers, and the experience is better — because we control the AI model, the context, and the behavior.

What Continue Does

Continue is an open-source AI code assistant that integrates into VS Code (and JetBrains). It provides:

  • Tab autocomplete (like Copilot's inline suggestions)
  • Chat interface (like ChatGPT but with your codebase as context)
  • Inline editing (select code, describe the change, AI applies it)
  • Custom slash commands (build your own reusable prompts)
  • Codebase indexing (AI understands your entire project, not just the open file)
  • Model flexibility (use Claude, GPT, local models via Ollama, or any OpenAI-compatible API)

Our Configuration

We use Claude as our primary model through Continue. Here is our config.json (located at ~/.continue/config.json):

{
  "models": [
    {
      "title": "Claude Sonnet 4",
      "provider": "anthropic",
      "model": "claude-sonnet-4-20250514",
      "apiKey": "YOUR_API_KEY"
    }
  ],
  "tabAutocompleteModel": {
    "title": "Claude Haiku",
    "provider": "anthropic",
    "model": "claude-haiku-4-20250414",
    "apiKey": "YOUR_API_KEY"
  },
  "customCommands": [
    {
      "name": "review",
      "description": "Code review the selected code",
      "prompt": "Review this code for bugs, security issues, performance problems, and readability. Be specific and suggest fixes with code examples."
    },
    {
      "name": "test",
      "description": "Generate tests for the selected code",
      "prompt": "Write comprehensive unit tests for this code using Vitest. Include edge cases, error scenarios, and happy paths. Use describe/it blocks."
    },
    {
      "name": "refactor",
      "description": "Suggest refactoring improvements",
      "prompt": "Suggest refactoring improvements for this code. Focus on readability, maintainability, and following SOLID principles. Show the refactored code."
    },
    {
      "name": "explain",
      "description": "Explain what this code does",
      "prompt": "Explain what this code does in plain language. Include the purpose, inputs, outputs, and any side effects. Assume I understand programming but not this specific codebase."
    }
  ],
  "contextProviders": [
    { "name": "code" },
    { "name": "docs" },
    { "name": "diff" },
    { "name": "terminal" },
    { "name": "problems" },
    { "name": "folder" },
    { "name": "codebase" }
  ],
  "slashCommands": [
    {
      "name": "commit",
      "description": "Generate a commit message for staged changes"
    },
    {
      "name": "share",
      "description": "Export this conversation as markdown"
    }
  ]
}

Why We Chose Continue Over GitHub Copilot

1. Model choice. Copilot uses OpenAI models. Continue lets us use Claude, which we find produces better code suggestions for our TypeScript/React stack. The ability to switch models without switching tools is valuable.

2. Codebase indexing. Continue indexes your entire codebase and uses it as context. When you ask "how does authentication work in this project?", it searches your codebase, finds the relevant files, and answers based on your code. Copilot's context is limited to open files and nearby files.

3. Custom slash commands. The ability to create reusable prompts tailored to our workflow is powerful. /review triggers a code review prompt we have refined over months. /test generates tests matching our exact testing conventions. Copilot's chat is good, but it does not have this level of customization.

4. Privacy control. With Continue, we control where our code goes. We can run local models via Ollama for sensitive projects, or use Claude's API (which does not train on API data) for everything else. Some of our clients have strict data policies that prohibit sending code to third-party AI services — local models solve this.

5. Open source. Continue's codebase is on GitHub. We can see how it works, contribute improvements, and trust that there is no hidden data collection. Copilot is a black box.

The Non-Obvious Trick

The @codebase context provider is the killer feature. When chatting with Continue, prefix your question with @codebase and it will search your entire project for relevant code before answering. This turns the AI from "generic code assistant" to "expert on your specific project."

Example: Type @codebase How do we handle API errors in this project? and Continue will find your error handling utilities, middleware, and patterns across the codebase, then explain them.

Compare that to Copilot, where you would need to manually open the relevant files and hope it picks up the context.

Cost Comparison

GitHub Copilot Continue + Claude API
Monthly cost (per developer) $10/month fixed ~$5-8/month usage-based
Annual cost (4 developers) $480 ~$240-384
Model choice OpenAI only Claude, GPT, Gemini, local models
Codebase indexing Limited Full project indexing
Custom commands No Yes
Privacy control Limited Full control
Open source No Yes

We spend roughly $6/developer/month on Claude API calls through Continue, which is less than Copilot and gives us a better experience.


Bonus: The 5 Extensions We Uninstalled and Why

1. Bracket Pair Colorizer — VS Code has this built in since version 1.60. Enable it with "editor.bracketPairColorization.enabled": true. There is zero reason to keep the extension.

2. Auto Close Tag — VS Code's built-in Emmet handles this in HTML and JSX. The extension added lag and occasionally interfered with TypeScript auto-imports.

3. Auto Rename Tag — VS Code added built-in linked editing for HTML tags: "editor.linkedEditing": true. Works in .html, .astro, and .jsx files.

4. Path Intellisense — VS Code's built-in path completion (triggered by typing ./ or ../) covers 90% of use cases. The extension added ~200ms to file completion in large projects.

5. Import Cost — Showed the bundle size of imported packages inline. Useful in theory, but it slowed down the editor significantly on large files and the data was often inaccurate for tree-shaken imports. We check bundle sizes with bundlephobia.com when it actually matters.

Performance Impact: Extension Audit Results

We measured VS Code startup time and memory usage with different extension configurations:

Configuration Startup Time Memory Usage
Zero extensions 1.2 s 280 MB
Our 5 extensions 1.8 s 340 MB
Previous 37 extensions 4.6 s 580 MB
Typical developer (15-20 extensions) 3.1 s 450 MB

Our 5-extension setup adds only 600ms to startup and 60 MB to memory usage. The previous 37-extension setup more than tripled startup time and doubled memory usage. Fewer, better extensions beat many mediocre ones.

Our Complete Team Settings (Sanitized)

Here is the relevant portion of our shared settings.json that every CODERCOPS developer uses:

{
  "editor.fontSize": 14,
  "editor.lineHeight": 1.6,
  "editor.fontFamily": "'JetBrains Mono', 'Fira Code', monospace",
  "editor.fontLigatures": true,
  "editor.minimap.enabled": false,
  "editor.bracketPairColorization.enabled": true,
  "editor.linkedEditing": true,
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "editor.suggestSelection": "first",
  "editor.tabSize": 2,
  "editor.wordWrap": "on",

  "files.autoSave": "onFocusChange",
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true,

  "terminal.integrated.fontSize": 13,
  "terminal.integrated.defaultProfile.osx": "zsh",

  "workbench.colorTheme": "One Dark Pro",
  "workbench.iconTheme": "material-icon-theme",
  "workbench.startupEditor": "none",

  "errorLens.enabledDiagnosticLevels": ["error", "warning"],
  "errorLens.excludeBySource": ["cSpell"],
  "errorLens.messageMaxChars": 120,
  "errorLens.delay": 500,

  "gitlens.codeLens.enabled": false,
  "gitlens.currentLine.enabled": true,
  "gitlens.currentLine.pullRequests.enabled": true,

  "todo-tree.general.tags": ["TODO", "FIXME", "HACK", "PERF", "SECURITY", "DEBT"],
  "todo-tree.general.statusBar": "tags",
  "todo-tree.tree.groupedByTag": true
}

We commit a .vscode/settings.json to every project repo with project-specific settings (formatter config, extension recommendations). Team-wide settings live in each developer's global config.

The Takeaway

Extension management is like dependency management: fewer, better choices beat a bloated list every time. These 5 extensions — Error Lens, Thunder Client, GitLens, Todo Tree, and Continue — cover error visibility, API testing, git workflows, technical debt tracking, and AI assistance. That is the entire outer loop of development, handled inside your editor.

Before you install the next trending extension someone recommends on Twitter, ask yourself: "Does VS Code already do this built in? Does one of my existing extensions already cover this? Is this genuinely worth the startup time and memory cost?"

If the answer to all three is no, install it. If not, keep your editor lean.


Want to Level Up Your Team's Developer Workflow?

At CODERCOPS, we help development teams optimize their tooling, workflows, and processes. From VS Code configurations to CI/CD pipelines to code review practices — we have opinions and experience to back them up.

Reach out if you want help standardizing your team's development environment or check out our blog for more developer productivity content.

Comments