A post of mine on LinkedIn blew up recently. I said: Hot take: NPM has done more harm to web development than good. It normalized trea...
A post of mine on LinkedIn blew up recently. I said:
Hot take: NPM has done more harm to web development than good. It normalized treating 500MB of node_modules as "normal." It made devs forget how to write 10 lines of code because there's a package for that. It turned "Hello World" into a dependency tree with 847 packages. 15 years in web dev. I've seen the before and after. We traded simplicity for convenience, and got neither.
Some people agreed. Some wanted my head on a plate. One person wrote "worst take on LinkedIn today and this platform is chock-full of terrible takes". Another simply said "spoons made me fat" - liked that one - fair enough! 😅
This article isn't about being right. It's about showing that alternatives exist - simpler ways to build web apps that some people might not know about. Everything here is opinion, and relates specifically to how NPM is used for front-end development (loading CSS, JS, and other assets in your web app). Not server-side JavaScript - that's a different conversation.
Let's dive in.
Before I start throwing punches, let me give credit where it's due. Before NPM, things were... rough.
Remember loading jQuery from a CDN and hoping it was still there next month? Remember manually downloading zip files, extracting them, and copying files to your /js folder? Remember having three different versions of the same library because you couldn't keep track? I do. It wasn't fun.
NPM solved real problems:
NPM helped JavaScript grow from "that language for image rollovers" to... well, everything. It enabled an entire ecosystem. And for large teams working on complex applications, it provided structure that was genuinely needed.
So yes, NPM did good things. A lot of good things. Let's acknowledge that.
Here's where it gets spicy.
The 500MB node_modules folder became "normal." A fresh create-react-app can easily have 200MB+ of dependencies. For a Hello World (granted using a huge framework, but that's the problem - using huge frameworks for anything even presentation websites is now normal). Someone in my LinkedIn comments said "500MB? Gotta get those numbers up. If it's not 3GB+ it's probably just another todo list app". Funny because it's true.
Dependency trees went insane. 847 packages for a simple app isn't a joke - it's reality. Each package has dependencies, which have dependencies, which have dependencies... You're not just trusting one maintainer, you're trusting hundreds. Thousands, even.
The left-pad incident happened. In 2016, one developer unpublished an 11-line package from NPM, and broke thousands of projects worldwide - including Facebook and Netflix. Eleven. Lines. Of code. That could have been written in 2 minutes. But it had millions of downloads because... why write 11 lines when there's a package? I truly hope that AI will prevent people from using such small packages nowadays.
Build tools became mandatory. Webpack. Then Webpack was too slow, so Vite. Then whatever comes next. Each with its own config files, its own learning curve, its own quirks. Developers just wanted to load a CSS file. Instead, they got a 200-line webpack config and a PhD in module bundling.
Security vulnerabilities multiplied. Every dependency is a potential attack vector. Supply chain attacks are real - malicious code has been found in NPM packages. When you have 847 packages, you have 847 potential problems.
Now, before you write that angry comment... I know what you're going to say.
I heard this a lot in the LinkedIn comments. And honestly? You're partially right.
One commenter said: "Don't blame NPM for stupid developer discipline. The same bloat happens in any ecosystem with weak discipline."
Another: "NPM didn't remove the importance of architectural awareness, it made it easier to ignore it."
And my favorite: "spoons made me fat".
Fair. The tool isn't inherently evil. Developers made choices. They installed is-odd instead of writing n % 2 !== 0. They added React to a landing page. They chose complexity when simplicity would've worked.
But here's the thing: tools shape behavior. NPM made it so easy to add dependencies that we stopped asking if we should. The friction to add a package became zero. And when there's no friction... things get out of hand.
The ecosystem encouraged this. Package count became a badge of honor. "Look how many downloads my package has!" Tutorials said "just npm install this". Nobody asked "do you really need this?"
So yes, it's the developers. But it's also the system that enabled and encouraged it. Both can be true.
This isn't just me being grumpy. Ryan Dahl - the guy who created Node.js - gave a famous talk called "10 Things I Regret About Node.js". He talked about security, the module system, and yes... how things got complicated.
He went on to create Deno, which takes a fundamentally different approach: importing directly from URLs, no node_modules folder, security by default. It's not a 1:1 replacement, but the philosophy is clear: simpler is better.
If the person who started this whole thing thinks the ecosystem went off the rails... maybe it's worth listening?
Here's the part people don't know about. There are genuine alternatives. Different tools and approaches that let you avoid (or minimize) the NPM complexity. Granted these are runtimes, libraries or techniques, not drop-in replacements, but that's the point - it's possible the change has to be structural:
Created by the guy who made Node.js, as mentioned. Deno encourages you to import directly from URLs, has no node_modules, built-in TypeScript. And it's at v2.6 already!
A faster JavaScript runtime that's compatible with most NPM packages. Bun still has node_modules, but it's much faster at installing and running things. Different philosophy than Deno.
Modern browsers now support import maps - a way to map package names to URLs directly in HTML. You can load JavaScript modules from CDNs without any build step at all. It's built into the browser spec.
You can load ES modules directly from CDNs in your browser:
<script type="module">
import confetti from 'https://esm.sh/canvas-confetti';
confetti();
</script>
No NPM. No build step. Just works.
This is what I built with my team for Laravel apps. More on this below.
Each of these approaches has tradeoffs. None is perfect. But they exist! And for many projects, they might be exactly what you need.
Okay, let me tell you about Basset. It's a package we built for Laravel that takes a simple approach to the asset problem.
The concept is dead simple: instead of using NPM to download packages, just reference the CDN URL directly - and Basset will download and cache that file on your server.
{{-- Instead of npm install + importing + bundling... --}}
{{-- Just do this: --}}
@basset('https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css')
@basset('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js')
{{-- Because it's as simple as these, but with benefits: --}}
@asset('https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css')
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"></script>
That's it. Basset will:
No node_modules. No package.json. No webpack. No Vite. No build step. Just... assets. The way it used to be, but with some modern conveniences.
Philosophically, yes - both say "use URLs instead of package managers". But technically, they're different beasts. Deno is a JavaScript runtime for server-side code. Basset is a Laravel helper for front-end assets. Same philosophy of simplicity and URL-based loading, different implementation and use case.
Basset has been around since March 2023. It has 750k+ downloads on Packagist. We've been using it in production on Backpack for Laravel and countless client projects. It's not experimental - it's battle-tested.
Let me be clear about the limitations:
For complex JavaScript applications with lots of interactivity and client-side routing, you probably still need a build system. Basset is for the other 80% of web apps - the ones that are mostly server-rendered with some JavaScript sprinkled on top.
Ask yourself:
✅ Basset might be great if you:
❌ Basset probably isn't for you if:
This isn't a "NPM bad, Basset good" argument. It's about using the right tool for the job. And for many jobs, a simpler tool works just fine - that's what 15 years of web dev have taught me.
Look, the commenter was right: "spoons made me fat" is a valid response to my original take. Tools don't force us to make bad decisions. We do that ourselves.
But we also get to choose our tools. And sometimes, choosing a simpler tool leads to simpler outcomes.
NPM revolutionized web development. It also complicated it. Both things are true. The question isn't "is NPM bad?" - it's "do I need NPM for this project?"
For a lot of Laravel projects - especially admin panels, CRUD apps, marketing websites with some interactivity - the answer might be "no". You might not need 500MB of node_modules. You might not need a build step. You might be able to just... load assets from URLs and be done with it.
If that sounds appealing, give Basset a try. It's free, it's open-source, and it might just make your life a little simpler.
Or don't! Use NPM. Use Vite. Use whatever works for you. I'm not here to convert anyone. I'm just here to say: alternatives exist. And it's worth knowing about them.
Thanks for reading.
Cheers! 🍻
Subscribe to our "Article Digest". We'll send you a list of the new articles, every week, month or quarter - your choice.
What do you think about this?
Wondering what our community has been up to?