Hacker News new | ask | show | jobs
by spellboots 4501 days ago
These complaints ring true for trivial javascript projects, however if you are building complex javascript heavy applications the equation quickly changes.

Point 1: You will almost always be transpiling your javascript. In any serious project, you will want to have lots of small files to ease development, and then concatenate into larger files to ensure optimal http loading. If you don't already do this regardless of modules, you probably should.

Point 2: If you are intentionally or serendipitously relying on script execution order, that is your bug. The module loader has helpfully exposed a bug in your code for you.

Point 3: If you have lots of modules, it is simpler to deal with a module system than mashing together a load of scripts.

This complaint should come with a severe disclaimer that it only applies to trivial applications, because as soon as you start building something complicated the arguments are just plain wrong.

Software engineering is hard, and robust software engineering practices usually only pay off once you get beyond trivial examples. The ones that work, however, pay off massively once you do use them for more complex use cases.

5 comments

> You will almost always be transpiling your javascript. In any serious project, you will want to have lots of small files to ease development, and then concatenate into larger files to ensure optimal http loading. If you don't already do this regardless of modules, you probably should.

I think he was alluding to browserify and the many AST transformation preprocessors it inspired[1], many are just there to deal with other AST transformations....

You don't need a module loader or packager or whatever to concat or order your scripts, tools like Webassets or Sprockets can do that without any new JS code. Hell, a simple Makefile that calls `cat` does a far better job than most stuff out there.

> If you are intentionally or serendipitously relying on script execution order, that is your bug. The module loader has helpfully exposed a bug in your code for you.

I call nonsense on this one. Scripts almost always have to execute in order. Can you run a jQuery plugin without having jQuery loaded and executed first?

> If you have lots of modules, it is simpler to deal with a module system than mashing together a load of scripts.

It may be simpler to deal with files, but definitely not simpler to deal with JS module loaders. I suspect that the current proliferation of JS module loaders / packagers are due to the long time lack of tools on the PHP side to provide something like Sprockets and the fact that JS has no import/export mechanism built into the language. Tools like RequireJS/Browserify/Bower/Component are all abominations in their own way.

[1]: https://github.com/substack/node-browserify/wiki/list-of-tra...

You can blame the people in charge of the ES spec. that's the single most important thing to have in any language,how to properly import external files. Having to rely on the DOM to do that is a horrible hack. Requirejs is a DOM library,not a javascript library.
I think the question is where you want to say that the jQuery plugin should load before jQuery. If you have 20 jQuery plugins, which cross-leverage some of the same dependencies, and then all rely on jQuery -- do you want to rely on ordering script tags for dependencies in the HTML? Or do you want to leverage a module system that lets you coherently state what the dependencies are?

Also I'm not sure how Bower got dragged into this since it has nothing to do with module systems. It is a nice tool though for just tracking what third party libraries are in a project and their versions. Not sure what they did to prove to be an "abomination" :-)

You can declare dependencies outside of JS. Sprockets does this. Linearizing a dependency graph is pretty easy in most languages.

Bower got dragged is because it manages dependencies while providing no other help whatsoever. It's also frequently used with RequireJS/AMD. So the obvious question becomes, why can't they get in a room and make a baby that isn't a horrifying monstrosity that is the RequireJS config file?

You can have external JavaScript dependency tracking, but you're not solving the problem, youre just moving it from one place to another.

If you want to manually manage it (or manage it outside of js), you can... but complaining that there are various tools that automate that process seems pretty nonsensical to me. Tools that automate things are good.

... besides, what are you even talking about. Requirejs config isn't that bad. If you want to grumble we can start with grunt files and (ugh) painfully repurposed make files.

Solving the problem requires ES6 modules and friends to take over this entire space. Which might not happen in a while.

Lot of tools automate things, few do it well.

ReqireJS isn't that bad? Compared to what? Maven's pom.xml?

Agreed. On the front-end, module loaders eventually come in handy but their utility isn't immediately useful. If an "anti-pattern" is something that seems useful but eventually isn't, and a "pattern" is something that seems useful and immediately is, we need a phrase for this third state, where it's a pain in the ass to start with and only becomes useful later.
If an "anti-pattern" is something that seems useful but eventually isn't, and a "pattern" is something that seems useful and immediately is, we need a phrase for this third state, where it's a pain in the ass to start with and only becomes useful later.

Technical investment, by analogy with technical debt?

I disagree that it's really even that hard to start with. It's like any library or framework. You have to learn it initially, but then it's just not that hard. I've been using RequireJS for a couple years as well as node.js and none of the complaints really resonated with me.

I would go so far as saying that the author has probably been using them wrong. If you follow the right patterns and write good code it should be a non-issue.

For those devs, like myself, who are unfamiliar with large javascript projects, do you have a link to an open-source javascript-heavy project that fits your points?
Could you elaborate on Point 2? What causes this bug, and how do you fix it? I'm looking for a low level explanation rather than, "Use a Module Loader" because I want to fundamentally understand what's going on when my code depends on load order and how to fundamentally fix it.
Unlike some languages, javascript files execute as they are loaded. In large projects relying on js files to take actions beyond the most basic definitional type things is usually a mistake because it means that there is evolving global state (always difficult to keep track of) with implicit semantics. It's very distressing to move some code around in the include order for an unrelated reason and then suddenly find that hundreds of unit tests start breaking with bizarre errors.

The way to avoid this is to limit your js files to definitions, and then finally kick everything off with a single call. Module loaders enforce this, but you can follow this advice without them.

It's reasonable to have one file that must be loaded first - this provides the mechanisms you're going to use to define your objects and nothing more. Then everything else can be done in whatever order seems good, before finally kicking off the action. Even registering code with factories and registries should be left until the end. There are some ordering requirements that are legitimate - if you inherit from another class, then the super class will typically need to be ordered before the subclass.

If you're doing a small project where you don't mind having up to 10 script tags in a page, then you'll do better following the 'separate definition from execution' advice than not. You'll do better still using some form of module loader that makes concerns of order obsolete and will let you scale. I like browserify, but there are lots of options, ranging from the very simple to the complex.

> Could you elaborate on Point 2? What causes this bug, and how do you fix it?

Unless @async is specified, the scripts linked in a page will be executed in that order. This means it's easy to unknowingly create implicit (and possibly unexpected) dependencies between scripts.

Because a module loader will perform asynchronous loading and execution of scripts, if dependencies are left unspecified the loading order is essentially random (mostly driven by the script's source size and how fast the server happens to respond to that request). A module loader requires that dependencies be explicitly spelled out, or changes are the application will randomly blow up at loading.

More like: point 1) source maps