Skip to main content
Published / updated

VS Code

Before you start

You need: any project — the frontend from Track 09 or the Python from Track 13.

Time: about 50 minutes, at the keyboard.

Learning objective

Configure VS Code for the stacks you work in, navigate and edit efficiently, and debug from inside the editor.

Topics

  • Workspaces and folders
  • Extensions worth installing
  • Navigation and editing
  • Multi-cursor
  • The integrated terminal
  • Debugging
  • Settings and workspace configuration
  • Tasks
  • Diagnosing a broken setup

Workspaces

VS Code opens a folder, not a solution. That folder is the workspace, and everything — settings, extensions, the terminal's working directory — is relative to it.

school-portal/
├── .vscode/
│ ├── settings.json workspace settings, committed
│ ├── launch.json debug configurations
│ ├── tasks.json build and run tasks
│ └── extensions.json recommended extensions
├── src/
└── tests/

Commit .vscode/. It means a new developer opens the folder, accepts the recommended extensions, and gets a working debug configuration — rather than spending a morning reproducing yours.

For several related folders, a .code-workspace file groups them:

{
"folders": [
{ "path": "school-portal-api" },
{ "path": "school-portal-web" }
]
}

Extensions

Install what the stack needs and nothing more — every extension costs startup time and memory.

StackExtension
.NETC# Dev Kit (Microsoft)
PythonPython + Pylance (Microsoft)
FrontendESLint, Prettier
AngularAngular Language Service
AnyGitLens, EditorConfig, REST Client, Error Lens

Error Lens shows errors and warnings inline on the line itself rather than only in the Problems panel. It is the single change that most reduces the compile-fix-recompile loop.

REST Client runs HTTP requests from a .http file in the editor — a lightweight alternative to Postman, and the file lives in the repository:

@baseUrl = https://localhost:7099
@token = eyJhbGciOi...

### Get students
GET {{baseUrl}}/api/students?schoolId=1
Authorization: Bearer {{token}}

### Create a student
POST {{baseUrl}}/api/students
Content-Type: application/json
Authorization: Bearer {{token}}

{
"name": "Sneha Patel",
"rollNumber": "NCA-2024-0044",
"className": "9th"
}
// .vscode/extensions.json
{
"recommendations": [
"ms-dotnettools.csdevkit",
"ms-python.python",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"editorconfig.editorconfig"
]
}

VS Code prompts a new developer to install these on first open.

ActionShortcut
Command paletteCtrl+Shift+P
Go to fileCtrl+P
Go to symbol in fileCtrl+Shift+O
Go to symbol in workspaceCtrl+T
Go to definitionF12
Peek definitionAlt+F12
Find all referencesShift+F12
Go to lineCtrl+G
Navigate backAlt+Left
Find in filesCtrl+Shift+F
Rename symbolF2
Quick fixCtrl+.
Format documentShift+Alt+F
Toggle terminalCtrl+``

Ctrl+Shift+P is the one to learn first. Every command is there by name — you never need to find a menu, and typing part of what you want is faster than remembering where it lives.

Ctrl+P accepts modifiers: @ jumps to a symbol, : to a line, > runs a command. Ctrl+P then @get finds every method starting with "get" in the current file.

F2 renames semantically, updating every reference. Find-and-replace does not know the difference between a variable and an unrelated string of the same name.

Multi-cursor

The editing feature that most changes day-to-day speed.

ActionShortcut
Add cursor above/belowCtrl+Alt+Up/Down
Select next occurrenceCtrl+D
Select all occurrencesCtrl+Shift+L
Add cursor at clickAlt+Click
Column selectionShift+Alt+Drag
Expand selectionShift+Alt+Right
Move line up/downAlt+Up/Down
Copy line up/downShift+Alt+Up/Down
Delete lineCtrl+Shift+K

Ctrl+D repeatedly selects the next occurrence of the current word, adding a cursor each time. Editing five of eight occurrences and skipping three is Ctrl+D with Ctrl+K, Ctrl+D to skip — something find-and-replace cannot do.

Shift+Alt+Drag selects a rectangle, which is how you add a prefix to twenty lines at once.

The integrated terminal

Ctrl+` toggles it. It opens in the workspace folder, which is what makes relative paths work.

ActionShortcut
New terminalCtrl+Shift+`
SplitCtrl+Shift+5
SwitchAlt+Left/Right
KillCtrl+Shift+P → Kill Terminal
// .vscode/settings.json
{
"terminal.integrated.defaultProfile.windows": "PowerShell",
"terminal.integrated.cwd": "${workspaceFolder}"
}

Split terminals are how you run a backend and a frontend side by side — dotnet watch run in one, npm run dev in the other.

The terminal inherits the workspace folder, not your shell's last directory. That is usually what you want, and it explains why a relative path works here and not in an external terminal.

Debugging

// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET API",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/Api/bin/Debug/net9.0/Api.dll",
"cwd": "${workspaceFolder}/src/Api",
"env": { "ASPNETCORE_ENVIRONMENT": "Development" },
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
}
},
{
"name": "Python: FastAPI",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["school.api.main:app", "--reload"],
"cwd": "${workspaceFolder}",
"justMyCode": false
},
{
"name": "Attach to Chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}/src"
}
]
}
ActionShortcut
StartF5
Toggle breakpointF9
Step overF10
Step intoF11
Step outShift+F11
StopShift+F5

Right-click a breakpoint for a condition, exactly as in Visual Studio:

student.rollNumber === "NCA-2024-0012"

A logpoint prints a message and continues — a console.log that is not in your source and cannot be committed by accident. Right-click the gutter → Add Logpoint.

justMyCode: false lets you step into library code. Leave it true normally; turn it off when you genuinely need to see inside a framework.

serverReadyAction opens the browser when the server prints its listening line — one less manual step per run.

For a frontend, launching Chrome from VS Code means breakpoints in your .ts files work directly, with source maps handled.

Settings

Three levels, each overriding the last: User, Workspace, Folder.

// .vscode/settings.json — committed, applies to everyone
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "explicit"
},
"editor.rulers": [88, 120],
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"files.exclude": {
"**/bin": true,
"**/obj": true,
"**/__pycache__": true,
"**/node_modules": true
},
"search.exclude": {
"**/dist": true,
"**/coverage": true
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.tabSize": 4
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

formatOnSave with a committed formatter configuration ends formatting arguments. Everyone's file is formatted identically, and diffs stop containing whitespace changes.

search.exclude matters more than it looks. Without it, Ctrl+Shift+F searches node_modules and dist, returning thousands of matches in generated code — which makes the feature useless on a real project.

Language-specific blocks let Python use Black and TypeScript use Prettier in the same workspace.

The Python interpreter

Ctrl+Shift+PPython: Select Interpreter → choose the one in .venv.

This is the single most common VS Code problem in Python work. Code runs in the terminal and VS Code shows import errors, because the editor is using a different interpreter than the activated environment.

{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/Scripts/python.exe"
}

Committing that path means a new developer gets the right interpreter automatically.

Tasks

// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": ["build", "${workspaceFolder}/NexCoding.SchoolPortal.sln"],
"problemMatcher": "$msCompile",
"group": { "kind": "build", "isDefault": true }
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"args": ["test"],
"problemMatcher": "$msCompile",
"group": { "kind": "test", "isDefault": true }
},
{
"label": "watch api",
"command": "dotnet",
"type": "process",
"args": ["watch", "run", "--project", "src/Api"],
"isBackground": true
}
]
}

Ctrl+Shift+B runs the default build task.

problemMatcher is what turns compiler output into clickable entries in the Problems panel. Without it the errors are plain text in the terminal and you navigate to them by hand.

preLaunchTask: "build" in launch.json builds before every debug run, so F5 never runs stale code.

Diagnosing a broken setup

SymptomCause
Python imports fail in the editor onlyWrong interpreter selected
No IntelliSense for C#C# Dev Kit not installed, or no project loaded
F5 does nothingNo launch.json, or the wrong configuration selected
Breakpoint is hollowThe source does not match the running build
Formatting does nothingNo formatter for that language, or formatOnSave off
ESLint not reportingExtension installed but no config file found
Search returns thousands of resultssearch.exclude not configured
Terminal is in the wrong directoryOpened a file, not a folder

A hollow breakpoint is the informative one. It means the debugger cannot map the source to the loaded module — usually stale build output, or the wrong project launched. Rebuild and check preLaunchTask.

Output panel → the relevant extension's channel shows why an extension is not working. The C# and Python channels report the exact reason far more often than the Problems panel does.

Ctrl+Shift+PDeveloper: Reload Window fixes a surprising amount, and it is faster than restarting.

VS Code versus Visual Studio

VS CodeVisual Studio
WeightLight, fast startHeavy
PlatformWindows, macOS, LinuxWindows (VS for Mac retired)
.NET supportGood, via C# Dev KitBest in class
Frontend supportExcellentAdequate
PythonExcellentAdequate
DesignersNoneWinForms, WPF
Profiling and diagnosticsBasicExtensive

Use Visual Studio for heavy .NET work — a large solution, WinForms or WPF, memory profiling. Use VS Code for frontend, Python, scripts and full-stack work where you want one editor across several stacks.

Most teams use both, and there is no need to choose.

Errors you will hit

What you seeCauseFix
Breakpoint is hollowNo matching source, or the wrong launch configCheck launch.json
Python import fails only in VS CodeWrong interpreterCtrl+Shift+P → Python: Select Interpreter
Extension does nothingNot enabled for this workspaceCheck the Extensions panel
Formatting differs between machinesNo committed settingsCommit .vscode/settings.json
Debugger will not attachWrong port or processCheck launch.json

Commit .vscode/settings.json and extensions.json. A new developer then gets the right formatter and the right extensions on first open.

Common mistakes

  • Opening a file instead of a folder
  • Not committing .vscode/
  • The wrong Python interpreter selected
  • Installing dozens of extensions
  • Not configuring search.exclude
  • No problemMatcher, so errors are not clickable
  • No preLaunchTask, so F5 runs stale code
  • console.log instead of logpoints
  • Not knowing Ctrl+Shift+P exists
  • Find-and-replace instead of F2
  • Formatter configured per developer rather than in the workspace

Practice

The course exercise is navigate files and use terminals.

  1. Open a project folder — not a file — and confirm the terminal's working directory.
  2. Install the extensions for your stack and create .vscode/extensions.json.
  3. Use Ctrl+Shift+P to run five commands you would normally find in a menu.
  4. Find a file with Ctrl+P using a partial name, then a symbol with Ctrl+P @.
  5. Navigate a call chain with F12 and return with Alt+Left.
  6. Rename a symbol with F2. Then rename another with find-and-replace and find what broke.
  7. Use Ctrl+D to edit five of eight occurrences, skipping three with Ctrl+K, Ctrl+D.
  8. Use Shift+Alt+Drag to add a prefix to twenty lines at once.
  9. Split the terminal and run a backend and a frontend together.
  10. Write launch.json for your stack. Debug with a breakpoint.
  11. Add a conditional breakpoint for one specific record.
  12. Add a logpoint and confirm it prints without editing the file.
  13. Create a hollow breakpoint by editing after building. Identify why, then fix it with preLaunchTask.
  14. Write tasks.json with a problemMatcher. Introduce an error and click through from the Problems panel.
  15. Enable formatOnSave in workspace settings and confirm a colleague's clone formats identically.
  16. Search without search.exclude and count the results. Add it and repeat.
  17. In a Python project, select the wrong interpreter deliberately and observe the import errors.
  18. Write a .http file with REST Client and call an API endpoint.

Exercises 13 and 17 are the two setup problems that waste the most time.

You can now

  • Configure a workspace so a new developer gets a working setup
  • Write a launch.json for .NET, Python and Chrome
  • Use multi-cursor editing and the integrated terminal
  • Select the right Python interpreter
  • Set a logpoint instead of adding a print statement

Review questions

  1. Why commit the .vscode folder?
  2. What does a hollow breakpoint indicate?
  3. Why does search.exclude matter on a real project?
  4. What is the most common cause of Python import errors appearing only in the editor?

Next: SQL Server and SSMS