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-analyzerfor Rust.goplsfor 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.
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
| Setting | Why I use it |
|---|---|
git.autofetch: false | I prefer to decide when the editor talks to remotes. In large repositories or enterprise networks, automatic background fetches are not always desirable. |
git.enableSmartCommit: true | Useful for personal productivity when I know exactly what is staged. |
git.confirmSync: true | Safer for enterprise and shared repositories. Bypassing sync confirmation is convenient, but it can be dangerous. |
chat.disableAIFeatures: true | A 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.quickSuggestions | Keeps suggestions useful in code while reducing noise in comments and strings. |
files.watcherExclude | Reduces background file watching on generated directories, dependency trees and build artifacts. |
search.exclude | Keeps search results focused on source code instead of generated or vendored content. |
| Markdown preview settings | Useful 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 clippyandcargo test. - Go correctness should still be validated by
go test,go vet,goplsand optionallygolangci-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
| Setting | Reason |
|---|---|
rust-analyzer.checkOnSave | Keeps 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.allTargets | Checks tests and additional targets, useful when the project is not huge. |
rust-analyzer.cargo.targetDir: true | Allows 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
| Setting | Reason |
|---|---|
go.toolsManagement.autoUpdate | Convenient on personal workstations because Go tools stay fresh without repeated prompts. |
gopls.ui.semanticTokens | Improves semantic highlighting beyond basic TextMate grammar. |
gopls.formatting.gofumpt | Applies a stricter Go formatting style through gopls. |
gopls.ui.diagnostic.staticcheck | Enables 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
| Setting | Reason |
|---|---|
python.defaultInterpreterPath | Encourages 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.exclude | Prevents 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 settings | Belongs in workspace settings |
|---|---|
| Theme and UI preferences | Formatter used by the project |
| Markdown preview preferences | Python interpreter path convention |
| Generic watcher exclusions | Rust analyzer behavior for that repo |
| Terminal safety behavior | Go/gopls options for that repo |
| AI enable/disable posture | TypeScript memory tuning if project-specific |
| Editor tab limits | Test 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
- Oracle NetBeans customer case study: Suyara / Storm Interactive Technologies
- VS Code user and workspace settings
- VS Code FAQ: disabling built-in AI features
- VS Code 1.86 release notes: multi-line paste warning
- rust-analyzer configuration
- VS Code Node.js debugging
- Go in Visual Studio Code
- gopls settings
- VS Code Python settings reference
- GitHub Blog: Sunsetting Atom
- Microsoft Learn: OpenSSH on Windows