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:
- Default settings — VS Code's built-in defaults
- User settings —
~/.config/Code/User/settings.json(your personal preferences) - 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.jsonto git. This ensures every developer has the same editor configuration. Add.vscode/launch.jsonandtasks.jsontoo. - 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.jsonrecommendations. 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.