Profile portrait
Tia Zanella
SKHELL

Posted Jun 18, 2026

From NetBeans to VS Code

A personal view of how IDEs evolved from enclosed development environments to distributed toolchains, plus an opinionated VS Code configuration strategy for modern multi-language workspaces.

Before today’s extension-driven editors, mcp’s, AI panels, remote containers and integrated terminals, the IDE was a more complete, clean and enclosed world.

I grew up with NetBeans, then moved through IntelliJ IDEA, Atom and eventually landed on Visual Studio Code, each tool represented a different moment in the philosophy of software development.

NetBeans gave us a complete cross-platform IDE that worked well out of the box, IntelliJ IDEA pushed deeper language intelligence and refactoring. Atom showed how far a hackable editor could go when the UI itself became programmable. VS Code changed the model again: the editor became an orchestration layer around language servers, extensions, debuggers, terminals, source control, remote environments and, more recently, AI-assisted workflows.

Back in 2014, Oracle published a customer case study about Suyara, a PaaS data storage platform I founded when I was at Storm Interactive. At that time, our development requirements were very clear: speed, stability, collaboration, source control, debugging, code navigation, diff tooling, visual customization and consistent behavior across macOS and Linux workstations.

Those requirements have not really changed in twenty years what changed is the architecture of the toolchain.

The IDE did not disappear, it became distributed.

Today, when I am working from home, travelling to Milan, writing or reviewing specifications, having fun with Go, experimenting with Rust, maintaining npm projects, or using Python, I still want the same thing I wanted in 2014: fast access to the workspace, a clear view of the project, a reliable development loop and no unnecessary noise between the engineer and the code.

The difference is that in 2026 the editor is not a single monolithic environment anymore rather it is a composition of moving parts and that is why configuring VS Code correctly matters.

Not because VS Code is slow by definition. Not because Electron is automatically bad. Not because the terminal is always better even if still vim is my favorite, but because a modern editor can become inefficient when we forget that every extension, language server, file watcher, indexer, formatter, linter, debugger and AI feature has a cost on the underline hardware.

The goal is not to make VS Code as minimal as Vim, if that is the objective, use Vim directly.

The goal is to brainstorm and decide what belongs to the editor, what belongs to the workspace and what must stay delegated to the language toolchain.

The IDE became a distributed system

The classic IDE bundled everything together: project model, compiler integration, source control, debugger, search, refactoring, UI customization and sometimes deployment tooling.

VS Code is different, the IDE itself is closer to a coordination layer. The actual intelligence often comes from external processes:

  • rust-analyzer for Rust.
  • gopls for Go.
  • TypeScript server for JavaScript and TypeScript.
  • Pylance or another language server for Python.
  • ESLint, Prettier, Ruff, Black, mypy, Clippy, Cargo, Go tools, npm scripts and test runners.
  • Git, either through the integrated Source Control view or directly from the terminal.

This architecture is powerful because it is modular, it is also dangerous somehow even within thhe IDE itself as every component can watch files, index folders, start background processes, scan dependencies or react to every save.

A practical decision model

Before touch anything I invite to usually think in few layers.

flowchart TB subgraph Env ["1. Environment context"] C["Do I work alone, in a team, hybrid or Enterprise-controlled environments?"] C --> C1["Personal projects"] C --> C2["Team repositories"] C --> C3["Enterprise-controlled environments"] end subgraph Role ["2. Role"] B["What is my role?"] B --> B1["Frontend developer"] B --> B2["Backend developer"] B --> B3["Architect"] B --> B4["Technical writer"] B --> B6["System or network engineer"] end %% Connect Environment selections to the Role evaluation C1 & C2 & C3 --> B %% Connect Role selections to the Configuration Strategies B1 & B2 & B3 & B4 & B6 --> D & E & F subgraph Global ["3. Global strategy"] D["What should be global?"] --> D1["Editor behavior"] & D2["Terminal safety"] & D3["Generic watcher exclusions"] & D4["AI and privacy posture"] end subgraph Workspace ["4. Workspace strategy"] E["What should be workspace-specific?"] --> E1["Language server behavior"] & E2["Formatter and linter"] & E3["Runtime path"] & E4["Build and test workflow"] end subgraph Toolchain ["5. Toolchain delegation"] F["What should stay delegated to the language toolchain?"] --> F1["cargo check / clippy"] & F2["go test / gopls"] & F3["npm scripts / TypeScript"] & F4["pytest / ruff / mypy"] end

This is the real distinction that matters. Global strategy should express how I use the editor. Workspace settings should express how a specific repository works. Language toolchain settings should stay close to the language ecosystem, not hidden behind editor magic.

What should be global?

Global settings should be stable across projects. For me, that means editor ergonomics, terminal behavior, generic search and watcher exclusions, Markdown preview preferences and my default posture around AI features.

These are settings I am comfortable applying to most of my development environments.

{
  "git.autofetch": false,
  "git.enableSmartCommit": true,
  "git.confirmSync": true,

  "chat.disableAIFeatures": true,

  "terminal.integrated.env.linux": {},
  "terminal.integrated.enableMultiLinePasteWarning": "auto",

  "workbench.editor.limit.enabled": true,
  "workbench.editor.limit.perEditorGroup": true,
  "workbench.editor.limit.value": 10,

  "editor.inlineSuggest.enabled": true,
  "editor.suggest.quickSuggestions": {
    "other": "on",
    "comments": "off",
    "strings": "off"
  },
  "editor.suggest.delay": 50,

  "files.watcherExclude": {
    "**/__pycache__/**": true,
    "**/.astro/**": true,
    "**/.pytest_cache/**": true,
    "**/.venv/**": true,
    "**/bin/**": true,
    "**/coverage/**": true,
    "**/dist/**": true,
    "**/env/**": true,
    "**/node_modules/**": true,
    "**/pkg/**": true,
    "**/target/**": true
  },

  "search.exclude": {
    "**/.astro": true,
    "**/.pytest_cache": true,
    "**/.venv": true,
    "**/coverage": true,
    "**/dist": true,
    "**/node_modules": true,
    "**/target": true
  },

  "markdown-preview-enhanced.mermaidTheme": "dark",
  "markdown-preview-enhanced.revealjsTheme": "black.css",
  "markdown-preview-enhanced.codeBlockTheme": "darcula.css",
  "markdown-preview-enhanced.previewTheme": "github-dark.css",
  "markdown-preview-github-styles.lightTheme": "dark",
  "markdown.extension.toc.levels": "1..3"
}

Why these settings matter

SettingWhy I use it
git.autofetch: falseI prefer to decide when the editor talks to remotes. In large repositories or enterprise networks, automatic background fetches are not always desirable.
git.enableSmartCommit: trueUseful for personal productivity when I know exactly what is staged.
git.confirmSync: trueSafer for enterprise and shared repositories. Bypassing sync confirmation is convenient, but it can be dangerous.
chat.disableAIFeatures: trueA deliberate choice for focused, privacy-conscious or enterprise-controlled workflows.
terminal.integrated.enableMultiLinePasteWarning: "auto"Safer than "never". Multi-line paste is convenient, but it can also execute more than expected in a terminal.
workbench.editor.limit.*Prevents tab sprawl and reduces memory pressure during long sessions.
editor.suggest.quickSuggestionsKeeps suggestions useful in code while reducing noise in comments and strings.
files.watcherExcludeReduces background file watching on generated directories, dependency trees and build artifacts.
search.excludeKeeps search results focused on source code instead of generated or vendored content.
Markdown preview settingsUseful because my work often mixes code, architecture notes, Mermaid diagrams and technical documentation.

A very important detail: I do not globally exclude **/.vscode/** from watchers in a recommended baseline.

The .vscode folder is often where project-specific settings, tasks, launch profiles and extension recommendations live. Excluding it globally may be convenient in some edge cases, but it can also hide useful workspace behavior and make team troubleshooting harder.

What should be workspace-specific?

Workspace settings should describe the repository. This is where I configure language servers, formatter behavior, interpreter paths, test runners, project-specific exclusions and anything the team should share.

For example, a Python repository may need a .venv convention. A Rust repository may decide whether rust-analyzer runs cargo check or cargo clippy on save. A Go repository may define gopls behavior. An npm project may need TypeScript server memory tuning.

These settings belong in:

.vscode/settings.json

only when they help the project and the team. They should not become a private dumping ground for personal editor preferences.

What should stay delegated to the language toolchain?

This is where many VS Code setups become messy.

The editor should not become a hidden CI system.

For example:

  • Rust correctness should still be validated by cargo check, cargo clippy and cargo test.
  • Go correctness should still be validated by go test, go vet, gopls and optionally golangci-lint.
  • npm projects should still expose scripts through package.json.
  • Python projects should still define their testing and linting stack explicitly: pytest, ruff, black, mypy, pyright, or equivalent tools.

VS Code should help me run those workflows. It should not make them invisible.

Two habits I still see too often on colleagues running Windows

I regularly see two patterns in enterprise environments.

The first is engineers still relying on PuTTY for every SSH session, completely bypassing the fact that modern Windows environments natively support OpenSSH and ~/.ssh/config-style workflows from Windows 10 back in April 2018 with it’s release 1803. Beyond just dropping a legacy tool, native OpenSSH is the critical bridge to VS Code’s Remote Development capabilities, by leveraging the Remote SSH extension, you can securely access remote Linux servers or network infrastructure, edit files, execute terminal commands and run debuggers directly over the SSH tunnel, all without leaving the IDE workspace.

The second is people using Notepad++ as their default technical editor for everything even when they are managing complex activities in a confused way of working with multiple tabs instead of an organized integrated workspace.

This is not a criticism of PuTTY or Notepad++ and is not the intention of this post, both tools had and still have valid use cases. The issue I see is always when we use something forget about the neighbor and the evolution of the technology we completely ignore opportunities of improve us and in team and remain with obsolete methods because of habit, it’s like comparing Nirsoft pinginfoview to pingtrace.

If your work is repository-driven, cross-platform and heavily reliant on remote infrastructure, your editor needs to understand the workspace and the network architecture, not just colorize some local text file.

Rust optimize rust-analyzer, not only VS Code

When writing Rust, the heavy process is usually not VS Code itself, I saw the cost comes from rust-analyzer, Cargo metadata, build scripts, procedural macros, dependency analysis and checks triggered on save. For normal Rust projects, for example I like this configuration:

{
  "rust-analyzer.checkOnSave": true,
  "rust-analyzer.check.command": "clippy",
  "rust-analyzer.check.allTargets": true,
  "rust-analyzer.cargo.targetDir": true
}

Why

SettingReason
rust-analyzer.checkOnSaveKeeps diagnostics close to the editing loop.
rust-analyzer.check.command: "clippy"Uses Clippy as the check command instead of plain check, giving stronger feedback.
rust-analyzer.check.allTargetsChecks tests and additional targets, useful when the project is not huge.
rust-analyzer.cargo.targetDir: trueAllows rust-analyzer to use a dedicated target subdirectory, reducing lock contention with manual Cargo commands at the cost of extra build artifacts.

For large Rust workspaces, I would make it less aggressive:

{
  "rust-analyzer.checkOnSave": false,
  "rust-analyzer.check.allTargets": false
}

Then I run checks explicitly:

cargo check
cargo clippy --all-targets --all-features
cargo test

The key point is the IDE is not the build system. For small and medium projects, immediate feedback is excellent, for very large workspaces, manual control can be more predictable.

Node, npm and Astro

In JavaScript, TypeScript, npm and Astro projects, the main enemies of editor performance are usually:

  • node_modules
  • generated output
  • framework caches
  • coverage reports
  • TypeScript server memory
  • automatic debugger attachment
  • too many frontend extensions running at the same time

For npm and Astro projects, I usually start from this:

{
  "typescript.tsserver.maxTsServerMemory": 4096,

  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/dist/**": true,
    "**/coverage/**": true,
    "**/.astro/**": true
  },

  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/coverage": true,
    "**/.astro": true
  },

  "debug.javascript.autoAttachFilter": "onlyWithFlag"
}

The most important line for debugging is:

{
  "debug.javascript.autoAttachFilter": "onlyWithFlag"
}

This prevents the debugger from attaching to every Node.js process launched in the integrated terminal, when I want debugging, I explicitly start the process with --inspect or --inspect-brk.

Example:

node --inspect-brk ./server.js

For me, this is the right balance: debugging is available, but it does not become background noise.

Go

With Go, the center of gravity is gopls, the official Go extension uses gopls for IntelliSense, navigation, symbols, diagnostics and formatting. That is a good thing, it means the editor is delegating language intelligence to the Go language server.

For personal local development, I like:

{
  "go.toolsManagement.autoUpdate": true,
  "gopls": {
    "ui.semanticTokens": true,
    "formatting.gofumpt": true,
    "ui.diagnostic.staticcheck": true
  }
}

Why

SettingReason
go.toolsManagement.autoUpdateConvenient on personal workstations because Go tools stay fresh without repeated prompts.
gopls.ui.semanticTokensImproves semantic highlighting beyond basic TextMate grammar.
gopls.formatting.gofumptApplies a stricter Go formatting style through gopls.
gopls.ui.diagnostic.staticcheckEnables Staticcheck analyzers through gopls. Useful, but potentially more expensive.

For enterprise, CI-aligned or reproducible environments, I would avoid automatic tool changes:

{
  "go.toolsManagement.autoUpdate": false,
  "go.toolsManagement.checkForUpdates": "local"
}

This is not about being conservative for no reason, it is about reproducibility, a personal workstation can move fast viceversa a shared engineering environment should be predictable.

Python

Python can make VS Code feel slow when the language server starts analyzing virtual environments, dependency trees, generated folders, notebooks, caches or large data-oriented repositories.

My preferred baseline in this case will be:

{
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv",
  "python.analysis.diagnosticMode": "openFilesOnly",
  "python.analysis.typeCheckingMode": "basic",
  "python.analysis.exclude": [
    "**/__pycache__",
    "**/.pytest_cache",
    "**/.venv",
    "**/dist",
    "**/build"
  ]
}

Why

SettingReason
python.defaultInterpreterPathEncourages a local .venv convention inside the project.
python.analysis.diagnosticMode: "openFilesOnly"Keeps diagnostics focused on files I am actively editing.
python.analysis.typeCheckingMode: "basic"Provides useful type feedback without going immediately into strict mode.
python.analysis.excludePrevents caches, virtual environments and build outputs from becoming analysis targets.

For serious Python libraries, I would probably increase strictness and make type checking part of CI, for scripting, automation, JSON parsing, reporting, local tools and experiments, basic is often the right balance.

Git editor integration or terminal?

I do not think there is a universal answer, sometime I use one over the other sometime both, the VS Code source control view is excellent for reviewing changes, staging individual hunks, checking diffs and handling many everyday operations.

But if I am already working heavily in the terminal git commands and gitk solve practically all my needs and I do not necessarily need additional Git extensions on top of the built-in Git support, I think many teams add too much visual tooling around Git stopping at the GUI level while still failing to teach the basic command-line model.

My rule is simple:

  • Use the integrated Git view when it improves review and visibility.
  • Use the terminal when the operation is clearer as a command.
  • Avoid installing Git extensions just because they look nice.
  • Do not hide Git fundamentals from junior engineers.

For shared repositories, I keep sync confirmation enabled for personal projects, I may relax it.

AI features useful but they should be intentional

AI assistance can be useful and I consider help us a lot bringing ideas to life in days rather than months or years that play an amazing role for prototype or showcase ideas quickly among team and I believe they are becoming part of the modern engineering workflow but AI should be intentional not AI everywhere.

I do not want AI features to silently reshape every editor session for example, that is why I like having a single explicit switch in my global profile:

{
  "chat.disableAIFeatures": true
}

This does not mean is bad, it means the editor should behave intentionally. When I want AI, I want to know that I enabled it and probably never happen as I use it directly in cli so as I do not want background panels, inline prompts or assistant behaviors inside every workspace I leave it disabled.

My opinionated rule for extensions

The extension ecosystem is the strongest part of VS Code and also its most common source of performance problems, there are extension for everything.

The problem is installing too many extension that do too many similar things instead of installing just some one that resume what’s missing by IDE to enhance your productivity or viceversa enhance IDE capabilities.

The extension I made for fun called Net Commander and here for VS Codium Net Commander it’s aimed for that goal, not to be perfect but to inglobe many things in one extension instead of an arsenal of several ones.

I use a simple rule: every extension must justify either language support, project understanding, documentation quality, debugging, or measurable productivity.

If an extension is only cosmetic, duplicated, abandoned, or constantly running in the background, it should probably be disabled or scoped to a specific workspace.

The most useful command for this is:

Developer: Show Running Extensions

That command tells you which extensions are active and how much they cost, if VS Code feels slow, I do not start by blaming VS Code, I start by inspecting deep what I asked it to load.

User settings vs workspace settings

One of the most important VS Code practices is to separate personal preferences from repository behavior.

I use this mental split:

Belongs in user settingsBelongs in workspace settings
Theme and UI preferencesFormatter used by the project
Markdown preview preferencesPython interpreter path convention
Generic watcher exclusionsRust analyzer behavior for that repo
Terminal safety behaviorGo/gopls options for that repo
AI enable/disable postureTypeScript memory tuning if project-specific
Editor tab limitsTest runner and launch configuration

A good .vscode/settings.json should help every contributor open the repository and work correctly. It should not force my personal theme, font, UI preferences or shortcuts onto the team.

The best IDE is the one you understand

The best IDE is not the one with the most features, it is the one that gives you the most control without hiding the engineering model from you. We have come a long way since the days of monolithic Java IDEs. But whether I was navigating PHP and Subversion conflicts in NetBeans in 2014, dialing with Go binaries today on projects like OSIRIS JSON, learning Rust, managing npm frontends or using Python for local automation, the goal has not changed.

I want an environment that adapt to me easy, VS Code can be that environment, but only if we configure it with intention.

References and further reading

menu
home
fingerprint
rocket
newsstand