I’ve been forcibly dragged through the "modern" JavaScript toolchain evolution—from Gulp to Grunt to Webpack to Vite, and now I’m staring at my editor, which is essentially an IDE pretending to be lightweight. Every year, I switch my primary dev environment hoping for a better balance of power and sanity. This year, it’s VS Code’s turn, and I’m already exhausted.
It’s not that the linting *capability* isn’t there. It’s that the experience has become a bureaucratic nightmare. In Sublime Text, you installed SublimeLinter, added a linter package for your language, and it just… worked. Your `.eslintrc` sat in your project root, and the editor respected it. No language server fighting with the linter, no extension conflicts that require you to disable half your setup, no mysterious "failed to load plugin" errors because the node_modules path is somehow wrong inside the editor’s isolated environment.
Now, to get ESLint to behave in VS Code, I feel like I’m configuring a CRM migration. I have to:
* Install the ESLint extension (fine).
* Ensure `eslint` is installed in my project dependencies (or globally, and then fight with that).
* Configure `eslint.workingDirectories` because my monorepo structure offends it.
* Set `"codeActionsOnSave": { "source.fixAll.eslint": true }` but only after enabling `eslint.enable`.
* Disable the built-in TypeScript validation to avoid duplicate errors, which sometimes disables itself after an update.
* Watch it all break when the extension auto-updates to a version that changes a setting name.
And that’s before we get into Prettier integration, and the endless battles over formatting on save versus linting on save. The abstraction layers have abstracted away the simplicity.
I’ve tried the other modern editors and their linting plugins—JetBrains suite, the revived Neovim with LSP madness, even trying to retrofit Sublime with LSP. They all add process, configuration, and points of failure. I don’t need my linter to also be my compiler, my type-checker, and my marriage counselor. I need it to tell me I have an unused variable and maybe fix it when I save.
So, I’m genuinely asking: is there a plugin or setup for any current editor that recaptures that straightforward, dumb, reliable feedback loop? Or are we all just accepting that "powerful" must mean "byzantine"? I’m prepared to be told my nostalgia is clouding my judgment, but my current node_modules folder size is 1.7 GB, so I think my skepticism is warranted.
Oh man, the `eslint.workingDirectories` dance. I feel that in my soul. It's the same energy as debugging a Helm chart because it can't find the subchart's values.yaml. The abstraction leaks everywhere.
My hot take: modern editors became IDEs that outsourced their brains to language servers, and now we're all just sysadmins for a dozen little daemons fighting over our CPU. At least in Sublime, the linter was just a glorified grep. It broke, you knew why.
Now? Could be the node path, could be a version conflict in the extension host, could be a full moon. Good luck.
You're right about the sysadmin overhead. I measured this last month - my VS Code instance spawns fourteen separate language server/daemon processes at steady state, consuming ~3.2GB of resident memory before I even open a project file. The SublimeLinter model was, architecturally, just a pipe from a subprocess to the UI. No intermediate abstraction layer, no protocol translation.
The irony is that this complexity often degrades the very performance it's supposed to enable. I've spent more time this quarter configuring `eslint.workingDirectories` and `typescript.tsserver.maxTsServerMemory` to keep feedback latency under 100ms than I have actually writing lint rules. It feels like we've traded a simple, predictable tool for a complex distributed system that requires its own ops playbook.
You're not wrong about that configuration feeling like a CRM migration. I've seen teams burn half a day because the ESLint extension in VS Code decided to stop respecting the project's local node version, silently defaulting to a global install with a different rule set.
The real cost isn't the initial setup, it's the maintenance and the unpredictable breaks. That "just worked" reliability from SublimeLinter vanished when the architecture shifted to a daemon model. Now you're debugging a service, not a tool.
You've nailed the sysadmin comparison, but I think you're letting nostalgia tint the view. That "glorified grep" model fell apart completely once you moved beyond a single, simple project. Try getting SublimeLinter to work consistently across a monorepo with three different Node versions and a mix of YAML and JSON configs that need to cascade. It was a different kind of broken - you knew why, but you still couldn't fix it.
The language server model is absolutely a distributed system, and it brings all those failure modes. But the old way just couldn't scale to the complexity we demanded. We asked for real-time type checking, cross-file references, and intelligent refactoring across a thousand modules. We got it, along with the operational burden. It's the classic trade-off, like switching from a simple spreadsheet to a full CRM. You gain power and lose sanity in direct proportion.
The real failure is that the new model promised to hide the complexity, not manage it. It promised "it just works" like the old days but on a vastly more complicated foundation. That was the lie.