All topics
General · Learning hub

VS Code notes for developers

Master VS Code with a curated set of 4 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
VS Code

Git & Source Control

VS Code: Git & Source Control VS Code has a full-featured Git client built in. The Source Control panel (Ctrl+Shift+G), diff editor, merge conflict resolution,

VS Code: Git & Source Control

VS Code has a full-featured Git client built in. The Source Control panel (Ctrl+Shift+G), diff editor, merge conflict resolution, and extensions like GitLens make it a powerful alternative to command-line Git for day-to-day work.

Built-in Git Panel

# Source Control Panel: Ctrl+Shift+G (Cmd+Shift+G on Mac)
# Shows: staged changes, unstaged changes, merge conflicts

# Keyboard shortcuts for Git operations:
# Stage current file:          (click + icon in Source Control panel)
# Stage a hunk:                Open diff view → click + on a hunk
# Commit:                      Ctrl+Enter in Source Control message box
# Push:                        Sync button in status bar (bottom-left)

# Stage individual hunks without CLI:
# 1. Click the file in Source Control panel (opens inline diff)
# 2. Hover over a change block → click "Stage Change" (+) icon
# 3. Or: right-click → "Stage Selected Ranges"

# Command Palette Git commands (Ctrl+Shift+P):
# Git: Checkout to...          (switch branches)
# Git: Create Branch...        (new branch from current)
# Git: Merge Branch...         (merge into current)
# Git: Rebase Branch...        (rebase current onto another)
# Git: Stash                   (stash all uncommitted changes)
# Git: Pop Stash               (apply most recent stash)
# Git: Fetch                   (git fetch all remotes)
# Git: Pull                    (git pull)
# Git: Push                    (git push)
# Git: Revert File             (discard changes in file)
# Git: Open Changes            (open diff view for current file)

# Timeline view (right-click file → Open Timeline)
# Shows all commits that touched the file
# Click any commit to see diff at that point in history

Diff View & Merge Conflict Resolution

# Opening diffs:
# Click file in Source Control panel → inline diff
# Ctrl+Shift+G → click file → opens side-by-side diff
# Compare any two files: right-click file → "Select for Compare",
#   then right-click second file → "Compare with Selected"

# Diff editor keyboard shortcuts:
# F7 / Shift+F7    Navigate to next/previous diff hunk
# Alt+F5           Navigate to next/previous change
# Ctrl+K Ctrl+N    Go to next difference
# Ctrl+K Ctrl+P    Go to previous difference

# Merge conflict resolution UI:
# VS Code shows conflict markers as interactive UI:
# - "Accept Current Change" (keep HEAD / your branch)
# - "Accept Incoming Change" (take the incoming branch changes)
# - "Accept Both Changes"   (keep both, stacked)
# - "Compare Changes"       (open 3-way diff view)

# Three-way merge editor (VS Code 1.69+):
# Ctrl+Shift+P → "Git: Open Merge Editor"
# Shows: Incoming (left), Current (right), Result (bottom)
# Click checkboxes to accept individual hunks
# Edit Result pane directly for custom resolution

# After resolving all conflicts:
# Stage the file (+ button or git add)
# Commit the merge (Ctrl+Enter)

GitLens Extension

GitLens supercharges the built-in Git capabilities. Install: Extensions panel → search "GitLens" → Install.

# Key GitLens features:

# 1. Inline blame (shown at end of every line)
# Hover to see full commit details, PR link, diff
# Toggle: Ctrl+Shift+P → "GitLens: Toggle Line Blame"

# 2. File annotations (full-file blame)
# Toggle: Ctrl+Shift+P → "GitLens: Toggle File Blame"
# Or: click "Toggle File Blame" button in editor title

# 3. Commit graph (GitLens Pro, or built-in in VS Code 1.73+)
# Ctrl+Shift+P → "Git: Show Commit Graph"
# Visual branch graph, clickable commits

# 4. Code lens (above functions - shows last change, authors)
# Enabled in settings: gitlens.codeLens.enabled: true

# 5. Compare branches/commits
# GitLens panel → Commits → right-click any commit → "Open Changes"
# GitLens panel → Branches → right-click → "Open Branch Changes"

# 6. Git worktrees (check out multiple branches simultaneously)
# GitLens panel → Worktrees → "Create Worktree"

# Useful GitLens settings in settings.json:
{
  "gitlens.codeLens.enabled": true,
  "gitlens.currentLine.enabled": true,
  "gitlens.blame.highlight.enabled": true,
  "gitlens.statusBar.enabled": true
}

Git Graph & Branch Visualization

# Built-in Commit Graph (VS Code 1.73+, no extension needed)
# Source Control panel → top-right "..." menu → "Show Commit Graph"
# Or: Ctrl+Shift+P → "Git: Show Commit Graph"

# Features:
# - Visual branch graph with colors per branch
# - Click commit to see files changed, diff
# - Right-click commit → cherry-pick, reset, revert, create tag
# - Filter by branch, author, date
# - Search commits

# Git Graph extension (popular alternative, more features)
# Extensions → search "Git Graph" by mhutchie
# Bottom status bar → "Git Graph" button
# Right-click any commit for: checkout, cherry-pick, merge, rebase,
#   create branch, create tag, revert, reset to, push, copy SHA

# VS Code Source Control status bar (bottom left):
# Shows: branch name, sync status (↑2 ↓1)
# Click: opens branch picker
# Click ↑/↓ arrows: push/pull

# Keyboard shortcuts:
# Ctrl+Shift+G G    Open Source Control panel
# Ctrl+Shift+P      Then type any Git command
VS Code

Terminal & Tasks

VS Code: Terminal & Tasks VS Code's integrated terminal and task runner keep you inside the editor for the full development workflow. Tasks automate common comm

VS Code: Terminal & Tasks

VS Code's integrated terminal and task runner keep you inside the editor for the full development workflow. Tasks automate common commands with keyboard shortcuts and problem matchers that highlight errors inline.

Integrated Terminal Shortcuts

# Open/toggle terminal
Ctrl+`                  # Toggle terminal panel
Ctrl+Shift+`            # Create new terminal
Ctrl+Shift+5            # Split terminal (side by side)

# Navigate terminals
Ctrl+PageDown/PageUp    # Switch to next/previous terminal
Ctrl+Alt+L              # Focus terminal (even when in editor)
Alt+Left/Right          # Navigate terminal history entries

# Terminal panel management
Ctrl+Shift+`            # New terminal
# Drag the + to create named terminals
# Right-click tab → Rename (give terminals names like "dev server", "tests")

# Send selection to terminal
# Select code in editor → right-click → "Run in Active Terminal"
# Or: Ctrl+Shift+P → "Terminal: Run Selected Text in Active Terminal"

# Clear terminal
Ctrl+K                  # Clear terminal output (while terminal has focus)
# Or: type "clear" / "cls" on Windows

# Copy/paste in terminal
Ctrl+C                  # Copy selected text (when text is selected)
Ctrl+Shift+C            # Copy (macOS: Cmd+C)
Ctrl+Shift+V            # Paste

# Zoom terminal text
Ctrl+= / Ctrl+-          # Increase/decrease font size

# Terminal settings
# Ctrl+Shift+P → "Terminal: Select Default Profile"
# Available profiles: bash, zsh, fish, PowerShell, Git Bash

# Split terminal for running two things simultaneously
Ctrl+Shift+5            # Split current terminal vertically
# Example: npm run dev in left, npm test in right

tasks.json

Tasks in VS Code run arbitrary shell commands with keyboard shortcuts, problem matchers, and dependency chains. Create `.vscode/tasks.json`.

// .vscode/tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Build TypeScript",
      "type": "shell",
      "command": "npx tsc --noEmit",
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "presentation": {
        "reveal": "always",
        "panel": "shared"
      },
      "problemMatcher": "$tsc"
    },
    {
      "label": "Run Dev Server",
      "type": "shell",
      "command": "npm run dev",
      "isBackground": true,
      "presentation": {
        "reveal": "always",
        "panel": "dedicated"
      },
      "problemMatcher": {
        "pattern": {
          "regexp": "^(ERROR|WARNING)\\s+in\\s+(.+):(\\d+):(\\d+)$",
          "severity": 1,
          "file": 2,
          "line": 3,
          "column": 4
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "Starting\\.\\.\\.\",
          "endsPattern": "ready in"
        }
      }
    },
    {
      "label": "Run Tests with Watch",
      "type": "shell",
      "command": "npx jest --watch --passWithNoTests",
      "isBackground": true,
      "problemMatcher": "$jest-watch"
    },
    {
      "label": "Full Check (type + lint + test)",
      "dependsOn": ["Build TypeScript", "Lint", "Run Tests"],
      "dependsOrder": "parallel",
      "group": "build"
    }
  ]
}

Terminal Profiles & Environment

// settings.json - terminal configuration
{
  // Default shell profile
  "terminal.integrated.defaultProfile.osx": "zsh",
  "terminal.integrated.defaultProfile.linux": "bash",
  "terminal.integrated.defaultProfile.windows": "Git Bash",

  // Custom named profiles
  "terminal.integrated.profiles.osx": {
    "zsh": {
      "path": "/bin/zsh",
      "args": ["-l"],
      "icon": "terminal-bash"
    },
    "node-dev": {
      "path": "/bin/zsh",
      "env": {
        "NODE_ENV": "development",
        "DATABASE_URL": "postgresql://localhost/myapp_dev"
      },
      "icon": "symbol-event"
    }
  },

  // Font and appearance
  "terminal.integrated.fontFamily": "'MesloLGS NF', monospace",
  "terminal.integrated.fontSize": 13,
  "terminal.integrated.lineHeight": 1.2,
  "terminal.integrated.cursorStyle": "line",
  "terminal.integrated.cursorBlinking": true,

  // Behavior
  "terminal.integrated.scrollback": 10000,
  "terminal.integrated.persistentSessionSupport": true,
  "terminal.integrated.enableImages": true,

  // Env vars for all integrated terminals
  "terminal.integrated.env.osx": {
    "EDITOR": "code --wait"
  }
}

Running & Debugging Tasks

# Running tasks
Ctrl+Shift+B            # Run default build task
Ctrl+Shift+P → "Tasks: Run Task"  # Pick any task
Ctrl+Shift+P → "Tasks: Run Build Task"
Ctrl+Shift+P → "Tasks: Run Test Task"

# After running a task:
# Errors are shown in the Problems panel (Ctrl+Shift+M)
# Click any error → jumps to the file + line
# Problem matcher parses compiler/linter output into Problems panel

# Common problem matchers:
# "$tsc"         TypeScript compiler
# "$tsc-watch"   TypeScript watch mode
# "$eslint-stylish"  ESLint stylish output
# "$jest-watch"  Jest watch mode
# "$go"          Go compiler
# "$rust"        Rust/Cargo compiler

# Binding a task to a keyboard shortcut (keybindings.json)
[
  {
    "key": "ctrl+shift+t",
    "command": "workbench.action.tasks.runTask",
    "args": "Run Tests with Watch"
  },
  {
    "key": "ctrl+shift+b",
    "command": "workbench.action.tasks.runTask",
    "args": "Build TypeScript"
  }
]

# launch.json: debug config (separate from tasks)
# Ctrl+Shift+D → create launch.json
# Run & Debug panel → play button
# F5 = start debugging, F9 = toggle breakpoint
# F10 = step over, F11 = step into, Shift+F11 = step out
VS Code

Remote Development

VS Code: Remote Development VS Code's Remote Development extensions let you write code locally while the execution environment is on a remote server, inside Doc

VS Code: Remote Development

VS Code's Remote Development extensions let you write code locally while the execution environment is on a remote server, inside Docker, or in WSL. The UI runs locally; the language server, debugger, and terminal run remotely.

Remote SSH

# Install: Extensions → "Remote - SSH" (ms-vscode-remote.remote-ssh)

# Connect to a remote host
# Ctrl+Shift+P → "Remote-SSH: Connect to Host..."
# Enter: user@hostname or use a config alias

# SSH config for named hosts (~/.ssh/config)
Host my-dev-server
  HostName 192.168.1.100
  User ubuntu
  IdentityFile ~/.ssh/id_ed25519
  ForwardAgent yes

Host aws-prod
  HostName ec2-54-123-456-789.compute-1.amazonaws.com
  User ec2-user
  IdentityFile ~/.ssh/aws-keypair.pem
  ServerAliveInterval 60

# After connecting:
# - VS Code status bar shows "SSH: hostname" (bottom-left)
# - Open Folder: opens folders on the remote machine
# - Terminal: runs on remote machine
# - Extensions: installed separately per remote host
# - Debug: runs on remote

# Port forwarding: forward remote port to local
# Ctrl+Shift+P → "Forward a Port"
# Or: Remote Explorer panel → Forwarded Ports → + icon
# Example: forward remote :3000 to local :3000 (access at localhost:3000)

# View forwarded ports
# Bottom panel → PORTS tab (appears when connected remotely)

# Reconnect to last remote
# Ctrl+Shift+P → "Remote-SSH: Connect to Host..." → pick recent

# Open remote file directly from URL
code --remote ssh-remote+my-dev-server /home/ubuntu/myproject

Dev Containers

Dev Containers define the development environment as code in `.devcontainer/devcontainer.json`. The entire team gets the exact same tools, runtimes, and dependencies - no more "works on my machine".

// .devcontainer/devcontainer.json
{
  "name": "Node.js + PostgreSQL",

  // Use a prebuilt image
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20-bookworm",

  // Or build from Dockerfile
  // "build": {
  //   "dockerfile": "Dockerfile",
  //   "context": ".."
  // },

  // Docker Compose for multi-container setups
  // "dockerComposeFile": "docker-compose.yml",
  // "service": "app",
  // "workspaceFolder": "/workspace",

  // Services (adds containers alongside the dev container)
  "features": {
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {},
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },

  // Forward ports (accessible on host)
  "forwardPorts": [3000, 5432],
  "portsAttributes": {
    "3000": { "label": "App", "onAutoForward": "openBrowser" },
    "5432": { "label": "PostgreSQL", "onAutoForward": "silent" }
  },

  // Run after container starts
  "postCreateCommand": "npm install && npm run db:migrate",

  // VS Code settings inside container
  "customizations": {
    "vscode": {
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash",
        "editor.formatOnSave": true
      },
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "ms-azuretools.vscode-docker"
      ]
    }
  },

  // Run as non-root user
  "remoteUser": "node"
}

WSL (Windows Subsystem for Linux)

# Install: Extensions → "WSL" (ms-vscode-remote.remote-wsl)
# Requires WSL 2 installed on Windows

# Install WSL 2
wsl --install              # Installs WSL2 + Ubuntu
wsl --install -d Ubuntu-22.04
wsl --set-default-version 2

# Connect VS Code to WSL
# Option 1: from WSL terminal
wsl
code .                     # Opens VS Code connected to WSL filesystem

# Option 2: from VS Code
# Ctrl+Shift+P → "WSL: Connect to WSL"
# Ctrl+Shift+P → "WSL: Connect to WSL using Distro..." → pick Ubuntu

# Status bar shows "WSL: Ubuntu" when connected
# Terminal runs Linux shell
# All tools (node, python, git) use Linux versions
# File system: /home/user/ (Linux) or /mnt/c/Users/... (Windows FS)

# Open WSL folder from Windows context menu
# Right-click folder in Windows Explorer → "Open with VS Code in WSL"

# Performance tip: keep project files on Linux filesystem (/home/user/)
# NOT on /mnt/c/ - Linux → Windows filesystem access is slow

# Port forwarding is automatic in WSL2
# App running on :3000 in WSL accessible at localhost:3000 on Windows

GitHub Codespaces

# GitHub Codespaces: cloud-hosted dev containers on GitHub
# Uses devcontainer.json from your repo
# Access via: github.com/owner/repo → Code → Codespaces → New codespace

# Open Codespace in VS Code desktop (instead of browser)
# Install: Extensions → "GitHub Codespaces"
# Ctrl+Shift+P → "Codespaces: Connect to Codespace..."

# GitHub CLI for Codespaces
gh codespace list
gh codespace create --repo owner/repo --branch main
gh codespace code               # Open current repo codespace in VS Code
gh codespace ssh                # SSH into codespace terminal
gh codespace stop
gh codespace delete

# Codespace environment variables
# Set in: github.com → Settings → Codespaces → Secrets
# Available as env vars in all your codespaces

# Prebuilds: pre-build codespace image for faster startup
# github.com/owner/repo → Settings → Codespaces → Prebuilds
# Triggers on push to branch, caches the post-create state

# Forward ports from Codespace
# Bottom panel → PORTS tab → Forward Port
# Set port visibility: Private (auth required) or Public
# Public URL: https://username-reponame-xxxx-PORT.preview.app.github.dev
VS Code

Snippets & IntelliSense

VS Code: Snippets & IntelliSense Custom snippets eliminate repetitive boilerplate. IntelliSense, code actions, and refactoring tools reduce the need to leave th

VS Code: Snippets & IntelliSense

Custom snippets eliminate repetitive boilerplate. IntelliSense, code actions, and refactoring tools reduce the need to leave the editor for documentation. Together they dramatically speed up development.

Creating Custom Snippets

// Open: Ctrl+Shift+P → "Snippets: Configure Snippets"
// Choose: language (typescript, javascript) or global
// File location (user snippets): ~/.config/Code/User/snippets/typescript.json

// Snippet syntax:
{
  "React Functional Component": {
    "prefix": "rfc",
    "scope": "typescriptreact,javascriptreact",
    "description": "React functional component with TypeScript props",
    "body": [
      "interface ${1:ComponentName}Props {",
      "  ${2:children?: React.ReactNode}",
      "}",
      "",
      "export function ${1:ComponentName}({ ${3:children} }: ${1:ComponentName}Props) {",
      "  return (",
      "    <div>",
      "      ${0:$3}",
      "    </div>",
      "  );",
      "}"
    ]
  },

  "useState Hook": {
    "prefix": "us",
    "scope": "typescript,typescriptreact,javascript,javascriptreact",
    "description": "useState with typed initial value",
    "body": [
      "const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState<${2:type}>(${3:initialValue});"
    ]
  },

  "Async Arrow Function": {
    "prefix": "afn",
    "body": [
      "const ${1:name} = async (${2:params}): Promise<${3:void}> => {",
      "  ${0}",
      "};"
    ]
  },

  "Try-Catch Block": {
    "prefix": "tc",
    "body": [
      "try {",
      "  ${1}",
      "} catch (error) {",
      "  const message = error instanceof Error ? error.message : String(error);",
      "  ${2:console.error(message);}",
      "}"
    ]
  }
}

Snippet Syntax Reference

// Tab stops
"body": [
  "${1:first stop}",   // Tab stop 1 with placeholder text
  "${2:second stop}",  // Tab stop 2
  "${0}"               // Final cursor position (exit point)
]

// Linked tab stops (editing one updates all with same number)
"body": [
  "const ${1:myVar} = ${2:value};",
  "console.log(${1:myVar});"  // Both ${1}s update together
]

// Transform: modify placeholder text
// Capitalize first letter:
"export function ${1:myFunction}() {}"  // Type "myFunc"
// Use transform on a copy:
"// ${1:name}\nexport class ${1/(.*)/\u$1/}Handler {}"  // → class MyFuncHandler

// Built-in variables
"${TM_FILENAME}"       // Current filename
"${TM_FILENAME_BASE}"  // Filename without extension
"${TM_DIRECTORY}"      // Directory of current file
"${TM_FILEPATH}"       // Full file path
"${CURRENT_YEAR}"      // 2025
"${CURRENT_MONTH}"     // 03
"${CURRENT_DATE}"      // 15
"${CLIPBOARD}"         // Current clipboard content
"${RANDOM}"            // 6 random hex digits
"${RANDOM_HEX}"        // 6 random hex digits

// Choice tab stop (dropdown)
"${1|option1,option2,option3|}"
// Example: HTTP method snippet
"method: ${1|GET,POST,PUT,PATCH,DELETE|},"

IntelliSense & Code Navigation

# IntelliSense triggers
Ctrl+Space           # Trigger suggestion (any time)
Ctrl+Shift+Space     # Trigger parameter hints (inside function call)
Ctrl+I               # Quick info (hover docs without moving mouse)

# Navigation
F12 / Ctrl+Click        # Go to Definition
Alt+F12                 # Peek Definition (inline, no navigation)
Shift+F12               # Find All References
Ctrl+Shift+F12          # Peek All References
F2                      # Rename Symbol (renames across all files)
Ctrl+.                  # Quick Fix / Code Action
Ctrl+Shift+.            # Focus next code action suggestion

# Go to anything
Ctrl+P                  # Go to File
Ctrl+P → @              # Go to Symbol in current file
Ctrl+P → @:             # Go to Symbol (grouped by type)
Ctrl+P → #              # Go to Symbol in workspace
Ctrl+G                  # Go to Line
Ctrl+Shift+O            # Outline view (same as @ in Ctrl+P)

# Breadcrumb navigation (top of editor)
# Click any segment to see siblings; type to filter
# Keyboard: Ctrl+Shift+; then navigate with arrows

# Multi-cursor
Alt+Click               # Add cursor at click position
Ctrl+Alt+Down/Up        # Add cursor above/below
Ctrl+D                  # Select next occurrence of selection
Ctrl+Shift+L            # Select all occurrences of selection
Ctrl+K Ctrl+D           # Skip current occurrence, select next

Code Actions & Refactoring

# Code Actions (lightbulb icon or Ctrl+.)
# Context-sensitive actions based on cursor position:
# - Fix all auto-fixable ESLint errors
# - Organize imports (remove unused, sort)
# - Add missing import
# - Generate missing interface member
# - Convert function to arrow function
# - Extract variable / Extract function
# - Infer function return type
# - Implement interface methods
# - Convert for-loop to array method

# Refactoring shortcuts
# Rename Symbol:           F2
# Move file (update imports): drag in explorer or right-click → Move
# Extract to function:     Select code → Ctrl+. → "Extract to function"
# Extract to variable:     Select expression → Ctrl+. → "Extract to variable"
# Organize imports:        Ctrl+Shift+P → "Organize Imports"
#                          Or: set "editor.codeActionsOnSave" in settings

# settings.json: auto-fix on save
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "explicit",
    "source.addMissingImports": "explicit"
  },
  "editor.formatOnSave": true,
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

# Workspace snippets (shared with team via .vscode/)
# Create: .vscode/myproject.code-snippets
# Same format as user snippets; checked into git
# Available to all team members who open the workspace

Keep your VS Code knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever