Ides Editors

VS Code Workspace Optimization for Productivity

How to configure VS Code workspaces for maximum developer productivity, including settings, file exclusions, task automation, and workspace-specific configurations.

VS Code Workspace Optimization for Productivity

Most developers open VS Code and start coding without touching the workspace configuration. They tolerate slow searches that crawl through node_modules, sidebar file trees cluttered with build artifacts, and generic settings that do not match their project's needs. A properly configured workspace transforms VS Code from a generic editor into a purpose-built tool for your specific project.

I configure workspaces for every project I join. The upfront investment of 15 minutes saves hours of friction over the project's lifetime. This guide covers every workspace optimization that matters.

Prerequisites

  • VS Code installed
  • A project with at least a few files and directories
  • Understanding of JSON configuration
  • Familiarity with VS Code settings and command palette

Workspace Settings vs User Settings

VS Code has three settings layers:

  1. Default settings — VS Code's built-in defaults
  2. User settings~/.config/Code/User/settings.json (your personal preferences)
  3. Workspace settings.vscode/settings.json (project-specific, committed to git)

Workspace settings override user settings. This lets you enforce project conventions without changing anyone's personal preferences.

// .vscode/settings.json
{
  // Project uses 2-space indentation
  "editor.tabSize": 2,
  "editor.insertSpaces": true,

  // Project-specific file associations
  "files.associations": {
    "*.pug": "pug",
    "*.env.*": "dotenv",
    "Taskfile": "javascript"
  },

  // Disable telemetry for this workspace
  "telemetry.telemetryLevel": "off"
}

File Exclusions

The most impactful optimization. Excluding irrelevant directories from the file tree and search makes everything faster.

Explorer Exclusions

Hide directories from the sidebar file explorer:

{
  "files.exclude": {
    "**/node_modules": true,
    "**/.git": true,
    "**/dist": true,
    "**/build": true,
    "**/coverage": true,
    "**/.nyc_output": true,
    "**/*.min.js": true,
    "**/*.min.css": true,
    "**/*.map": true,
    "**/.DS_Store": true,
    "**/Thumbs.db": true
  }
}

Search Exclusions

Exclude directories from search results (separate from explorer exclusions):

{
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/build": true,
    "**/coverage": true,
    "**/.next": true,
    "**/package-lock.json": true,
    "**/yarn.lock": true,
    "**/*.min.js": true,
    "**/vendor": true,
    "**/.cache": true
  },

  // Also exclude from file watchers (reduces CPU usage)
  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/dist/**": true,
    "**/build/**": true,
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/coverage/**": true,
    "**/.next/**": true
  }
}

The difference matters. files.exclude hides from the explorer. search.exclude hides from search. files.watcherExclude reduces file system events (critical for large projects).

Conditional Exclusions

For monorepos where you only work in certain packages:

{
  "files.exclude": {
    "packages/legacy-app": true,
    "packages/mobile-app": true,
    "packages/docs": true
  }
}

Toggle visibility with the "Files: Toggle Excluded Files" command or remove entries when you need to work in those directories.

Search Configuration

Fine-tune search for your project:

{
  // Use ripgrep-compatible glob patterns
  "search.useIgnoreFiles": true,
  "search.useGlobalIgnoreFiles": true,

  // Follow symlinks (disable for large projects)
  "search.followSymlinks": false,

  // Show line numbers in search results
  "search.showLineNumbers": true,

  // Smart case: case-insensitive unless you use uppercase
  "search.smartCase": true,

  // Default include patterns for search
  "search.include": {},

  // Max results before stopping
  "search.maxResults": 10000
}

Scoped Search Shortcuts

Create keybindings for searching specific directories:

// .vscode/keybindings.json (user-level, not workspace)
[
  {
    "key": "ctrl+shift+f",
    "command": "workbench.action.findInFiles",
    "args": {
      "query": "",
      "filesToInclude": "src/**",
      "filesToExclude": "**/*.test.js"
    }
  }
]

Workspace Tasks

VS Code tasks automate common operations. Define them in .vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Start Dev Server",
      "type": "shell",
      "command": "npm",
      "args": ["run", "dev"],
      "group": "build",
      "isBackground": true,
      "problemMatcher": {
        "pattern": {
          "regexp": "^(.*):(\\d+):(\\d+): (error|warning) (.*)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "Starting dev server",
          "endsPattern": "Server running on"
        }
      },
      "presentation": {
        "reveal": "always",
        "panel": "dedicated"
      }
    },
    {
      "label": "Run Tests",
      "type": "shell",
      "command": "npm",
      "args": ["test"],
      "group": {
        "kind": "test",
        "isDefault": true
      },
      "problemMatcher": "$jest",
      "presentation": {
        "reveal": "always",
        "clear": true
      }
    },
    {
      "label": "Lint",
      "type": "shell",
      "command": "npx",
      "args": ["eslint", "src/", "--format", "unix"],
      "problemMatcher": "$eslint-stylish",
      "presentation": {
        "reveal": "onProblem"
      }
    },
    {
      "label": "Build Production",
      "type": "shell",
      "command": "npm",
      "args": ["run", "build"],
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "problemMatcher": [],
      "dependsOn": ["Lint"]
    },
    {
      "label": "Database Migration",
      "type": "shell",
      "command": "npx",
      "args": ["knex", "migrate:latest"],
      "problemMatcher": [],
      "presentation": {
        "reveal": "always"
      }
    },
    {
      "label": "Docker Up",
      "type": "shell",
      "command": "docker",
      "args": ["compose", "up", "-d"],
      "problemMatcher": []
    }
  ]
}

Run tasks with Ctrl+Shift+B (build task) or Ctrl+Shift+P → "Tasks: Run Task".

Compound Tasks

Run multiple tasks together:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Full Stack Dev",
      "dependsOn": ["Docker Up", "Start Dev Server", "Watch Tests"],
      "dependsOrder": "sequence",
      "problemMatcher": []
    },
    {
      "label": "Watch Tests",
      "type": "shell",
      "command": "npx",
      "args": ["jest", "--watch"],
      "isBackground": true,
      "problemMatcher": "$jest",
      "presentation": {
        "panel": "dedicated",
        "group": "dev"
      }
    }
  ]
}

Launch Configurations

Configure debugging in .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Server",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/src/index.js",
      "env": {
        "NODE_ENV": "development",
        "PORT": "3000",
        "DB_HOST": "localhost"
      },
      "envFile": "${workspaceFolder}/.env",
      "console": "integratedTerminal",
      "restart": true,
      "runtimeArgs": ["--inspect"],
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**"
      ]
    },
    {
      "name": "Run Current Test File",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": [
        "${relativeFile}",
        "--no-coverage"
      ],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    },
    {
      "name": "Debug Current Test",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": [
        "${relativeFile}",
        "--no-coverage",
        "--testNamePattern",
        "${selectedText}"
      ],
      "console": "integratedTerminal"
    },
    {
      "name": "Attach to Process",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "restart": true,
      "skipFiles": ["<node_internals>/**"]
    }
  ],
  "compounds": [
    {
      "name": "Server + Tests",
      "configurations": ["Launch Server", "Run Current Test File"]
    }
  ]
}

Recommended Extensions

Declare recommended extensions for the project:

// .vscode/extensions.json
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "bradlc.vscode-tailwindcss",
    "ms-vscode.vscode-typescript-next",
    "humao.rest-client",
    "editorconfig.editorconfig"
  ],
  "unwantedRecommendations": [
    "ms-vscode.vscode-typescript-tslint-plugin"
  ]
}

When someone opens the project, VS Code prompts them to install these extensions.

Language-Specific Settings

Override settings per language:

{
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit"
    }
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true,
    "editor.tabSize": 2
  },
  "[markdown]": {
    "editor.wordWrap": "on",
    "editor.quickSuggestions": {
      "other": false,
      "comments": false,
      "strings": false
    },
    "editor.rulers": [80]
  },
  "[sql]": {
    "editor.tabSize": 4,
    "editor.formatOnSave": false
  },
  "[pug]": {
    "editor.tabSize": 2,
    "editor.insertSpaces": false
  }
}

Editor Rulers and Formatting

{
  // Show rulers at specific columns
  "editor.rulers": [80, 120],

  // Trim trailing whitespace on save
  "files.trimTrailingWhitespace": true,

  // Ensure final newline
  "files.insertFinalNewline": true,

  // Trim final newlines (keep just one)
  "files.trimFinalNewlines": true,

  // Auto-save after delay
  "files.autoSave": "afterDelay",
  "files.autoSaveDelay": 1000,

  // Format on save (if formatter is configured)
  "editor.formatOnSave": true,
  "editor.formatOnPaste": false,

  // Default end of line character
  "files.eol": "\n"
}

Complete Working Example: Full Workspace Configuration

Here is a complete .vscode/ configuration for a Node.js API project:

// .vscode/settings.json
{
  // Editor
  "editor.tabSize": 2,
  "editor.insertSpaces": true,
  "editor.rulers": [100],
  "editor.formatOnSave": true,
  "editor.minimap.enabled": false,
  "editor.bracketPairColorization.enabled": true,
  "editor.guides.bracketPairs": true,
  "editor.renderWhitespace": "boundary",

  // Files
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true,
  "files.trimFinalNewlines": true,
  "files.eol": "\n",
  "files.associations": {
    "*.pug": "pug",
    ".env.*": "dotenv",
    "Dockerfile.*": "dockerfile"
  },

  // Exclusions
  "files.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/coverage": true,
    "**/.nyc_output": true,
    "**/*.min.js": true,
    "**/*.min.css": true
  },
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/coverage": true,
    "**/package-lock.json": true
  },
  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/dist/**": true,
    "**/coverage/**": true,
    "**/.git/objects/**": true
  },

  // Search
  "search.smartCase": true,
  "search.followSymlinks": false,
  "search.showLineNumbers": true,

  // Language-specific
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit"
    }
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.tabSize": 2
  },
  "[markdown]": {
    "editor.wordWrap": "on",
    "editor.rulers": [80]
  },
  "[sql]": {
    "editor.tabSize": 4
  },

  // ESLint
  "eslint.workingDirectories": ["."],
  "eslint.validate": ["javascript"],

  // Terminal
  "terminal.integrated.defaultProfile.linux": "bash",
  "terminal.integrated.env.linux": {
    "NODE_ENV": "development"
  }
}
// .vscode/tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "dev",
      "type": "shell",
      "command": "npm run dev",
      "isBackground": true,
      "group": { "kind": "build", "isDefault": true },
      "problemMatcher": {
        "pattern": { "regexp": "." },
        "background": {
          "activeOnStart": true,
          "beginsPattern": ".",
          "endsPattern": "Server running"
        }
      },
      "presentation": { "panel": "dedicated", "group": "dev" }
    },
    {
      "label": "test",
      "type": "shell",
      "command": "npm test",
      "group": { "kind": "test", "isDefault": true },
      "problemMatcher": "$jest",
      "presentation": { "clear": true }
    },
    {
      "label": "lint",
      "type": "shell",
      "command": "npx eslint src/ --format unix",
      "problemMatcher": "$eslint-stylish"
    },
    {
      "label": "db:migrate",
      "type": "shell",
      "command": "npm run migrate",
      "problemMatcher": []
    }
  ]
}
// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Server",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/app.js",
      "envFile": "${workspaceFolder}/.env",
      "console": "integratedTerminal",
      "restart": true,
      "skipFiles": ["<node_internals>/**", "**/node_modules/**"]
    },
    {
      "name": "Test File",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": ["${relativeFile}", "--no-coverage"],
      "console": "integratedTerminal"
    }
  ]
}
// .vscode/extensions.json
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "editorconfig.editorconfig",
    "humao.rest-client",
    "mikestead.dotenv"
  ]
}

Common Issues and Troubleshooting

Search is slow despite exclusions

The files.watcherExclude might not match your patterns. Glob patterns in watcher exclusions need the trailing /**:

// Wrong: doesn't exclude contents
"**/node_modules": true

// Correct: excludes all nested files
"**/node_modules/**": true

Format on save does nothing

No formatter is configured for the language. Check the bottom-right status bar for the formatter:

Fix: Install a formatter extension (Prettier, ESLint) and set editor.defaultFormatter in the language-specific settings.

Workspace settings not taking effect

The .vscode/settings.json file might have JSON syntax errors. VS Code silently ignores invalid JSON:

Fix: Open the file and check the Problems panel for JSON parse errors. Trailing commas and missing quotes are the usual culprits.

Tasks cannot find npm or node

The integrated terminal's PATH might differ from your system shell:

Fix: Set terminal.integrated.env.* to include the correct paths, or use absolute paths in task commands.

Extensions conflict with each other

Two formatters both trying to format on save can cause issues:

Fix: Set editor.defaultFormatter explicitly for each language. Disable format-on-save for languages where you use a linter's auto-fix instead.

Best Practices

  • Commit .vscode/settings.json to git. This ensures every developer has the same editor configuration. Add .vscode/launch.json and tasks.json too.
  • Do not commit personal preferences. Settings like font size, theme, and keybindings belong in user settings, not workspace settings.
  • Exclude aggressively. Every directory you exclude from search and file watching makes VS Code faster. Start broad and add back only what you need.
  • Use language-specific settings. Different files in the same project may need different indentation, formatters, and rulers. Configure per-language.
  • Define tasks for common operations. If you run it more than twice a day, make it a VS Code task. Developers should not need to memorize CLI commands.
  • Keep launch configurations up to date. New team members should be able to press F5 and have the app running with debugging enabled.
  • Use extensions.json recommendations. This is how you ensure every developer has the tools the project depends on without mandating specific extensions in documentation.
  • Review settings when project dependencies change. Adding a new framework or tool often means adding new file exclusions, formatters, or task definitions.

References

Powered by Contentful