
JavaScript Modularity Shaming - fogus
http://swannodette.github.io/2015/01/06/the-false-promise-of-javascript-microlibs/?utm_source=dlvr.it&utm_medium=twitter
======
imslavko
Same people who shame other's work for not being modular enough for their
taste, would worship a one-line module[0] that is modular down to every
subroutine.

[0]: [https://github.com/blakeembrey/is-upper-
case/blob/master/is-...](https://github.com/blakeembrey/is-upper-
case/blob/master/is-upper-case.js)

~~~
mwhite
Wow. I bet these could even be grouped into one module with minimal size
overhead by using some higher-order functions or something.

[https://github.com/blakeembrey/swap-
case](https://github.com/blakeembrey/swap-case)

[https://github.com/blakeembrey/is-upper-
case](https://github.com/blakeembrey/is-upper-case)

[https://github.com/blakeembrey/title-
case](https://github.com/blakeembrey/title-case)

[https://github.com/blakeembrey/snake-
case](https://github.com/blakeembrey/snake-case)

[https://github.com/blakeembrey/dot-case](https://github.com/blakeembrey/dot-
case)

[https://github.com/blakeembrey/path-
case](https://github.com/blakeembrey/path-case)

[https://github.com/blakeembrey/camel-
case](https://github.com/blakeembrey/camel-case)

[https://github.com/blakeembrey/param-
case](https://github.com/blakeembrey/param-case)

[https://github.com/blakeembrey/upper-case-
first](https://github.com/blakeembrey/upper-case-first)

[https://github.com/blakeembrey/lower-
case](https://github.com/blakeembrey/lower-case)

[https://github.com/blakeembrey/upper-
case](https://github.com/blakeembrey/upper-case)

[https://github.com/blakeembrey/pascal-
case](https://github.com/blakeembrey/pascal-case)

[https://github.com/blakeembrey/is-lower-
case](https://github.com/blakeembrey/is-lower-case)

[https://github.com/blakeembrey/constant-
case](https://github.com/blakeembrey/constant-case)

~~~
mattdesl
To illustrate the problem: my dom-css module[1] needs a method to convert to
camel case.

I could copy-paste from stack overflow, but I would have to maintain/test that
function. I'd rather depend on something.

I could depend on a big "string-utils" library, but that would probably carry
some useless baggage, and its scope may change or grow over time. Further,
maybe it has a competitor module with a vehement following (like lodash vs
underscore), which makes my module less appealing to them. This sucks for my
module because I just want that one function.

The better alternative is just to depend on the exact, clearly named "to-
camel-case" function which is very unlikely to ever change or grow in scope.

[1][https://npmjs.com/package/dom-css](https://npmjs.com/package/dom-css)

~~~
nostrademons
If the to-camel-case function never changes or grows in scope, then you don't
need to maintain or test it.

If it does change, then you need to integrate & test your dependencies, same
as if it were part of your own codebase.

I think this point is often overlooked by the "I'll use someone else's code
because I don't want to maintain it" folks. Code that is frozen doesn't rot:
if you never change a piece of code and don't change its dependencies, it will
continue working. Similarly, usually the reason you'd in-source a library is
so you can change it at-will without getting some other maintainer to accept
your patches. Most of the time, the effort that you save by using 3rd-party
code is exactly equal to the time it would take to write & debug the library
to get it to its current state, minus the time spent hunting for, learning,
and integrating the library. Both of these are pretty easy to estimate when
it's a one-line function; does it take you longer to find and npm install that
one-line function than it does you to write a line of code?

~~~
williamcotton
There's a cohesive social element to "many small modules" that fosters a
community.

Module authors work with and respect each other.

 _Most of the time, the effort that you save by using 3rd-party code is
exactly equal to the time it would take to write & debug the library to get it
to its current state, minus the time spent hunting for, learning, and
integrating the library._

There are plenty of small functions that might be a little trickier than is
first imagined. Time isn't the only issue. There's also much less cognitive
overhead in the two minutes it takes to search npm, figure out the interface,
install the module, and use it. I'd much rather outsource my yak shaving.

------
city41
I'm a fan of ClojureScript and one reason being how easy it makes it to take
advantage of the Closure compiler. But it can be a double edged sword. At
times the advanced compilation mode breaks your code, and it can be
_extremely_ difficult to track down why. The combination of symbol renaming
and dead code elimination usually means final JavaScript that looks utterly
nothing like the ClojureScript you started out with.

Just today I had an issue where the Closure compiler decided my call to (set!)
wasn't necessary, and so it removed it, completely breaking my app. The best
solution I could find was a work around involving a pretty large let block. I
was just happy to get it working, debugging optimized Closure compiled code is
not fun at all.

~~~
swannodette
Debugging advanced compiled Closure in ClojureScript is actually pretty
straightforward if you know which knobs to turn - :pseudo-names true, :pretty-
print true build options are pretty much all you need.

I'm somewhat skeptical about a set! being eliminated unless you were setting a
property of an object from a random JavaScript library you haven't provided an
extern for. The above compiler settings would have showed this immediately.

~~~
city41
I did not know about :pseudo-names, that looks to be quite helpful. Thanks.

This is the method I was dealing with:
[https://github.com/city41/bookends/blob/master/site/src/cljs...](https://github.com/city41/bookends/blob/master/site/src/cljs/demo/knex.cljs#L6)

In that let expression I'm digging my way down into a native JS object to
monkey patch it. The equivalent (set! (.. knex -client -Runner -prototype
-debug) ...) was being completely removed as far as I can tell. Same with an
equivalent aset. That channel no longer got set up and my app stopped working.
If I simply put that method back to set!, then it breaks again, very
reproducible. When I added in a (.log js/console "hello") to the method, I
could see that console log inlined where the method call was, but no other
remnant of the method could be found.

I will play with :pretty-print and look into creating a small repro of what
I'm seeing. But if I don't succeed with a smaller repro, then at the very
least this app is already quite small.

EDIT: here is a little gist showing the difference I'm seeing if anyone is
curious:
[https://gist.github.com/city41/12099b91e0fbb526b0dc](https://gist.github.com/city41/12099b91e0fbb526b0dc)

~~~
swannodette
Yes this code is exactly the problem I described above - you are not supplying
the required Closure externs file for Knex.

~~~
city41
Ok, thanks for the help. I still can't seem to give it an externs file that
makes the compiler happy. But I'll look into it on my own.

------
nwienert
Relevant discussion thread on github[1].

Max Ogden's last comment irks me. After a long and fairly productive
discussion, he generalizes / calls out people, and doesn't explicate on what
he's trying to point out.

[1]
[https://gist.github.com/substack/68f8d502be42d5cd4942](https://gist.github.com/substack/68f8d502be42d5cd4942)

~~~
sehr
It's not exactly out of character for 'high profile' node community members is
it.

I love the work they're doing, and the community they've fostered might be one
of the most progressive in the industry, but they can be insanely pretentious
at times.

(irony of responding to a comment about generalization noted)

------
juandopazo
This is an area where the new syntax for modules in EcmaScript 6 does really
well. Since exports are static, a dead code elimination tool can figure out
which exports from a module are being used and remove the ones that are not
being used. As mentioned in comments about how the Closure Compiler works, you
can't really do this with purely dynamic code, but you can do it with static
imports/exports.

------
serve_yay
This is a strange post, it's about two completely separate things.

I do agree with the "modularity shaming" idea though, the JS community does
seem to be overzealous about that. However I think it better to err in that
direction than the other direction.

------
ianbicking
Is the Closure Compiler specifically good at dead code elimination in Closure
libraries? Theoretically I would imagine that it could be applied to anything,
though I'm sure there are many Javascript tricks that would make dead code
detection ineffective.

~~~
swannodette
Closure Compiler requires authors to write code in a very specific static
style. It's somewhat tedious, but for some libs worth the effort. In the case
of ClojureScript we can of course automate the tedium for you through the
compiler :)

For random JS libs Closure can't really do much better than Uglify.

~~~
nostrademons
Yeah, I tried for a couple years to get Google Search to adopt JQuery, but
JQuery without dead-code elimination would've doubled the SRP latency, and
JQuery with dead-code elimination doesn't actually eliminate any dead code.
The problem is that it's written in a style that dynamically assigns methods
to the JQuery prototype, and so it's not possible to analyze which code
actually exists in the library without executing the whole of the library.

~~~
dmethvin
You could build a custom jQuery copy that eliminated whatever methods you
don't need. Since plugins use unknown parts of jQuery and Closure Compiler
can't always detect that, you really need to do some manual labor to pull in
what you need.

In the 1.8 time frame the jQuery devs made a call for people who wanted to use
Closure Compiler's ADVANCED_OPTIMIZATIONS to participate, but just didn't get
any community interest. CCAO style is not typical JavaScript and it doesn't
seem that a lot of people use it.

~~~
nostrademons
At that point you're better off just writing the functionality you need from
scratch - which was basically the coding standard when I joined Google Search
in 2009. They've since moved to allow Closure, which was a big win for
developer productivity, but OTOH the latency of the SRP has roughly quadrupled
since I joined Google.

Anyway, I'm firmly in the "you don't need JQuery" camp now, since most of its
functionality is trivially implementable in one-liners since IE9+. I'm
unlikely to go back; I've been using vanilla JS for prototypes for the last 2
years and am using it for my startup now, and have yet to encounter a case
where I really miss JQuery. It was a very different world in 2006 when I
learned it, or even in 2009 when I joined Google.

Also, I suspect that the overlap of people that use Closure and those that use
JQuery is near zero, hence why nobody in the JQuery community cared about
CCAO. Both of them are highly non-typical Javascript, as is Angular. JS shows
its Scheme heritage in that somehow every major framework results in
completely different, mutually-incomprehensible code.

~~~
swannodette
Not sure why you got downvoted. Thanks for the perspective on usage of Closure
at Google.

------
amelius
Why don't we just let browsers treat javascript files as modules, just like we
have shared libraries on our OS?

This way, libraries such as JQuery, could be cached, and shared between
websites.

~~~
lomnakkus
You can sort-of already do that with CDNs.

Browser still has to parse+run all of that JS though. Because JS-in-browsers
is antimodular in that all previous JS on a given page influences all
subsequently loaded JS, even if using just script tags with a URL. In a way
this is very similar to the pains experienced by the C++ community for many
years now.

~~~
amelius
Could you elaborate on the C++ part?

~~~
DiThi
C++ doesn't have a "load this module" statement. I does have an "include all
the contents of X file here", and file X has to deal with possible duplication
and cyclic dependencies, usually with something like

#ifndef _SOME_FILE_H #define _SOME_FILE_H // code goes here #endif

Also any "module" can define any global variable, so name clashes (esp. with C
libraries) are possible. Most languages with proper modules the "global"
variables are module-wide.

An equally nasty issue happens with JS, where many libraries must be loaded in
the proper order and not duplicated. There are a bunch of module systems but
none is part of the spec. The good thing is that module systems are easy to
make thanks to closures.

------
thomasreggi
I had a twitter convo with David DeSandro about this exact thing today
[https://twitter.com/thomasreggi/status/552903079142883329](https://twitter.com/thomasreggi/status/552903079142883329).
I've really taken to projects like
[https://github.com/tjmehta/101](https://github.com/tjmehta/101) where you are
forced (there's an error if you include the index) to include the files /
functions that you need directly. He raised some good points.

------
skybrian
Of course GWT and Dart also do dead code elimination (also known as tree-
shaking). There's a reason Google's JavaScript-targeted compilers implement
this.

On the other hand, the consistent dedication to writing tiny libraries in
JavaScript ecosystem is not a bug. Tree-shaking compilers make it somewhat
harder to tell where the bloat is coming from, and that encourages people
writing libraries to get sloppy.

~~~
swannodette
My experience so far is that writing code that defeats tree shaking is
somewhat more difficult when adopting the simple functional style of the
Closure Library base libs. Fortunately for ClojureScript, this style is
already idiomatic.

------
mattdesl
I responded a while back in another thread about "small modules" in npm.
Filesize is only one (rather small) facet of why some people prefer this
approach to the "put everything under the same hood".

[https://news.ycombinator.com/item?id=8830058](https://news.ycombinator.com/item?id=8830058)

------
zubairq
[https://www.youtube.com/watch?v=dtnQ3Mwp0KQ](https://www.youtube.com/watch?v=dtnQ3Mwp0KQ)

