Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
NPM and Left-Pad: Have We Forgotten How to Program? (haneycodes.net)
1725 points by df07 on March 23, 2016 | hide | past | favorite | 854 comments


Holy moly-- is-positive-integer/index.js:

  var passAll = require('101/pass-all')
  var isPositive = require('is-positive')
  var isInteger = require('is-integer')

  module.exports = passAll(isPositive, isInteger)
I retract my previous statements that Javascript programmers are going down the same enterprise-y mess that Java programmers went down a decade ago.

They've already taken it to an entirely different level of insanity.


This comes up a lot when people discuss anything related to npm modules. It's easy to simply dismiss these trivial one-line modules as "insanity" and move on, but there's actually plenty of good reasons as to why many prefer to work with multiple small modules in this manner. This GitHub comment by Sindre Sorhus (author of over 600 modules on npm) is my favorite writeup on the topic:

https://github.com/sindresorhus/ama/issues/10#issuecomment-1...

TL;DR: Small modules are easy to reason about, and encourage code reuse and sharing across the entire community. This allows these small modules to get a tremendous amount of real world testing under all sorts of use cases, which can uncover many corner cases that an alternative naive inlined solution would never have covered (until it shows up as a bug in production). The entire community benefits from the collective testing and improvements made to these modules.

I also wanted to add that widespread use of these small modules over inlining everything makes the new module-level tree-shaking algorithms (that have been gaining traction since the advent of ES6 modules) much more effective in reducing overall code size, which is an important consideration in production web applications.


Small modules are easy to reason about

Yes they are, in the same way that a book in which every page consists of a single word is easier to understand than one with more content per page.

By focusing on the small-scale complexity to such an extreme, you've managed to make the whole system much harder to understand, and understanding the big picture is vital to things like debugging and making systems which are efficient overall.

IMHO this hyperabstraction and hypermodularisation (I just made these terms up, but I think they should be used more) is a symptom of a community that has mainly abandoned real thought and replaced it with dogmatic cargo-cult adherence to "best practices" which they think will somehow magically make their software awesome if taken to extremes. It's easy to see how advice like "keep functions short" and "don't implement anything yourself" could lead to such absurdity when taken to their logical conclusions. The same mentality with "more OOP is better" is what lead to Enterprise Java.

Related article that explains this phenomenon in more detail: http://countercomplex.blogspot.ca/2014/08/the-resource-leak-...


Perhaps another reason for this is that Javascript is inherently unsafe. By relying on these small rigorously tested libraries they are avoiding the need to test their own code, and thus avoiding basic null check or conversion errors.

Other languages handle this with compilers, may be strongly typed, and/or have features that don't allow nulls. Javascript doesn't exactly have that luxury. So maybe it makes sense in Javascript, where it wouldn't in other languages (though one could argue this is a flaw in the language design).

edit: to be clear I'm not really defending the practice, but trying to give a little perspective.


> By relying on these small rigorously tested libraries they are avoiding the need to test their own code, and thus avoiding basic null check or conversion errors.

In practice, many, if not most, of these one-line modules don't even work correctly, and it is difficult to get people to care to collaborate on fixing them instead of just replacing them with a new one-line module that works slightly better as the concept of complex collaboration to perfect one line of code is so foreign to a generation of developers who would rather just throw code to the world and never look back (an issue made all the worse as tiny bugs in these one-liners can almost seem dangerous to fix when you aren't really sure how they are being used: maybe it actually is better to not fix anything and just have people rely on the replacement better module :/).


Odd, the ones I use all have tests, thousands of downloads, and active bug trackers. If I reinvented the wheel, or copy pasted, I wouldn't get those things.


Doing a for loop is not equal to re-inventing the wheel.

Words and phrases have lost their meaning nowadays.

By the JS community's standards if I wanted code to capitalize the 3rd or sometimes 4th letters of a string, they would be 2 different npm modules.

Maybe they didn't forget how to program, as the article implies; maybe they never knew how to program in the first place.


Writing your own solution, when you could use a common widely known solution, exactly matches both writing your own left pad and inventing your own wheel.

> By the JS community's standards if I wanted code to capitalize the 3rd or sometimes 4th letters of a string, they would be 2 different npm modules.

If there was, it would have been published and people would be depending on it. Instead, people use the stdlib first, then big lego bricks like underscore, then (when they don't want the bulk of big lego bricks) smaller lego bricks. A single capitaliseMap() might be in there but it's a much more specific case than padding.


A 1-line of code module matches your needs exactly just for today. When you have 100 such modules which don't quite work and don't quite need after a month(quite a long time for a JS project to exist untouched), then your dependency hell shows its ugly head.

If you had years of experience in a wide array of technologies and languages, you'd be aware of that which is, and has been, obvious to the rest of us for the past 2-3 decades.


> A 1-line of code module matches your needs exactly just for today.

Nothing about function length determines utility. There are many one lines that match many people's needs repeatedly.

> When you have 100 such modules which don't quite work

This is worrying. How are you picking the modules you use? 100x popular modules from npm - with git repos, unit tests, READMEs, and hundreds of other users - beat 100x functions implemented in-house for NIH (or more likely, 100x functions copy pasted from Stack Overflow).

> If you had years of experience in a wide array of technologies and languages

Please don't assume things about other people. It's very rude, and it makes you look bad when you're wrong.


Worry not my friend. I'll take the risk of being wrong. Up to now, it seems that there's consensus on the fact that the JS/frontend world is the worst offender when it comes to engineering robust software.

At least there are JS programmers that seem aware of it and agree that this has to change. So there's still hope I guess.


> JS/frontend world is the worst offender when it comes to engineering robust software

I completely agree. I still can't understand why the language that we depend on for increasingly large-scale front-end work still works like this:

[1, 10, 2, 21].sort() // returns: [1, 10, 2, 21]

It's 2016, software engineering is purported to be a profession, and we still have the worst tools imaginable.


Yep. And very large software developers have created languages that compile to javascript ... it is madness but it solves their problems.


Try

.sort(sorts.numerical)

Using npms 'sorts' module


I think the only thing you highlighted is why weakly typed languages have issues with readability. Looks like it sorted it fine to me.


I think there are people who are aware of the problem, but that problem is people reinventing the wheel a thousand times, and relying on copypasta and needless work rather than package management.


But surely the really concerning part is despite their very wide usage, these dependecies turned out in some cases to be poorly coded functions indeed. Throughout this whole thread are examples where these one or two line "modules" have poor runtime performance, miss key cases in the one single thing they claim to do, and in the very worst cases, those small "modules" themselves are depending on many more "modules".

Diseases turtles all the way down.


> Throughout this whole thread are examples where these one or two line "modules" have poor runtime performance, miss key cases in the one single thing they claim to do, and in the very worst cases, those small "modules" themselves are depending on many more "modules".

Where? I've only seen the exact opposite: folk who don't believe in package management write their own naive 'reinvent the wheel functions' with those issues. Nobody I can see is quoting actual npm modules.


The OP's example ( is-positive-integer) has tests, and is only three lines long, and is on major release version 3.X, because despite all that, it had serious problems in versions 1 and 2.


"Serious problems in versions 1 and 2" suggests that it might be a good idea not to write your own version 1.

But to be explicit about the history: version one[0] had dependencies. Version two[1] had no dependencies, and a comment on github suggests that it might be wrong but I can't immediately see how. Version three[2] is a different implementation, and adds another export.

[0] https://github.com/tjmehta/is-positive-integer/commit/3003f9...

[1] https://github.com/tjmehta/is-positive-integer/commit/b47e90... - it should return false for 0, so I'm not sure what the commenter is getting at

[2] https://github.com/tjmehta/is-positive-integer/commit/3cdfe1...


Case in point: the left-pad package has quadratic complexity.


Depends on the JS implementation in question. And I think node uses one that optimizes string concatenation, possibly making that the fastest way to do it.


Yeah, as far as I know in modern JS engines += is actually faster than using an array.


[citation needed]

It's really nice how we once again depend on "I think some guys do it like this" for performance. What happened to standards?

What's next? SEO for code?


The equivalent in C would then be replacing

#include <math.h> and "-lm"

with

#include <sin.h> #include <cos.h> #include <tan.h> #include <sinh.h> …

and "-lsin -lcos -ltan -lsinh …"

Which is nuts no matter what language you are coding in.


The difference with C is that with a good linker, functions you don't call are removed from the resulting binary (or are maybe not even in the binary if you are dynamically linking to the c runtime).

With javascript however, if you import a hypothetical "math.js" instead of just "sin.js", "cos.js" or "tan.js", then you'll need to download and evaluate a whole bunch of javascript that you might not need.

I'm not defending javascript, because I dislike it and avoid it where possible, but I can understand some of the reasons why short, specific modules came about.


Webpack 2 (probably the closest thing to a JS linker) can do that same tree shaking.


what you describe is the "-flto" option in GCC and I believe the equivalent in JS is donr by Google Closure compiler.


He described not Link Time Optimization (-flto), but the very basic linker functionality: to only include required functions. C/C++ has a weird compilation model where each source file is translated to machine code separately, with placeholders for unknown function addresses. Thus it is trivial to take only required functions.

-flto, on the other hand, allows to reverse this process to allow interprocedural optimization across different translation units.


@TickleSteve below me: "without this, removal is only performed within the compilation-unit"

Not quite. When you link to a static library (.a) with lots of .o object files in it, only those object files will be linked that are actually used by your program.

I first learned about this when I looked at the source of dietlibc, and wondered about every function being in a separate file. That enables the aforementioned trivial optimization, even without -flto.


I saw that discussion years ago about the G++ String implementation. That was a big .o file.


Thanks for the correction. Didn't know that.


He's describing unused-function removal... which "-flto" is the correct option to use (on GCC). like you say, without this, removal is only performed within the compilation-unit, but my statement was quite correct.

@majewsky: what you're describing is more akin to "-ffunction-sections" and "-gc-sections" which will split ewach function into a separate section and garbage-collect those sections.


> #include <sin.h> #include <cos.h> #include <tan.h> #include <sinh.h>

I've written worse - at least those cover multiple variations each (or overloads in C++ for float/double/std::complex?/...)

While I'm not a fan of the enforced java route of 1 file = 1 class, I do trend towards 1 file ~ 1 (main, public) thing - which might be a single function with no overloads. #include <string_left_pad.h>? Better than #include <everything.h>, which I see far too often...

I don't have to figure out which grouping of things my coworkers decided to throw something into if I can just #include by name - whereas aggregation headers more often than not will trigger a full project file-system search.

Unnecessary #include s don't accumulate nearly so much when you don't have to audit 100 functions to see if any of them use features from said header.

I don't trigger a rebuild of everything when the only things that #include stuff actually need it.

Lots of benefits!

> and "-lsin -lcos -ltan -lsinh …"

Or sin.o, cos.o, tan.o, sinh.o... which suddenly sounds a lot more reasonable. I wouldn't bat an eye at string_left_pad.o either.

Sure, I want to use tools to simplify the use of those micro-modules by aggregating them into libraries for convenience. But that's not a knock against micro-modules - just their packaging and tooling. Rewind time back far enough and I would've been manually specifying .o files on the command line and kvetching too...


Java doesn't enforce 1 file = 1 class but rather 1 file = 1 public class, which is exactly what you asked for. You can put as many private classes in the file as you want.


This isn't accurate either. Java does not enforce 1 file = 1 public class but rather 1 file = 1 public top-level class.

For example this is totally legit:

  // ClassA.java
  public class ClassA {

    public static class ClassA_Inner_Public_Static {

    }

    public class ClassA_Inner_Public {

    }

  }

  // ClassB.java
  public class ClassB {

    ClassA classa = new ClassA();
    ClassA_Inner_Public classA_Inner_Public = new ClassA().new ClassA_Inner_Public();
    ClassA_Inner_Public_Static classA_Inner_Public_Static = new ClassA_Inner_Public_Static();

  }


Not exactly - I didn't ask for enforcement, and the irritation usually arises from wanting the occasional secondary type - such as an enum - to be part of the public interface (thus forcing the type itself to be public, naturally) without either:

1) moving it into a separate file, for the simplest two value one liner

or

2) making it interior to the other class, with all the verbose lengthy names (and semantic implications) that this might entail.

I want clear and concise names, so I lean towards #1. And if an enumeration gets more shared between multiple classes, or collects enough values and comments to merit it, sure, I'll move it into it's own file. But forcing me to shove it in a separate file before I've even decided on the best name (meaning I need to rename the file repeatedly as well), and before I'm even certain I'll keep that specific enumeration, is just meaningless friction.


So have libmath depend on libsin, libcos, libtan and libsinh. People who want the kitchen-sink version can get it. People who want just one specific submodule can depend on that. What's not to like?


Having a full set of math functions isn't kitchen sink, it's the right level of granularity for a module. If I want math functions I really want them to all be written by the same author and kept in lock-step as a unit.

Why don't you want the whole module? Because it's bloat? Surely it's far less bloat than one submodule per function?


A lot of javascript is going to be deployed to a website.

Every line of code that isn't used is bytes you're sending to every user unnecessarily

> Surely it's far less bloat than one submodule per function?

How is that bloat? It's the same LoC whether it's 1000 modules or 1 after it's been compiled/minified


Bloat in the source code, build system, dependency management, overheads per submodule, complexity.

I'm not arguing that there shouldn't be a math module but that you shouldn't split it up into such (imho) crazy small parts.

Closure Compiler can remove any unused functions.


It's not bloat in the source code - if you don't care which pieces you depend on you declare a dependency on math, if you do care you list the specific pieces.

It shouldn't be (impactful) bloat or overhead in the build system or dependency management. That stuff should just handle it.

Most of the time you don't care and can just declare the dependency on math and let the closure compiler handle it. But sometimes it might be really important that a project only used cos and not sinh, in which case you want to enforce that.


Ooops. Upvoted you by mistake. Why would it be so important?


Maybe you're running on an embedded processor that can do sin/cos/tan with high performance but sinh performance is bad. Maybe part of your project needs to depend on an old version of sinh with a correctness bug so that you can reproduce existing calculations, so you don't want any other parts of the code depending on sinh. Maybe your legal team doesn't want you using sinh because they believe the implementation might infringe some patent.


In the compiled artifact there might not be a difference. But developers tend to ignore the issues outside their scope. So yes, for a developer it might not make much difference. For people who need to make sure that you can compile and deploy, it's much more complexity.


If basic, bog-standard functionality was built into the standard library, then it's not bloat that you have to deal with. 500 kb or a meg or two for a decent, full-featured standard javascript library isn't going to even make a dent in the install sizes of web browsers, even on awful mobile devices.


There's a cognitive load to every additional thing you need to know to use a module. At a certain point, added flexibility becomes more trouble than it's worth.


But there is no cognitive load to having hundreds of modules in a crazy tree of dependencies?


What's not to like? The fact that every one of those dependencies is an attack vector when one of those package's maintainers gets compromised / project hijacked / bought off by some black hat operator.

It's easier to keep an eye on a small number of trusted parties than 200 random strangers.

You thought this SNAFU was bad...


That has nothing to do with how granular packages are. The npm is broken as it allows such things. Look at this spectacular example of providing high security for users: https://www.npmjs.com/package/kik

Someone has to do this manually?! If the package is not popular, no one cares? What happens if I send them an email and provide the same package with the same API (not trademarked and MIT licensed) but break it a bit on every update?

No one knows.


when those packages are not under your control, it has everything to do with how granular they are and by extension how many you depend on and thus have to trust/verify.

when was the last time you rechecked the entire source of a package you depend on after updating it?


> What's not to like?

That depends; in the case of mathematics libraries they have to be bundled together because of functional dependencies. It's either that or ignoring DRY principles -- which if you do that, you're ignoring the entire point of what breaking things into submodules is intended to do.


Separate interface and implementation. cos and sin might well use common underlying code (by depending on the same module), but they should expose orthogonal interfaces.


Oh, but javascript is dynamically typed, so it doesn't matter if your sin-module works with a different kind of arbitrary-precision decimal number module than your geolocation module, your cosine module or your openg-draw-triangle or opengl-draw-circle modules... /sarcasm


It's not sarcasm if it's true!


OT but apt to this community - That's what I thought, but after a lifetime of 'correcting' people, I looked up the definition of sarcasm. Turns out that the sarcastic-sounding truths I'd been deploying for decades were indeed by-the-book sarcasm rather than 'I can't believe it's not sarcasm'.

Key quote from brilliant word-talking-guy Henry Fowler: "Sarcasm does not necessarily involve irony [but it often does] ... The essence of sarcasm is the intention of giving pain by (ironical or other) bitter words."

So, we circle back to Node.js by way of pain and bitterness. Sounds about right.


> basic null check

What's funny to me is that leftpad(null, 6, ' ') outputs " null".


Am I right that len(" null") here is 5, so it's working incorreclly?


I think the joke here is that javascript has a long history of thinking that this:

    null
is the same as this:

    "null"
...Which it isn't.


Thought it was a typo on my part... but HN seems to be trimming running spaces.


HN isn't: your browser is. Runs of whitespace are collapsed in HTML to single spaces unless specifically styled otherwise. You can verify that they're there by viewing the page source.


I like to use non-breaking spaces for that kind of thing, although they still might collapse that? At least it doesn't wrap non-breaking spaces.


No, they wouldn't collapse that: non-breaking spaces aren't considered whitespace in HTML. The side effect of that is that if you copy some text with non-breaking spaces, you'll get non-breaking spaces, so something that looks as if it contains spaces won't necessarily behave as such. In HN, if you need to quote something where space is significant, you're best off formatting it as a preformatted block by indenting it:

    Here is    some    preformatted text.
It might break up the flow of the text, but if something like whitespace is significant, that's probably a good thing.


I think it's a very valid (and concerning) point. JavaScript is made in way that one fears to implement isPositive or isArray.


> Yes they are, in the same way that a book in which every page consists of a single word is easier to understand than one with more content per page.

A more apt metaphor would be separating a book into many small paragraphs that each serves a single purpose makes the book easier to understand. Regardless, the book metaphor misses a lot of the nuance.

Of course taking the approach to an extreme would be detrimental. However, a vast majority of these small modules that are actually widely used consist of functions that may seem trivial at first sight, but actually contain a lot of corner cases and special considerations that a naive inline implementation could miss.

Sure, there are also plenty of trivial one-line modules on npm that don't fit into a such a description, but those are simply side effects of the unprecedented popularity of the platform and its completely open nature, and shouldn't be used to infer any kind of general trend towards "hypermodularisation" because very few reasonable developers would ever import them into their projects.


> A more apt metaphor would be separating a book into many small paragraphs that each serves a single purpose makes the book easier to understand

No, that would not be an apt metaphor for the problem he is describing.

> Regardless, the book metaphor misses a lot of the nuance.

Not if you are trying to understand the point he is trying to make.

Is there any cost to creating modules, uploading them to npm and using them in other projects ? Clearly there is, as is shown by the world-wide breakage from yesterday. This is probably only one of such failure modes.

The point is, breaking everything into micro-modules can have benefits and costs. Ultimately, it is an engineering trade-off.

Now, it is possible that npm modules are at the right level of abstraction, even though other module systems in the world are not this granular. If this is due to the special nature of JavaScript then the point must be argued from that perspective.


> No, that would not be an apt metaphor for the problem he is describing.

That's because I don't agree that the problem he is describing exists, or at least not to the degree he's describing, which was full of hyperbole.

> Is there any cost to creating modules, uploading them to npm and using them in other projects ? Clearly there is, as is shown by the world-wide breakage from yesterday. This is probably only one of such failure modes.

This was a failure on npm's side by including a functionality that allows users to trivially remove packages from a package management system that is used by hundreds of other packages, something that most major package management systems have decided was a bad idea.

> The point is, breaking everything into micro-modules can have benefits and costs. Ultimately, it is an engineering trade-off.

Agreed. And I don't think nearly as many people are erring on the extreme side of this tradeoff to the degree that he's describing.


> This was a failure on npm's side by including a functionality that allows users to trivially remove packages from a package management system that is used by hundreds of other packages, something that most major package management systems have decided was a bad idea.

That is emphatically not the problem. The author of those modules could have just as easily modified the code instead of deleting it:

    function leftpad(str, len, ch) {
        // NPM won't let me delete modules, so here you go
        return "";
    }
Now you'd have an even harder time figuring out what happened to your code that you did if the module just disappeared. What you're asking for is basically for the repository to adopt a policy of "all maintainers must be given free rein to get their modules correct and keep them maintained, but they shouldn't have the ability to do anything that causes problems downstream" which is impossible and frankly silly.

The problem is the dependency, not the precise mechanism by which that dependency screws you when it goes bad.


You can't republish with the same version number, so you would be protected if you're pinned to a specific release.


At which point you don't get bug fixes. This is an old problem and there is no good solution.


You get bug fixes when you upgrade, deliberately.


This is what everyone should do ideally, but that means your solution is conscientiousness, and good luck spreading that across all your dependencies.


> for the problem he is describing.

Because it is a strawman.


> By focusing on the small-scale complexity to such an extreme, you've managed to make the whole system much harder to understand

Well put. I've noticed a curious blind spot in how people account for complexity: we count the code that 'does' things much more than the code that glues those things together. This distorts our thinking about system complexity. A cost/benefit analysis that doesn't consider all the costs isn't worth much.

An example is when people factor complex code into many small functions, then say it's much simpler because smaller functions are easier to understand. In fact this may or may not be true, and often isn't. To get a good answer you must consider the complexity you've added—in this case, that of the new function declarations and that of the calls to them—not just the complexity you've removed. But it's hard to consider what you don't see. Why is it so easy not to see things like this? I think it's our ideas about programming, especially our unquestioned assumptions.

The complexity of glue code goes from being overlooked to positively invisible when it gets moved into things like configuration files. Those are no longer seen as part of the system at all. But of course they should be.


I just saw this insanity a couple of days ago:

https://youtu.be/l1Efy4RB_kw?t=343

He refactored a Java application into 1400 classes!


And to add to the mess, until npm introduced deduplication every dependency recursively included it's own dependencies as sub folders, so you get a bajillion tiny dependencies over and over (and sometimes with different versions to boot).

Frankly some of the comments I'm seeing really do reinforce his point: people really don't know how to program. I suspect this is it the flipside of Javascript being popular and accessible - people can churn out products without really knowing what they are doing...


Sorry for the nitpick, but "hyperabstraction"?

left-pad isn't even slightly abstract.


It's not.

The hyperabstraction is in how even tiny functions like this, isPositive, isArray, etc. are being abstracted and turned into individual modules.


Again, maybe I'm nitpicking, but turning something into modules doesn't seem like abstraction... you could call it encapsulation, or modularisation, or maybe extraction.

But abstraction? I don't see how that word is connected to what's happening. Maybe you can explain it.


    Small modules are easy to reason about
They really aren't, when I'm reading is-positive-integer(x) and wonder if 0 is positive I need to hunt down the definition of positive through two packages and as many files. And it gets wrose if both your code and one of your dependencies required 'is-positive-integer' and I have to also figure out which version each part of the code base is using.

If you had written (x > 0) I would have known immediately, it also wouldn't be doing the same thing as is-positive-integer(x) but how many calls to is-positive-integer are actually correct in all the corner cases that is-positive-integer covers?

And then there's the other problem with dependencies: you are trusting some unknown internet person to not push a minor version that breaks your build because you and Dr. is-positive-integer had different definitions of 'backwards compatibility'.


JS does not have an Integer type, so (x > 0) does not work. Now if you add all those checks necessary to see whether you're actually dealing with an integer here, you get to a point where you cannot be sure anymore whether there aren't any weird type coercion issues there and need to start writing tests. Suddenly your whole positive integer check took 1h to write and you still cannot be sure whether there's something you didn't think of. I'd rather use an external library for that that's properly unit tested.


A module doesn’t solve that kind of type confusion, though. Static typing does. The next best thing in JavaScript is passing consistent types of values to your functions, so just write

  function isPositiveInteger(x) {
      return x > 0 && Number.isInteger(x);
  }
and always pass it a number. (Of course, this shouldn’t even be a function, because JavaScript is confused about the definition of “integer” – maybe you really want `x >>> 0 === x` – and apparently is-positive-integer is confused about the definition of “positive”, so it’ll be clearer to just put things inline.)


> A module doesn’t solve that kind of type confusion, though. Static typing does.

That's the root of all the problems, though, isn't it? JavaScript is just a terrible language.

Actually, that's not really fair. It's a great little language for writing late-90s Dynamic HTML, maybe just a little too powerful. And that additional power enables people to build cathedrals of cruft in order to Get Things Done.

I don't like Scheme (Lisp is IMHO better for real-world, non-academic work), but I still wonder how much things would have been if Eich had been allowed (or had chosen) to implement a small Scheme instead of JavaScript. The world of web software engineering would be so profoundly better.

Maybe WebAssembly will save us all.


There are already some quite advanced compilers that treat JavaScript itself as just a web assembly language, you don't technically have to wait for WebAssembly. TypeScript (some type safety) PureScript (Lots of type safety) and GHCJS (MOUNTAINS of type power) all work right now, though the latter is still experimental grade.

But I don't think an initial choice of implementing a Scheme would have helped. The idea of +0 vs 0 vs -0 could just have easily happened in a Scheme, same too for the reliance on stringy types. Those are symptoms of a rushed design and an unwillingness to accept the temporary sharper pain of invalidating existing bad code to produce a new better standard (the exact same tendency - to dig in deeper rather than risk the pain of turning back - is literally how spies managed to convince people to commit treason).

Then of course there's also the great risk that, just like Scheme not-in-the-browser, Scheme-in-the-browser might never have widely caught on.


> There are already some quite advanced compilers that treat JavaScript itself as just a web assembly language, you don't technically have to wait for WebAssembly.

Yeah, there's even a Common Lisp which compiles to JavaScript …

> The idea of +0 vs 0 vs -0 could just have easily happened in a Scheme, same too for the reliance on stringy types.

I don't necessarily know about these specific examples: the Scheme standards have been quite clear about their numeric tower and equality standards.

I think your general point about the hackiness which was the web in the 90s, and the unwillingness to break stuff by fixing things holds, though. And of course it wasn't just the web: I recall that the Makefile syntax was realised to be a mistake weeks into its lifetime, but they didn't want to fix it for fear of inconveniencing a dozen users (details fuzzy).

> Then of course there's also the great risk that, just like Scheme not-in-the-browser, Scheme-in-the-browser might never have widely caught on.

I dunno — would a prototypal language have ever caught on were it not for the fact that JavaScript is deployed everywhere? I can imagine a world where everyone just used it, because it was what there was to use.

And honestly, as much as I dislike Scheme, it would have fit in _really_ well with JavaScript's original use case and feel. And if the world had had a chance to get used to a sane, homomorphic programming language then maybe it might have graduated to a mature, industrial-grade, sane, homomorphic language.

But alas it never happened.


It's easy to say things like this. It's a lot harder to actually suck it up and live with what happened. Try to make progress, remember that's what all of the offenders in this story are trying to do. Whether or not it is perceived like that by the community.


Only comment worth up-voting I could find.


I would swap the expressions on each side of the '&&' around. While JS doesn't care (as it'll apply '>' to just about anything, even in strict mode), this is perhaps a stronger statement of intent:

    return Number.isInteger(x) && x > 0;
Thus the more general assertion (is it an integer) guards the more specific specific one (is it greater than zero).


Another question is whether you'd want to include negative zero as a positive integer.


x >>> 0 changes a negative integer to an unsigned (positive) 32-bit integer. You can have integers greater than 32 bits. They're represented internally as floats. Bitwise operators make 32-bit assumptions, with the top bit as a sign bit, unless you do x >>> 0, in which case you can revert a sign bit overflow back to a positive integer. If you have a positive integer that's greater than 32 bits, x >>> 0 === x fails. The expression (84294967295 >>> 0 === 84294967295) is false.

Don't even get me started on negative zero.


I thought all numbers in Javascript were represented as 64-bit floats. Therefore you can do correct integer math up to 53 bits without getting into too much weirdness (bigger than that and the weirdness is guaranteed).

I didn't know the >>> operator converts to 32-bit. How does it do that? Mask off the top bits? Or give you a NaN if it's bigger?

But (and someone please correct me if I'm wrong), at the end of the line it's still represented as a 64-bit float internally and accurately.


The module checks that the positive integer is less than Number.MAX_SAFE_INTEGER constant, while yours would lead to unsafe comparisons if we trust its validity as a number.


The name of the function, though, isn’t `isPositiveSafeInteger`; it matches `Number.isInteger` instead.

Or maybe you don’t just want safe integers, maybe you want `x >>> 0 === x`, or `x > 0 && (x | 0) === x`, or…. This is what I meant about JavaScript being confused about the definition of “integer”.


    JS does not have an Integer type, so (x > 0) does not work
It depends on what the surrounding code actually does wheter (x > 0) works or not. Depending on the version of is-positive-integer you are using a positive instance of the class Number may return true, false or cause an error.

PS. never mind that the implementation of is-positive-integer is extremely inefficient.


>TL;DR: Small modules are easy to reason about, and encourage code reuse and sharing across the entire community.

How does that even remotely applies to the "is positive integer" test, and even more so to "it's positive" and "it's integer"?

What's next? is-bigger-than-5? word-starts-with-capital-letter? add-1-to-a-number?


Not defending javascript because I dislike it immensely, but with javascript being a dynamically typed language, and with the way it handles implicit conversions between types, and with trickiness around NaN, Infinity and null objects, there are sufficient edge cases that writing a correct function to test if some variable contains a positive integer is not as trivial as it is with more sane languages, and is likely to trip up even experienced developers if they miss one or more of those edge cases.

Having pre-written modules that account for all edge cases that have been battle-tested by hundreds, thousands, or even millions of users does have merit.

The main problem here is with the Javascript language, which makes code evolve to things like 'is-positive-integer'.


(That's the whole point of using a dynamic language, what '>' actually does depends on the surrounding code. If you want '>' to type check you should be using a type safe language to begin with.)

The issue I have with this is readability. Type '>' and I know exactly what it does, I know what implicit conversions are involved, and how it would react to a null object. Type 'isPositiveInteger' and I need to check. I can not read your code fluently anymore.


What if sometimes I want the overloaded, type relevant '>', and sometimes I want to do '>' if the inputs are positive integers, and raise an exception otherwise?


If you want to build is-bigger-than-5 yourself, I recommend pulling in fivejs[1] as a dependency.

[1] https://github.com/jackdcrawford/five


five.js was missing something. I fixed it. https://github.com/jackdcrawford/five/pull/234


I laughed way too hard at this :)

Well played.


Did you add dist/five.js to commit by mistake?


Nope, that was on purpose.


No way!


Lucky for you, I just made yeonatural.js, project template generator that can instanciate fully amd compatible <n>.js.

We're doing a GSoC for efficient templating of real number though.


This module is a joke, right? Right?


I would have bet on "yes" until a day ago.


Yes it's a joke. In fact it's a joke on this very matter.


Why is English the only language in which "Five" is capitalized?


Five would only be capitalized at the beginning of a sentence.

You wouldn't write: "I saw Five of my friends last night."


add-1-to-a-number

This seems to be a very common example in educational materials about how to create a function in some programming language. Perhaps they should all be prefaced with "NOTE: you should not actually make such a function, because its triviality means that every time you use it you will be increasing the complexity of your code", or a longer discussion on when abstraction in general is appropriate and when it is not.

Ditto for any other abstraction --- I've found there's too much of an "abstraction is good so use it whenever you can" theme in a lot of programming/software design books and not enough "why abstraction can be bad" sort of discussion, which then leads to the "you can never have too much" mentality and the resultant explosion of monstrous complexity. Apparently, trusting the reader to think about the negatives and exercise discretion didn't work so well...


> Apparently, trusting the reader to think about the negatives and exercise discretion didn't work so well...

That's the whole point. If you use is-positive-integer, you won't have to think about the negatives!


It doesn't.

Of course we have a number of packages that are truly trivial and should probably be inlined. Npm is a completely open platform. Anyone can upload any module they feel like. That doesn't mean anyone else will feel the need to use them.

I think you'll find that the vast majority of small modules that are widely used are ones that also cover obscure corner cases and do benefit from widespread use and testing.


You don't have to be widely used to be widely used, though. I bet there are tons of developers who would never dream of using left-pad or is-positive-integer, but nevertheless have copies of them (multiple copies?) on their computers due to transitive dependencies.


No, you see "is-bigger-than-5" clearly does 4 different things. You need these modules:

module.exports = exec("===");

module.exports = bigger(a, b){return a > b};

module.exports = () => return "than";

module.exports = (a) => a.search('ABCD...');

.. or something like that. These are all plugins of course.


Do you seriously think that "word starts with a capital letter" is an easy function to write?

I feel like you haven't spent enough time with Unicode.


return string[0] === string[0].toUpperCase();

You're welcome!


WRONG. Typical junior developer mistake. Even if we disregard trivial problems such as non-letter input or empty input, this will only work with English and a few other languages. This will TOTALLY fail with e.g. Turkish input (I ı vs İ i).


Not really. (string[0] == string[0].toUpperCase() && string[0] != string[0].toLowerCase()) is the correct way to approach this problem. If toUpperCase() and toLowerCase() don't handle Turkish letters, then that's a BUG in those methods, which should be reported and someone should freaking take responsibility for them and fix them.

Adding another module with a method does not fix the original problem, it just creates more problems for other people to solve.


> var isCapital function(s) { return s[0] === s[0].toUpperCase(); };

> isCapital("שלום");

true

> isCapital("1");

true

> isCapital('\uD83D\uDE00'); // Smiling emoji

true

> isCapital(\u279B); // Right-facing arrow

true

> isCapital("\u061Casd"); //Bidirectional control character

true

> isCapital(" ");

true


If "true" is not a valid answer, what would've been one? Similar code in C# returns the same. E.g. Console.WriteLine("שלום".ToUpperInvariant()=="שלום") returns true.


Hebrew doesn't have upper and lower case, so the question "is this hebrew character capital" is meaningless. So, the function in question should not return just a boolean value; it should have a way to return a third option. (Whether it's nil, a C-style return code, an exception, an enum or something else is irrelevant here.)

Actually, it just means that if you're wondering "if this word starts with a capital", you're asking a wrong question. Instead, you should be asking "if this word is a valid generic name", or "is this word a start of a new sentence", and implement these semantic queries in a language-specific way.


You have final-forms in Hebrew, although probably not at the same level of support for checking as you'd get with a script like Arabic.


That's true, but I don't think that sofits should be viewed as capital/not-capital letters: they're not semantically altering the meaning of the word in wider context, like capital letters do.


This will give

  startsWithCapitalLetter("!") == true
which is not what you want.


TIL 1 is a capital letter.


string[0] === string[0].toUpperCase() && string[0].toUpperCase() != string[0].toLowerCase();


typeof string == "string" && string.length && string[0] == string[0].toUpperCase() && string[0].toUpperCase() != string[0].toLowerCase();


Man, Javascript is AWESOME.


/\p{Lt}/.test(str) would be much more compact, but Unicode properties still aren't available in Javascript regular expressions. It doesn't look like they will be anytime soon. I guess they have some staging system and it's still at stage 0 (https://github.com/tc39/ecma262/blob/master/stage0.md), grouped with lookbehind assertions, named captures, and mode modifiers.


Not that I agree with micro modules (I would rather see a set of js core libs), but your code fails with empty strings.


That code fails in a whole host of cases, kind of proving that sometimes trivial "just one line" functions aren't actual that trivial to get right.

edit: fortunately there's an npm modules you can included to fix it...


Maybe F1axs knows that at this particular spot the string will never be empty? There are two philosophies in libraries; one is to check for every possible error in the lib function, the other is to state the pre-conditions in the documentation.


> Not that I agree with micro modules (I would rather see a set of js core libs)

Why not both? If you just want one part of a module, you can just import the dependency directly, if you want a whole core lib, you can do that.

Some people really like underscore and import that. I use ES6/7 functions for the most part, but underscore has some useful things I can import on their own.


string[0] isn't necessarily one code point.


The three cases you state aren't nearly as hairy as determining whether something is a positive integer.

Someone might do something like ~~n to truncate a float. That works fine until you go above 32 bits. Math.trunc should be used instead. Someone might do something like n >>> 0 === n, and using any bitwise operators will always bake in the assumption that the number is 32 bits. Do you treat negative 0 as a negative integer? Detecting negative 0 is hairy. So, to avoid bad patterns, it makes sense.

For is-bigger-than-5(x), x > 5 is not hairy. For add-1-to-a-number(n), n + 1 is not hairy.

For word-starts-with-capital-letter(word)? That one is actually pretty hairy. There are programmers that would write a regular expression /^[A-Z]/, or check the charCode of the first character being within the A-Z range, amongst other solutions. An appropriate solution, however, would be (typeof word == "string" && word.length && word[0] == word[0].toUpperCase() && word[0].toUpperCase() != word[0].toLowerCase()), because the toUpperCase and toLowerCase methods are unicode-aware, and presently you can't access unicode properties using Javascript's flavor of regular expressions.


People have started to point out the existence of modules in this very vein. And, of course, write them. With two such together, you can probably get is-maybe-5 .

* https://news.ycombinator.com/item?id=11351905

* https://news.ycombinator.com/item?id=11359302


If those functions are reusable or complex enough (partly due to deficiencies in javascript the language, sure) then why not?


>add-1-to-a-number

i++


You'd have a good point, if all of those tiny but probably useful modules were given the use you're describing.

Discoverability though is so poor that most of those modules are most likely just used by the author and the author's co-workers.

If a typical npm user writes hundreds of packages, how the hell am I supposed to make use of them, when I can't even find them? Npm's search is horrendous, and is far from useful when trying to get to "the best/most supported module that does X" (assuming that random programmers rely on popularity to make their choice, which in itself is another problem...).


The isArray package has 72 dependent NPM packages, it's certainly not undiscoverable. The leftpad package gets 5000 downloads a month, that's quite a bit of testing for edge cases, compared to the none that I would have gotten had I implemented this myself.

Intuitively, this thread wouldn't exist if your assertion were correct.


Edge cases for a padding function? Your comments make me think that the author's point is valid.


Edge case for left padding - when you provide empty string as padding filler. It can go into infinite loop if not written correctly.


Write a damn unit test for your padding function. We should share unit tests, not one-line libs. Libs with unit tests can be one-liners and be as fine-grained as we like, since it's something your users don't have to download


> We should share unit tests, not one-line libs.

Most of the small libs I've shared are mainly sharing unit tests.

Here's a function I've shared on npm:

  var kind = function(item) {
    var getPrototype = function(item) {
      return Object.prototype.toString.call(item).slice(8, -1);
    };
    var kind, Undefined;
    if (item === null ) {
      kind = 'null';
    } else {
      if ( item === Undefined ) {
        kind = 'undefined';
      } else {
        var prototype = getPrototype(item);
        if ( ( prototype === 'Number' ) && isNaN(item) ) {
          kind = 'NaN';
        } else {
          kind = prototype;
        }
      }
    }
    return kind;
  };
The tests:

  suite('kind', function(){
    test('shows number-like things as numbers', function(){
      assert(avkind(37) === 'Number');
      assert(avkind(3.14) === 'Number');
      assert(avkind(Math.LN2) === 'Number');
      assert(avkind(Infinity) === 'Number');
      assert(avkind(Number(1)) === 'Number');
      assert(avkind(new Number(1)) === 'Number');
    });
    test('shows NaN as NaN', function(){
      assert(avkind(NaN) === 'NaN');
    });
    test('Shows strings as strings', function(){
      assert(avkind('') === 'String');
      assert(avkind('bla') === 'String');
      assert(avkind(String("abc")) === 'String');
      assert(avkind(new String("abc")) === 'String');
    });
    test('shows strings accurately', function(){
      assert(avkind(true) === 'Boolean');
      assert(avkind(false) === 'Boolean');
      assert(avkind(new Boolean(true)) === 'Boolean');
    });
    test('shows arrays accurately', function(){
      assert(avkind([1, 2, 4]) === 'Array');
      assert(avkind(new Array(1, 2, 3)) === 'Array');
    });
    test('shows objects accurately', function(){
      assert(avkind({a:1}) === 'Object');
      assert(avkind(new Object()) === 'Object');
    });
    test('shows dates accurately', function(){
      assert(avkind(new Date()) === 'Date');
    });
    test('loves Functions too', function(){
      assert(avkind(function(){}) === 'Function');
      assert(avkind(new Function("console.log(arguments)")) === 'Function');
      assert(avkind(Math.sin) === 'Function');
    });
    test('shows undefined accurately', function(){
      assert(avkind(undefined) === 'undefined');
    });
    test('shows null accurately', function(){
      assert(avkind(null) === 'null');
    });
  });


If you've called it with an empty string there's probably a bug in your calling code which you'll want to test for anyway.


And when a bug is found, instead of fixing it in one place once, you now have to hope that all of your deps and all of their deps update themselves appropriately.


Which is great if you are one person and you fix the bug in your own code. What you're ignoring is that if everyone writes their own version, then the same problem exists. That bug has to be fixed across every (buggy) implementation. A well-defined dependency system where it is easy to discover and update to a new version isn't a matter of hope.


Remember that download counts for package indexes are often misleading. Between scrapers, deployments and continuous-integration systems (all of which download a package -- in the case of CI, sometimes it downloads on every single test run), hitting the thousands-of-downloads level is not hard at all.


Discoverability though is... poor seems like an argument for improving discoverability, not against having small modules.


Doesn't apply to Javascript, but something like Haskell's hoogle (stackage.org/hoogle could help with a small module approach. It lets you query for functions matching a certain type signature, like this: https://www.stackage.org/lts-5.9/hoogle?q=%5Ba%5D+-%3E+a+-%3...


Discoverability happens with standardization.

Why search through millions of unknown packages when a standard library would have it all right there?


Because the standard library is where modules go to die. Slaving the release cycle of a library to the release cycle of the language is bad for the health of that library, especially in the case of javascript where code often has to be written to run on old browsers with old versions of the standard library.

Now having a metalibrary that depends on a curated collection of popular/useful utilities is probably a good idea - but isn't that exactly what many of the libraries that broke due to depending on left-pad were?


I've wondered why the official committee doesn't have a recommended list of libraries for specific purposes.

That way I can get a curated list of great functions I can trust.

Essentially a standard library for all intents and purposes.


Yeah, I think the natural question here is: why doesn't TC39 round up all these basic packages and add them to the standard library? I've seen other languages criticized for having too large a standard library, but if this is the alternative...

left-pad was released under WTFPL, so in this particular case there'd be no legal barriers to it. (And I'd assume that, for any libraries with a restrictive enough license, it wouldn't be a hard sell on TC39's part -- if they put out an announcement that they were going to do that, I'd go panning for street cred, and I wouldn't be the only one.)

An alternative could be to pull all this stuff together into one big bucket of Legos and package it with a code analysis tool at the beginning of your build process to strip out everything you don't need from the bucket... but I'd guess that's either already been done or a stupid idea for reasons I don't know yet.


Its not about legal barriers, its about the incredible amount of work to precisely specify everything, which is required for any web standard.


Can we ever achieve this?

We can't exactly search the code to find out what it does, otherwise you'd basically be reading all the code to discover it's true behavior, which negates the usefulness of modules in the first place...

Any search will have to rely on developer-made documentation, and/or meta data. This is great in theory, but documentation is rarely well maintained, and/or someone changes the code but neglects to update the documentation.

This leaves us with the situation we have today. A search that somewhat works kindof, and mostly you rely on googling the feature you need and looking for what seems to be the most popular choice.

I'm not sure how this situation can be made better, especially if we continue down a path of having all these "one-liner" libraries/modules that anyone can make and stick out there into the unsuspecting world. When I need a square root function, and my search returns 800 one-liner modules, how am I supposed to pick one that I know is reasonably well done, and does what it says it will do, without looking under the hood - you'll end up just picking whatever seems to be popular...


In most languages, things as common as square root would be part of a standard library which everyone uses, so there wouldn't be any question. That also means it would obviously be well-tested and vetted, because everyone depends on it. Perhaps the solution is avoiding one-liners and moving towards more comprehensive libraries, but that doesn't seem to be what npm devs want (not a JavaScript guy so I'm just observing from the outside).


> Any search will have to rely on developer-made documentation, and/or meta data. This is great in theory, but documentation is rarely well maintained, and/or someone changes the code but neglects to update the documentation.

This is why types are better than textual documentation - the system enforces that they're kept up to date.


We could have something similar with full dependent types a la Idris: you could write a proof and search for a function that satisfies it. If such a thing were popular and huge amounts of Idris code were written, you could write only proofs for your program and the Idris system could go download and piece together your program!

That would be very cool, but I'm not sure how much easier it would actually turn out to be. Also to do anything useful you'd probably have to restrict the language to be non-Turing-complete.


Similar idea but what if you were to write your tests first and then upload them to a site that pieces together the required modules to pass them and generates an API against the modules.


Because finding code that passes arbitrary tests is undecidable in the general case.

(Same reason the pseudo-Idris language would have to be non-Turing-complete)


I was joking clearly. Thought the ridiculousness of the idea make that clear.


You're totally right. Most developers don't develop this way, and that makes the benefits of this approach far less pronounced than they would be if they did. It also doesn't negate the benefits entirely, of course.


Another benefit of small well understood components is that they are easier to write tests for. Do these npm modules have good test coverage? Did the leftpad module have a unit test that would have caught the issue?


No, leftpad has four test cases and they are all obvious, given the leftpad API.

Would I write tests for my own leftpad implementation if I were not farming it out to a dependency? Its possible. More likely I would want to understand the intent behind using leftpad in my application or library, and have test for "formatTicket" or whatever it is that I'm actually doing. But for all the talk about tiny modules that cover surprisingly tricky edge cases, this is not one of them.


which issue? I thought we were talking about the random removal of left-pad and a couple hundred others.


> This GitHub comment by Sindre Sorhus (author of over 600 modules on npm) is my favorite writeup on the topic:

https://github.com/sindresorhus/ama/issues/10#issuecomment-1....

If anything looking at sindresorhus's activity feed: (https://github.com/sindresorhus) perfectly supports the author's point. Maybe some people have so little to do that they can author or find a relevant package for every single ~10 line function they need to use in their code and then spend countless commits bumping project-versions and updating package.json files. I have no idea how they get work done though..


I think the gist of this whole discussion ( at least the OMG WHY?!?! ) part, can be easily explained by an excerpt from your example comment that sums up in a nutshell the all too pervasive mindset I've seen over the years:

"...LOC is pretty much irrelevant. It doesn't matter if the module is one line or hundreds. It's all about containing complexity. Think of node modules as lego blocks. You don't necessarily care about the details of how it's made. All you need to know is how to use the lego blocks to build your lego castle. By making small focused modules you can easily build large complex systems without having to know every single detail of how everything works..."

By LOC he's referencing 'ye ole Lines of Code paradigm, and trying to make the point that in the end it just doesn't measure up to the prime directive of "containing complexity".

... and that's where I beg to differ.

What I think is being completely overlooked here is the net cost of abstracting away all that complexity... It's performance.

Every extra line of code, extraneous function call, unnecessary abstraction ( I'm looking at you promise ), every lazy inclusion of an entire library for the use of a shortcut to document.getElementById -- these all add unnecessary costs from the network down to the cpu.

And who pays these costs?

The end users of your slow, memory hogging, less-than-ideal set of instructions to the cpu that you call an application... but hey, it's easier for you, so there's that.


> ye ole Lines of Code paradigm

Little-known fact: the *y in old signs is actually a 'þ', which is an Old English letter named 'thorn' and pronounced like modern 'th.' Thus, the old books & signs were really just saying, 'the.' Incidentally, þ is still used in modern Icelandic.

Completely off-topic, but I þought you might be interested.


holy shit, I had no idea. That's cool.

edit: and I double checked, because the internet :)

http://www.etymonline.com/index.php?term=ye

it's true! that's awesome.


I really þink ðat we ought to use þ and ð in modern English, but oddly enough no-one agrees wiþ me:-)


Promise was clearly necessary, because without it we had hundreds of other less principled ways to implement async control flow :)


We had, and still prefer (looking at npm stats) the async module. Eg:

    promise.then(function(){}).then(function(){}) 
is not substantially different than:

    async.waterfall([function(){}, function(){}])
Promise advocates kept pretending async didn't exist though, and everybody was using callback hell.


There's a lot of old cruft using async (my current workplace being one of them) - bluebird also has 4x the downloads of async.

async is pretty terrible though, you cannot pass the results from one execution to another, i.e. passing results from one function in async.series to another, which results in developers tending to pollute variable scope by defining above and filling it in inside each callback. This prevents the ability to write clean isolated testable functions.


You can return the first from a function and it will be a promise. Promises are reusable. Also they contain exceptions in their context. Those can be handled at any point of the promise-chain.

So promises are way more composable than callback based solutions.


Nobody pretended that async didn't exist, we just knew its the best "solution" that ignored the problem.

Which was: throwing away the entire language's compositional features, including the concept of input arguments and return values, results with... poor compositionality, of course.


I thought the problem was callback hell. I've sat through a bunch of promise talks and the problem was never 'JS should be more compositional'.


Yes, uncompositionality of callbacks leads to callback hell. Or to reinventing every single thing but for callbacks. Like array.map (which works with promises) or array.forEach (also works with promises) or every single synchronous function (they all work when passed to promises).


If you solve callback hell for a series of sequential functions by using an actual series of sequential functions, you don't have nested callbacks and you didn't require Promises or composition - just a basic understanding of data structures and first class functions.

It seems you're defining callback hell as 'whatever promises solves' rather than it's common definition of over-nesting.


Callback hell is a "loss of composition" problem. Most languages, including JavaScript, have things called expressions. Expressions rely on functions returning their output. They become useless when functions give their output through a callback passed as their input: they don't compose with that frankenstein.

Node style callbacks are a prime example of how in the quest for "simplicity" and hate for abstractions its easy to build a monster / frankenstein like caolan's async. Node callbacks were a self-contradicting philosophy: it abandoned all thats already in the language in the quest to avoid an abstraction that was not already in the language :)


Hrm, it's always seemed natural (and not frankenstein) that a function would be input as much as a number or string is. 'My name is Steve, my age is 7, and here's what do to one the database has saved'.

That said I see how the inconsistency you're talking about with how expressions return values with callbacks and you've certainly put the point very well.


> Maybe some people have so little to do that they can author or find a relevant package for every single ~10 line function they need to use in their code and then spend countless commits bumping project-versions and updating package.json files. I have no idea how they get work done though...

Rather than copy paste someone else's unmaintained thing and handle all the bugs, or write their own code and unit tests, they use the same library as a few thousand other people?


If you don't know how to write a function to left-pad a string without copy & pasting, you have other problems..


What makes you think people who don't write their own left pad function can't write a left pad function?


How is that superior to copypasting from a stackoverflow answer?

If it's a popular issue, lots of people had the same issue, many will be nice enough to add their edge cases and make the answer better, most will not. Same goes for contributing to a package.

With a package you would be able to update when someone adds an edge case, but it might break your existent code and that edge case may be something that is not particular to your system.

If you don't want to get too deep in the issue, you can copy paste from SO, just the same you can just add a package.

If you want to understand the problem, you can read the answers, comments, etc. With the package you rely on reading code, I don't know how well those small packages are documented but I wouldn't count on it.

The only arguments that stands are code reuse and testability. But code reuse at the cost of the complexity the dependencies add, which IMO is not worth the time it'll take you to copy and paste some code from SO. Testability is cool but with an endless spiral of dependencies that quite often use one or more of the different (task|package|build) (managers|tools) that the node ecosystem has, I find it hard to justify adding a dependency for something trivial.


How is it superior? Simple.

How do I systematically make sure that I have the latest version of every stackoverflow code snippet? If it's a new post, it may not have all the edge cases fixed yet. So now I have to check back on each of the X number of snippets I've copied.

In the npm approach, I can easily tell if there's a new version. For prod, I can lock to a specific version, but in my test environment, I can use ^ to get newer versions and test those before I put them in production.

If the edge case of new version of a package breaks my code, I've learned that I'm missing a unit test. Plus, the question isn't whether this bad thing might happen on occasion, the question is whether this approach is, on balance, superior to cutting and pasting random code snippets into my code. I think the downside of the npm approach is less than the downside of the copypasting from stackoverflow approach.

And every moderately useful npm package I've looked at has very good to great documenation.


The simple rebuttal to that is that modules that are collections of small functions are easy to reason about as well, and don't the downside of increasing the metadata management to useful function ratio nearly as much. Why have just an average (mean) function, when it makes sense to provide a median and mode as well? Even then, you might find that there's a bunch of extra math operations that are useful, and you might want to include some of those.


With the advent of ES6 modules's selective imports and tree-shaking, that's definitely quickly becoming a better approach. With the old CommonJS modules, you need to be concerned about overall code size, which is where small, single purpose modules excel, and why this approach has proliferated to this degree.


I've been reading about tree shaking. I'm not at my laptop at the moment, so I can't settle this question by testing it. I'll toss it to the community:

https://news.ycombinator.com/item?id=11349606

Basically, how does tree shaking deal with dynamic inclusions? Are dynamic inclusions simply not allowed? But in that case, what about eval? Is eval just not allowed to import anything?

I've been reading posts like these, but they are pretty unsatisfying regarding technical detail: https://medium.com/@Rich_Harris/tree-shaking-versus-dead-cod...

Maybe someone else was wondering the same thing, so I decided to post it here before wandering off to the ES6 spec to figure it out.


> Are dynamic inclusions simply not allowed?

Correct. One of the motivating factors for ES6 modules was to create a format that can be statically analyzed and optimized.

> Is eval just not allowed to import anything?

Correct.

See this excellent writeup for more details regarding ES6 modules: http://www.2ality.com/2014/09/es6-modules-final.html


Doea code size really matter to node.js ? And how common was commonjs (no pun intended) on the client before ES6 ? Also doesn't commonjs bundling add a significant overhead when talking 5 line function modules ?


Quite common actually (see Browserify)! In fact, the increasingly widespread use of npm and CommonJS on the client is one of the factors that motivated the npm team to transition to flat, auto-deduplicated modules in version 3.


There is little reason to bundle node.js code. It's an optimization, and a dubious one. In my experience, speed of execution isn't impacted at all. I haven't tested the memory footprint, but it seems vanishingly unlikely that dead code elimination would have any substantial effect.

There's probably not any overhead in bundling, though. Not in speed or memory, at least. The overhead is in the complexity: the programmer now has one more place to check for faults, and debugging stack traces now point to a bundled version of code instead of the original sources.

The case where none of this is true is when require() ends up being called thousands of times. Startup time will be correspondingly slow, and bundling is the cure. But that should only be done as a last resort, not a preemptive measure.


That entirely depends on your bundler. Newer bundlers like http://rollupjs.org/ combine everything to avoid this.


Just sugar-coated kool aid I'm hearing. Community benefits? First of all, I'm coding to get paid and this recent madness proved that JS ecosystem is semi-garbage. Back to the original question - do people really can't program that they need packages like left-pad or is-integer which had their own dependencies? Before writing those cool toolchains (which would likely work in a specific machine with a specific setting with all the real world testing the community has) can we at least pretend that we know the computer science basics?


People need modules like leftpad to tick the "maintains moderately popular open source project" checkbox. Instant hirability.

I don't want to claim that it would be a directly calculated career move, more like starting a blog: you admire a good blog, you want to be more like the blogger, you start your own one. On the dim chance that it will become both good and not abandoned after the third post. Nanomodules can be just like that, the air guitar of would-be rockstar coders. This is certainly not the only reason for their existence (and even the air-guitar aspect has a positive: low barrier of entry that might lead to more serious contribution), but the discussion would be incomplete if it was ignored.


Yep, I think this is a huge, HUGE part of it.

Step 1: Create a culture in which "having open source contributions" is a requirement to entering said culture.

Step 2: Remove all friction from introducing open source contributions into the culture.

Step 3: Watch the Cambrian explosion.

Step 4: (Two years later) Point to the Cambrian collapse and how the new hot thing will solve everything.

I don't know what sort of shit show Step 4 will turn into, but it will also definitely be the result of folks taking a simple and good rule of thumb (this time, it won't be "small and composable"), and implementing it without ever stopping to think what problems it solves and doesn't solve.


That scenario in the last paragraph is an uncomfortably convincing view of the future. Do you think it could be avoided by an additional step like the following?

Step 5: a mechanism for curated metamodules

This would not have changed much for the current situation, but it might help cultivating a "too important to play it fast and loose" mindset that would.


No, probably not. On the contrary, what I'm suggesting is that the solution of Step 5 you suggest would be "the hot new thing" of Step 4.

It's about unforeseen consequences of a solution to a problem. That's what I mean by "I don't know what it will look like", because it will definitely solve the problem this thread is dissecting, and the problem it introduces will be a whole new vector.

But its selling point (the way it solves our current problem) will be a simple idea that is taken to its logical extreme by people who don't think critically, and then it will be time to solve that problem.

That is, I see the underlying recurring problem as stemming from the cultural forces –– how we educate people, how we make ourselves hireable –– that enable even very smart people to be shortsighted and immune to learning from the past.


I would give those candidates the FizzBuzz problem just to see if they can actually code from scratch or `require('fizz-buzz-solver');`. I care more about their competence than what 1 or 10-liner packages they maintain.

On the contrary, I would certainly mark "maintains moderately popular open source project" as a calculated career move, just like how prostitutes would dress up right for the cliente. Sorry for the crude example but I am not placing the blame on the devs here but the absurd notion of employers considering a GitHub repo the 'in' thing before actual capability. Candidate - Brah I got 10 of those packages with 10mil downloads in total but I can't code for shit. Hiring manager - Since we are suckers who look at stats, you're hired!

The problem with this nanomodules approach is that it results in complacent and naive developers that eschew the basics and just publish random crap. Anything can make it to be a NPM package and be judged by...popularity? Since when is code turning into pop music :)?


I'd hire the people who use fizz-buzz-solver and spends time doing original work rather than rewriting something for the sake of ego.


Here's where expectation should be set for both candidates and interviewers. The point of solving Fizz Buzz problems is to open up angles for discussion. What would you do differently with this problem, at this scale, for this integration etc etc. If someone writes crude code, I'd ask if they follow style guides or conventions. If someone writes overly fancy and clever stuff, I'd ask if they ever have to maintain shit. There is not much value in talking if all candidates would write "import fizz-buzz-solver from 'fizz-buzz-solver';".

You see, the whole Fizz Buzz is nothing more than a prop to allow me to find out what this candidate can actually do. Anyone can include a package and critical thinking differentiate smart ones from the pack. Heh, if there is a package for any and everything, most devs would be flipping burgers instead of doing original work.

Believe me, most problems you think of as original, aren't original. It's not about solving problems either, it's about repacking the solutions into something to sell, or making it popular enough so you can make money off it.


if leftpad were in the node stdlib, would you still be making the same point about knowing computer science and using leftpad?

in python: https://docs.python.org/3.3/library/stdtypes.html#str.ljust


Yes, until people stopped publishing isInteger and isArray as `packages`. Plus, stdlib is standard library. Can you cite one for JS? NPM is not.


They do NOT encourage code re-use because the effort required to understand your need, refrain from writing code, and hunt down a module in npm, far outweighs the effort to just write the code and stick it in your in-house utils library.


Yeah but, why do you need your own in-house utils library. Node needs batteries included!


Because writing code is what programmers (should) do. If something is simple, then it is simple and won't take up time and energy. If you don't know how to do it, then learning how to do it is worthwhile -- especially if it turns out to be simple. If you make a mistake, then someone on the team must look at the code and understand the mistake. This means that the team learns that things that seem simple are sometimes complex and they learn how to recognise their mistakes. When there is a mistake, someone has to fix that mistake. They learn how to fix the problem. Other people on the team must review that fix and they also learn how to fix the problem.

There is always a balance between getting some extra productivity by using somebody else's code, or getting the knowledge, expertise and control of writing it yourself. Far too often I see young programmer's choose something off the shelf so that they don't have to understand it. This is a recipe for disaster -- for your project, for your team and for yourself.

IMHO, as programmers, we should err on the side of writing code. Often it is obvious that you should use standard libraries, or a particular framework or or another reuse library. But where it is not really obvious, I would start with writing your own code and see where it gets you. You can always replace it later if it doesn't work out. I think you will find that you and your team will be miles ahead if you take this approach.


Never understood the fun in importing everything and chaining together someone else's code. Sometimes if I'm building something trivial, a bit of test code or something for fun I'll write up all of it, even if I have to reference others code. You learn so much more.


Well, it does encourage code re-use, but the issue is that the benefits of code re-use here are far outweighed by the downsides of just writing it yourself.


I think that there's a certain wishfulness bordering on naïveté to this pursuit. We tell ourselves that we are managing complexity by having small, well-tested units that are reused by an entire community. But software complexity continues to exist. We think we are mitigating the complexity of our own software. But the complexity really just shifts to the integration of components.

Anyone that has been around long enough has war stories about getting two relatively simple pieces of software working with each other. In my experience, integration problems are often the most difficult to deal with.


But this doesn't address the author's point that each module, each dependency is an additional point of failure.


There are definitely trade-offs involved when making decisions to inline vs import.

Strictly speaking, you're definitely right. Each dependency is an additional point of failure, but so is each additional line of code you inline.

The benefits of these small modules is that they're very thoroughly tested by the entire community. I'd say for most cases, they will be much more robust than any solution an individual developer can spontaneously whip up.

Of course, these modules don't exist in a vacuum, and infrastructure failures such as this one do pose another point of failure you wouldn't have with inlining code, but I think in this particular case it had more to do with the failure of npm to adhere to package management best practices to not include an unpublish feature.


>There are definitely trade-offs involved when making decisions to inline vs import.

If someone can't make the right trade-off regarding "should I import a module called "isArray" or "is-positive-integer", then they should not be programming...


I think a lot of the confusion here is that in most languages "isArray" or "is-positive-integer" are simple functions of simply build into the language.

Dynamic typing and diversity of devices and virtual machines mean the ability to simple tell if something is an Array could be multiple lines of code that could take a considerable amount of time to test.

Delegating this to the community is arguably the only sane choice, however strange that may be to someone come from another environment.


Yep. Everyone mocking NPM and the javascript community for this ought to retire, they clearly don't understand how to program in this day and age. Or is that not what you meant?


author of over 600 modules and 1200 lines of javascript.


Wow, you were not kidding. I chose a random package of his [1], and indeed: a grand total of 1 LOC...

This is madness.

[1] https://github.com/sindresorhus/float-equal/blob/master/inde...


and it uses another 1LOC package. https://github.com/sindresorhus/number-epsilon/blob/master/i...

and no, the edge cases argument does not apply to either of them. Wow.


Would you like to memorise that number yourself to use in your app?


What memorise? How about this for a dependency:

https://www.google.com.au/search?q=es6+epsilon


That module exists precisely because this is a new addition to the language, it's a polyfill for backwards compatibility.


> Small modules are easy to reason about

Flip-side: it isn't easy to reason about a large and complicated graph of dependencies.


I'm not at all clear why this blog post is touted as evidence that the tiny modules approach is correct. I think it might be all the people after it congratulating him.

"It's all about containing complexity." - this completely ignores the complexity of maintaining dependencies. The more dependencies I have the more complicated my project is to maintain.

Dependencies are pieces of software managed by separate entities. They have bugs and need updates. It's hard to keep up to date.

When I update a piece of software I read the CHANGELOG, how am I expected to read the CHANGELOG for 1,000 packages?

Depending on a bigger package (handled by the same entities, who write one changelog, in the same form) is more straight forward.

I'm not saying this is wrong - but there's a balance here, and you must not ignore the complexity of increasing your number of dependencies. It does make things harder.


My problem with this, as an occasional JavaScript developer, is "discoverability" (as many others have mentioned). If I decide I need a left-pad function, and search on NPM, how do I choose which one is best? The one with the most downloads? Not always the best indicator of quality; perhaps it's just the oldest.

Not to mention the cognitive overhead of stopping programming, going to NPM, searching/finding/installing the module, then reading the documentation to understand its API. Isn't it simpler to `while (str.length < endLength) str = padChar + str;`? How can there be a bug in that "alternative naive inlined solution"? Either it works or it doesn't!


I don't see how your linked comment brings more to the table than the basic arguments for code reuse.

But naturally, with any code reuse there's a benefit and a cost to instance of internal or external reuse.

The Benefits for external reuse include ideal reliability as your describe as well as not having to create the code. The costs for external reuse include having your code tied to not just an external object but also the individuals and organizations creating that object.

I think that means that unless someone takes their hundreds of modules from the same person or organization and is capable of monitoring that person, that someone is incorporating a layer of risk to their code that they don't anticipate at all.


Percentage of module owners who you can't trust to not screw up their module: H

Risk of indirectly hosing a project with N module owners providing dependencies: 1-((1-H)^N)

Let's say H is very small, like 0.05% of module owners being the type who'd hose their own packages.

3 module owners: 0.15% chance your own project gets hosed

30 module owners: 1.49% chance your own project gets hosed

300 module owners: 13.93% chance your own project gets hosed

Keep in mind it's not just your dependencies, but your entire dependency chain. And if you think a module owner might hose some modules but not others, maybe H is actually the number of modules in which case 300 starts getting pretty attainable.

Upshot:

Not everyone is trustworthy enough to hang your project on. The more packages you include, the more risk you incur. And the more module owners you include, definitely more risk.

The micromodule ecosystem is wonderful for all the reasons described, but it's terrible for optimizing against dependency risk.

Takeaways:

Host your own packages. That makes you the module owner for the purposes of your dependency chain.

If you're not going to do that, don't use modules dynamically from module owners you don't trust directly with the success of your project.

I love collaborative ecosystems, but some people suck and some perfectly non-sucky people make sucky decisions, at least from your perspective. The ecosystem has to accommodate that. Trust is great...in moderation.


I agree with you, besides the tree-shaking (nice word btw). It's like NPM shaking the Christmas tree and then say "Have fun cleaning up the floor". Remember that NPM is not like apt-get, where the packages are Managed for you by an expert. In NPM you have to manage the packages! And where you can't have NPM and build dependencies, like in production, maintenance now becomes much harder!


My problem is one of productivity. There's already a standard library, and if it's a language I've been using for a while, I probably remember most of it. I can pretty much go straight from my thought of what I want done to typing it out, much like typing English. If you force a 'cache miss' and force me out of my head and into a documentation search, well, that's going to have a significant effect on my performance. If the function is well-named, it has less of a cost in reading the code, but there's still a cost, because what if it's slightly different? I have a pretty good feel for the gotchas in much of the standard library of the languages I use. I have to stop and check what the gotchas of your function are.

Yes, at some point the complexity cost of gluing together the standard library functions to do something becomes greater than the lookup cost of finding a function that does what I want; but I am saying that adding more functions is not costless.


Small modules are also often the result of dealing with different javascript implementations over the years. I've recently seen a simpler version of the left pad that would not have worked on multiple Safari versions or <IE6

The derision is unwarranted, due to a failure in critical thinking from otherwise smart people.


It's interesting to me that people find this convincing. I find it to be complete insanity. People need their libraries, but putting everything in tiny buckets is just not working. Why aren't people working on good utility libraries instead?

There's even some guy calling for a "micro-lodash". To me, as a Python engineer, lodash [1] is already a tiny utility library.

I guess it's also about the fact that JS is a pretty bad language. That you need a one-line `isArray` dependency to `toString.call(arr) == '[object Array]'` is crazy.

[1] https://lodash.com/docs


There is a practical reason for tiny modules in client-side JS that doesn't exist with Python: page load times. If your base layer is going to have third-party dependencies, they better be tiny and do only and exactly what you need.

That said, lodash core is only 4KB, and lodash gives you the ability to build it with only the categories or exact functions you want, so I don't understand what the purpose of a "micro-lodash" would be.


There are tools to include only the stuff you actually use that you can stuff in your build process though.


I get that author's point, but do you REALLY need a dependency module that tells you the type of something?

That's not a Lego block; that's an excuse.


When using a language where

    isArray(arr)
turns into

    toString.call(arr) == '[object Array]'
...then I guess that's more reasonable than if you use something sane.


The alternative to small modules is not no modules at all, it's libraries.


Why dont they put everything together and call it "commons" like in Java? (commons-lang, commons-io, etc).


That argument sounds like an enterprise architect explaining why the AbstractReusableBeanFactoryGenerator is going to increase productivity.


it reminds an apt proverb: missing forest for the trees.

Too many modules do not necessarily become a good thing. They may appear to get rid of complexity but in reality, you will have to face the complexity some level above and in fact, the sheer number of small modules will most probably add more complexity of themselves.


The reasoning makes sense for small modules that might change in the future, but as he says himself, most of his modules are finished and will never change. That makes many arguments in his post moot and the modules should probably be snippets instead that are implemented directly.


> author of over 600 modules on npm

Nope.


Author of "is-positive-integer" here. I will admit the implementation is pretty funny, but I move-out all single-purpose utils out of projects to modules for a bunch of reasons. DRY is the most obvious one, but one that may be less obvious is for better testing.

I move out modules so I can write really nice tests for the independent of the projects I am using them in. Also, I tend to write projects w/ 100% test coverage, breaking out utils allows me to test projects easier and faster.

Also note, the implementation of this module changed a few times today. With it being open source and having the collaboration of other engineers, we ended up with a very performant version, and discovered interesting quirks about "safe integers" in JS.


"DRY is the most obvious one, but one that may be less obvious is for better testing."

Isn't that why we just write functions? Turning simple functions into entire modules just adds an unnecessary level of abstraction that helps nobody.


Of course that's why we write functions. But we can't share a function with others and across projects... so we make it into a module to do that.

It's like (micro)crowdsourcing (the smallest components of) the standard library that JavaScript never had.

Some bit of logic could go from being DRY in one project, to DRY in all of my projects, ... to eventually be DRY in all projects. It's globally DRY.


Two points:

- Breaking out is-positive-integer hasn't reduced the number of paths to test. You have not gained anything, you've added overhead.

- 100% test coverage is rarely a good thing. It is required for safety critical areas like avionics. I can guarantee that your JS code is not making into any safety critical environment!


>hasn't reduced the number of paths to test

But it has: it's now tested, and you don't need to write tests for it next time you want to use it.

>100% test coverage is rarely a good thing

Not sure what your argument is here. Sure, it may not be helpful but are you saying that one should strive for less than 100% coverage, because "it's rarely good"?


100% coverage is rarely worth the time, unless it's an engine controller or something else that needs the assurances.


If tjmehta likes to cover his open source code 100%, under whatever metric, by God let's encourage him in that and not start a discussion about the economic sense of it!


> 100% test coverage is rarely a good thing

Do you mean rarely useful, or actively harmful?


Both in a way.

What happens when you have a "100% test coverage" requirement is that people don't think about the tests, they simply make tests to force the code down every path without thinking whether it was intended to operate like that.

For example if the is-positive-integer had a silly test for "if(value==23) return false", a requirement for "100% test coverage" would simply result in someone creating a test for that condition instead of considering if it was actually a fault.

100% test coverage != no faults.

What you have done by generating 100% test coverage is effectively 'frozen' your code and made it harder to implement any changes.


I would say that what's most harmful is using code coverage as the primary measure of quality of your tests. It's that mindset that puts coders in a mode where they write tests which upon failure mean nothing significant (unless it finds a runtime error). It's a type of fallacy. Instead of considering if your tests verify your real-world requirements, you feel like your job is done because you have reached 100% line coverage.

It's like following someone's car and congratulating the driver that he drove correctly, without considering if he reached the correct destination.


Just to pick at a nit with you, it's a little meaningless to say "100% test coverage", without specifying whether you're talking about line coverage, branch coverage, path coverage...

This is especially true for one-liner modules in js, where any test at all might let you claim 100% statement coverage, without accounting for branches or loops within method calls.


With how weird javascript can be about numbers, it actually didn't surprise me that your module existed.


Actually, thats a good reason to use trivial functions like the one described. Hopefully the author has discovered all of the quirks in Javascript that might affect this. It will likely be a lot more tested than any version I would write.

As someone who spends 80% on the back end, I often get bit on the ass from JavaScripts quirks when I need to add some front end stuff.


That's interesting. JavaScript has enough quirks to warrant an is-positive-integer module.


See:

  isPositive = (x) => +x === x && x > 0
In which conditions does it return a wrong value? I haven't found any.


It would be really, really great if this function was not in its own module, but was part of a larger library in which all such functions of related modules were captured, without the (cognitive and other) overhead of the separate packaging.


A fun r/programming subthread about this package: https://www.reddit.com/r/programming/comments/4bjss2/an_11_l...

Those are just the three top-level dependencies. The package has 9 recursive dependencies.

There's also a nice table explaining how a package called "is-positive" managed to reach version 3.1.0.


This is just baffling...


Similarly, the `average` package on NPM is one that I came across:

https://www.npmjs.com/package/average

    var average = require('average');
    var result = average([2, 5, 0, 1, 25, 7, 3, 0, 0, 10]);
 
    console.log('The average for all the values is:', result);
It's hard to not stare at that in complete disbelief; someone thought that it was worthwhile to create a package for determining the mean of an array of numbers.


You know what's worse? Javascript numbers are all floating point numbers, which means integers are 53 bits long. So, you might think this library would try to address issues this can cause, but nope, this is the average statement you'd write if you didn't know was a mantissa was and had never heard of big.js, bignumber.js, decimal.js, crunch.js or even strint (which represents integers as strings because wtf not).


Not to mention the fact that adding many floating point numbers in a naive way results in a serious error accumulation, which is why things like Kahan summation algorithm[1] exist.

[1] - https://en.wikipedia.org/wiki/Kahan_summation_algorithm


Every once in a while, I come to HN, and inevitably find a post about some new JavaScript technology on the front page, wherein I find some comment that gives me a new appreciation for the web app language I use at work, which fortunately is not JavaScript.


I'd be interested to know what language it is.


It must be one of: Elm, Purescript, GHSJS, Faye or Haste


What is the web app language you use at work? I assume you're talking about something that runs on the client -- a web browser -- so it probably compiles down to JavaScript, right? JS literally has no numeric type that isn't a float, so how does your language escape this fundamental limitation?


Not OP, but I used Google's Long.js (among other things) in a project[0] to get around the limitation of not being able to do as in Python:

    import fractions
    print str(fractions.Fraction.from_float(0.3333).limit_denominator(10))
Almost anything that compiles to JS would be better than by-hand JS; I'd love to use ClojureScript with re-frame at work...

[0] http://www.thejach.com/pseudo-public/webgl_coursera/assignme...


asm.js is proof that a compile-to-JS language doesn't necessarily have to discard the correctness of its number types. A statically typed language can do a lot. Integer values can be can to int32 via `| 0`, longs can be emulated with two numbers (GWT does this).

The tradeoff is speed, especially for dynamically typed languages. Having to check your operand types in JS before doing work is a performance killer.


And WebAssembly means that soon it will be practical to use any language on the client or server side.


It already is on the server-side. Feel free to use C; at least it would be performant.


It's easy to write code that does the wrong thing quickly in any language, and IME that tends to be the usual result of choosing C.


ok, you are aware that the vast majority of the worlds software is written in C.... yes there are problems with it but it is still suitable for real-time, embedded, safety critical software of the sort I've been making quite successfully for close to 30 years.

Don't fall into the 'C is the devil' trap, any tool can be misused.


Straight-up C is not at all suitable for safety-critical software. C plus various bolt-on tools for static analysis and the like can be usable, but is always going to be less effective (IMO) than a unified language where every tool is working according to the same rules.

There might be a few legitimate use cases for C, but I've seen people pick it for the wrong reason so often (and using C because "it would be performant" is entirely invalid IME).


You would have to argue with the overwhelming majority of safety-critical software that is and has been for decades, written in C...

Of course, static analysis is always used in combination with proper coding style... but that is just the normal (professional) C development environment.


> You would have to argue with the overwhelming majority of safety-critical software that is and has been for decades, written in C...

> Of course, static analysis is always used in combination with proper coding style... but that is just the normal (professional) C development environment.

>> Straight-up C is not at all suitable for safety-critical software. C plus various bolt-on tools for static analysis and the like can be usable, but is always going to be less effective (IMO) than a unified language where every tool is working according to the same rules.

Pretty sure you've just restated GP's point in your second paragraph.


You just have to use the right libraries:

https://github.com/Adaptv/ribs2


That's not true. In most javascript implementations, integers will be represented by real 32 bit integers.


How does that help? Besides they overflow to doubles if they get bigger than 2^31-1 anyway.

The problem with a naive average like this is that if you're averaging a bunch of big numbers there is a very high chance of overflow when you're summing, so even if all the numbers fit in 32 or 53 bits your calculation will be off.

If you're not averaging thousands of large numbers, why are you even using a package instead of nums.reduce((a,b)=>a+b) / nums.length anyway?


How does storing integers in 32-bit values help the issue of integers being truncated to 53 bits?


53 bits that cannot overflow (it only becomes less accurate) is not enough but 64 bits are, even with the risk of overflow?


Who said anything about 64 bits?


The complaint was that JavaScript doesn't have 64-bit integers but only 53.


Clearly 2^32+2^32 < 2*53, so there is no problem ;-)


Source? I find this very hard to believe. 1. How does the engine know of it should be an integer or not? 2. Why would they have a special case for this, it won't benefit accuracy and treating them in a special way probably hurts performance more than just always keeping it as doubles.


As long as everything used is really an integer and that everyone you work with is careful to keep things as ints (oring with 0 and such)


Well a bunch of standard libraries have sum defined, right?

JavaScript has suffered from a lack of a standard library for a while. Having a small package like this means that (in theory) everyone is using the same bug free version of summing instead of writing their own.

Honestly having JS start building a standard library at this point would be wonderful.


For now I suggest using lodash functions exported as modules.

For example: https://www.npmjs.com/package/lodash.mean

Or, in light of `left-pad`: https://www.npmjs.com/package/lodash.padstart

You get the benefit of a "small, focused" module but still rely on a large and stable project with a great maintainer that cares about performance and testability.


There's already `Array.prototype.reduce()` in most browsers, which will do summing, string concatenation, and any other kind of aggregation:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

If you need something more convenient I highly recommend just adding lodash and getting a whole bag of rock-solid awesome tools.


reduce isn't a safe way to add floating-point numbers unless your accumulator is much more precise than your array values. You'll end up with what they call "catastrophic cancellation".

And they won't let you have fixed-point in JavaScript!


Doesn't a for loop have the same problem?


If you write the loop yourself, you can add everything to a double instead of a float, or a long double instead of a double, if you have those.

Oh, if all your numbers are positive you can also sort the array and it will minimize the error accumulated, but I'm not sure what to do with mixed signs.


here is the source for that npm package

https://github.com/bytespider/average/blob/master/src/averag...

and here is the code copied out in full

module.exports = function average(values) { for (var i = 0, sum = 0; i < values.length; ++i) { sum += values[i]; } return sum / values.length; };

is this worth adding a dependency to your build for?


Sounds like the author was aiming for something to put on his resume: e.g., "Author of 25 libraries on NPM, some with more than 500K downloads." etc...


Well if I was interviewing the guy, my second or third question would be "tell me about these packages you published on NPM"

It's going to be damn hard to make an average function sound impressive.


I think you're seriously overestimating quality of the average programmer candidate.

Someone who understands what NPM is, has written and published working code (even stupid code) is miles ahead of the curve already.

Most companies in the world, especially non-software companies, don't go for 5% hacker gurus. Attracting someone vaguely competent is already a blessing.


No, I don't think I am. I've conducted many dev interviews. Gurus can often be prima donnas, difficult to work with in a team, btw.

Interviewed enough "C++, 3 years" people who couldn't even describe a class, or give the most basic description of OO, to have no illusions of some of the standards out there. Similar for other languages. Similar for how much having a degree is worth. I used to be surprised such people tried it on, as there was no way they'd walk out of the hour looking anything but stupid, but it happened often enough to stop surprising. Of course I've also experienced code produced by such types, and worked with a couple.

If you're going to publish something then make the world a better place not pollute it with pointless stuff.

Spending more time publishing this package than most basically competent developers would spend writing the four lines is not going to mark you as "miles ahead of the curve" in my eyes. If you're an 18yo fresh out of school without degree, seeking trainee role, I'd look on it favourably.


In JavaScript, the API could be fairly complex. Thing is, JavaScript doesn't have arrays of numbers. So, what should the function do if the array passed in contains a null, an undefined, a string such as "7", "012", "008", or "2+3", a function, another array, a hash, etc?

I can easily see this grow into a function accepting options indicating such things as "null values are zeroes", "ignore null values", "evaluate string arguments", etc, or maybe into a factory that produces customized averaging functions.

For example, average([null,1,2,3],[4,5]) could either be 3 (the average of all numbers), 2.5 (sum of numbers divided by number of 'root' items), undefined (input format error), 3.25 (average of average of [1,2,3] and average of [4,5]), etc.

And what if it gets passed arrays of equal size? "average([1,2,3],[7,4,5])" could easily be interpreted as a vector average, and produce [4,3,4]

Silly? Yes, but if you want to write a robust library in JavaScript, you have to think about what to do with all these kinds of input.

And of course, there are the simple variants of harmonic mean, arithmetic mean, and geometric mean, and several others (https://en.m.wikipedia.org/wiki/Average)


...and this example is none of those things, and untouched in 2 years.

If an interviewee did respond with some or all of the points you raise he'd have turned a potential negative into a positive point. This instance is the simple arithmetic mean with no consideration of anything.

I stand by my original comment. Nine times out of ten an average is going to be resolutely unexceptional, one time in ten or less it's the core of a well-crafted solution with a decent reason for being.



IMO the behavior of "average" should be unspecified if it's passed anything but a single nonempty array of numbers. Making it even a tiny bit more robust is wasted work. Moreover, it's harmful because it encourages sloppy calling code.


The committee that designed SQL had a different opinion.


Right, that's a different philosophy. You can use JS as a dynamic language with DWIM qualities, like SQL. But I prefer to write JS code as if it were a typed language without nulls, and use tools to enforce that. I think that approach will win.


Im of the mind most employers are looking for "have you done stuff" "can you do stuff". Veey rarely are they looking for anyone extraordinary, simply a tool that works


I doubt something like 'wrote npm package that averages stuff' would be relevant without fluffing it up to impress the non-tech hiring manager. Still would ring alarm bells with devs though.


That plus perhaps some kind of dopamine kick these people get out of this whole charade... Sindre Sorhus keeps describing himself as an "Open Source addict", after all.


This sounds like a symptom of an inadequate standard library. I do expect to be able to call "average" on a list of numbers without writing it myself, but I expect that to be part of the language not a 3rd party package.


> I do expect to be able to call "average" on a list of numbers without writing it myself

Just out of interest, what kinds of functions would you expect to have to write yourself, if you're not happy about calculating the average of a list of numbers?


Alright, take a simple function like average. How might someone naively calculate the average?

    for n in list
        sum += n
    return sum / len(list)
Which will fail but will probably be caught in the code review. Then a cleverer developer might think to write

   l = len(list)
   for n in list
      sum += n / l
   return sum
Which will also fail but in more subtle ways, hopefully the senior dev will catch it in the code review.

Then they correct it to

    l = len(list)
    for n in list
        sum += n / l
        rem += n % l
        sum += rem / l
        rem = rem % l

    return sum
But this could further optimized and might still be dangerous. This one might not be fixed at all in the code review.

The best algorithm might be Knuth's which is fairly non-trivial and might not even be the fastest/most efficient given certain unique JS optimizations.

Do you want to do this for every 'trivial' function or would you rather import average where (in theory) someone knowledgeable has already done this work and benchmarks for you and you get the best performing algorithm basically for free?


1. The algorithm in question does not do any of the latter 'better' approaches.

2. The pursuit of all edge-cases is a form of hubris. Your first version is fine in many cases (it is the algorithm used by the package referenced [edit:] and by both the Python and .NET standard libraries) and can be written inline.

3. At the point you are concerned with specific edge-cases, and need the 'correct' algorithm, you need to have found that algorithm, understood it, and make sure that the package you're importing implements it. It's even worse for a builtin function: there's no guarantee that a language implementation of average does so, or that it has the correct tradeoffs for your code. You'd need to test it, code review it, and so on. Do you do that for every trivial function?

4. If you're really suggesting that you can't be trusted to calculate an average correctly, then how can you have the slightest confidence in anything you do that is more complex?


>It's even worse for a builtin function... You'd need to test it, code review it, and so on

If you can't trust built-in functions to do what they say on the standard library documentation, you're either paranoid or made a wrong choice of language. (Or you have some very compelling reason to be working in a language that's still rough, like the early days of Swift, which is an entirely different game).

>If you're really suggesting that you can't be trusted to calculate an average correctly

It's not about what you can be trusted to do, but what's worth spending your time on.


Which languages do you trust to use Knuth's algorithm (or something similarly comprehensive and exhaustive) without checking?

Not going to check many, but a quick look, since I've got them handy: Python fails Spivak's criteria, as does .NET.


I don't care. I expect it to work for the simple case, and I expect not to spend time on it unless there is a good reason (like I have very large numbers and must be careful not to overflow).


You responded to a point I made in response to Spivak. If you don't care about his argument, then it might be worth responding elsewhere.


No reason to turn something trivial into rocket science.

You examples does not mention the type of variables involved. And dependent on the type and the language your first example may be (a lot more) correct compared to your third example. Ie. negative floating point numbers.

But who says that a library implementation is a lot better than what you as a programmer can come up with?

In particular with all these "micro" libraries written by random people.

Are they actually smarter than you? Do they understand the "edge" cases as you do?

Most people don't bother with looking at the library source code they are importing. And I don't blame them because things are in general a mess and not only for JavaScript.

The solution, for me at least, is to import less and be more conversative with what you import.


Those that are unique to the problem I'm actually trying to solve, and not already in the standard libraries or well-maintained packages of mainstream languages.


Okay, but how long would you think it sensible to look for, test and assess a package for being 'well-maintained' before you'd consider it a better use of your time to average a list of numbers?

If you had to, say, add a list of numbers together, without averaging them, would your first thought be to go searching for a package, given that you know some languages have a 'sum' function? Some languages have an 'add' function (separate from the operator) - would you go looking for a package to supply an add function if you (say) needed to pass it to reduce?


>would your first thought be to go searching for a package

No, because if this isn't in the standard library (or a very simple one-liner from standard library functions like "fold +") then I don't want to be working in this language.

If I have to work in this language, and I'm allowed to bring in packages, I'd go look for the community's attempt at fixing the standard library, or at least a "math" package, particularly if there were lots of other basic math functions I'd have to do myself. If it's really just this, I'll probably paste a Stackoverflow answer.

Could I come up with it myself? Yes, but thinking through the pitfalls of the implementations of basic math operations is not a good use of time.

What would you do if your language didn't have exponentiation?


> If I have to work in this language, and I'm allowed to bring in packages, I'd go look for the community's attempt at fixing the standard library ... If it's really just this, I'll probably paste a Stackoverflow answer.

Sorry, can I get this straight, we're talking about sum() now, right? I'm genuinely amazed.

> thinking through the pitfalls of the implementations of basic math operations is not a good use of time

I suppose there's a trade off, at some point it is less time to find, check, and invest in a package rather than write its contents. For lots of cases it is definitely faster to use a package. But we're talking about averaging a list of numbers here, or adding them, right? Doesn't this strike you as rather bizarre to be having this discussion over things you should be able to write trivially?

> What would you do if your language didn't have exponentiation?

It depends what I need it for and what kind of exponentiation. I'd use inline exponentiation by small positive integers. I'd be concerned if my team were writing pow(x, 2) + pow(y, 2), for example. If I needed fractional exponentiation (e.g. pow(k, 3.567)), then I know that's beyond the realm of something that could be implemented in a couple of lines. If the language didn't have it, I might write it, certainly, especially if it wasn't part of a bigger suite of functionality I need.


I'd consider it a serious code smell for my codebase to be carrying around my personal attempt (or my team's attempt) at at half of the Python standard library. When it looks like we're heading in that direction, let's just use Python!

Sure, if it's really just arithmetic on lists, I won't be happy about it, but I'll write it. I've yet to meet a standard library which is wonderfully complete except for that one thing. If the abstractions of the environment I'm working in are that poor, there's going to be a thousand other little "trivial" things that I'm now responsible for maintaining, and some of them will turn out to be less trivial than imagined.

For example, it is "trivial" to write linked lists in C, but tracking down the one-character typo that causes them to nondeterministically explode is IMO a distraction from the project, not a valid part of it.

And what about the next project in that language? Wouldn't it be nice to factor out all that stuff and bring it with me? Well, now I am using a community standard library, but it's the community of "just me." Why not take one with some internet recommendations and an active Github?

My employer fortunately runs its own package mirrors, though, so we don't run the risk of packages just disappearing on someone else's whim.

I suppose our difference in values is that I consider each line of in-house code as much of a liability as you consider each line of other people's code.

My reaction to the article is very simple:

>What concerns me here is that so many packages took on a dependency for a simple left padding string function, rather than taking 2 minutes to write such a basic function themselves.

"So many people" writing a basic function themselves is an exactly wrong outcome IMO.


[Edit: Parent comment has been rewritten, see second response, below].

That you think averaging a list of numbers qualifies as a 'feature' you might add to a codebase is rather the surprising thing about your response. I'm aware I'm coming over rather arsey, and that isn't intended, I'm just surprised to find someone actually defend the attitude, generally, that says a trivial calculation should be either a) provided, or you're not going to use the language, or b) put in a package to mean you don't have to spend 'the time' writing it, while at the same time saying you don't care what is in those implementations.

I mean, it doesn't matter to me, obviously. You can choose to have whatever criteria for taking a job you like, and I can have whatever criteria I like for my hires. I'm just surprised. Sorry If I've come over argumentative or grandstanding.


It should, but the ECMAScript spec doesn't have it, and Node.js team prefers to keep the core libraries to a minimum and definitely do not want to modify prototypes. Thus we depend on userland to provide those features for us.


Thats why somebody should fork Node and start building a batteries included standard library.


It seems C++ approach is quite good: you have a very stable (if a bit too stale) standard library, and you have Boost, which is a cutting-edge batteries-included extension to stdlib curated by much the same people as in the standardization committee. It serves as a great _inclusive_ counterpart to the _exclusive_ stdlib and a sandbox for stdlib extension proposals.


I think the scarier thing there is this line: "0.0.1 is the latest of 2 releases"

I wonder if there will be a 3rd release with an updated averaging function.

Edit: to be fair, it was an optimization from reduce to for loop [https://github.com/bytespider/average/commit/7d1e2baa8b8304d...]


I'm thinking that someone wanted to learn about building and publishing a package and the ecosystem so they made this computationally trivial thing as a practical exercise.

Pretty much every package management system gets cruft in it like this. Example: for a long time someone had uploaded a random Wordpress core install into Bower.


And 54 people found it a worthwhile package to install _this week alone_.


That could be one dependent module that got installed in 54 places. More likely, it was mostly various bots that install everything on some semi-regular schedule.


Let's say these are just prescient trolls. For our sanity.


While it demonstrates the problem of npm lacking namespaces (such that the word "average" is wasted on such a trivial implementation)...it doesn't seem anyone was using that library



That package is, at best, average...

ducks

(Edit: typo)


Don't be .... Mean.


Yes, but it's MIT licensed. We also need average_GPL to go with it.


You bring up a good point with respect to copyright/licensing --- at what point does code stop being copyrightable and become public domain simply due to pure triviality? There are not very many sane ways to write an averaging function, and the most straightforward one (add up all the value, then divide) would probably be present in tons of other codebases.


It's a fucking nightmare, is what it is.

I wanted to use a javascript tool that would make my life easier, and when I looked at the npm dependency tree it had 200+ dependencies in total.

If I used that javascript tool, I'd be trusting hundreds of strangers, lots of which had absolutely no clout in github (low number of stars, single contributor projects) with my stuff.

And not just them, I'd be trusting that no one steals their github credentials and commits something harmful (again, these projects are not very popular).

It doesn't help that npm doesn't (AFAIK) implement code signing for packages which at least would let me manage who I choose to trust


YES YES YES!

In all the debate about this, why is the trust-dependency-fuck-show not getting more attention?

Every dependency you take is another degree of trust in someone else not getting compromised then suddenly finding all sorts of horribleness making it into your production environment.

It beggars belief!


This is more a reflection of how bad the JS language is than anything. Real programming languages have means of standardizing the most common UX and simple patterns into a standard library. Javascript is a consortium hell that never gets updated sufficiently, and has no good standard library, so NPM basically replaces the standard library with a thousand micropackages.


Also it is a lot easier to get it wrong in JS. Is it null? Is it undefined? Is it bird? Is it a plane? No it's a string!(but sometimes a number). Good programming languages make it easy to write is negative e.g. isNegative = (<0) where implicitly by the 0 and < it will take a Num and return a bool and this is type checked at compile time.


Use the null object pattern.

If you're checking a value to see if it's set by testing for null/undefined, you're doing it wrong.

This is good advice for any language, not just JS.

Besides, using null as the default for an undefined value is a mistake. Null should indicate a non-value not the absence of a value. Maybe one day the static OOP languages will die so devs have a chance to learn the difference.


My point is more that the compiler* can't detect these mistakes but in other languages you can. E.g. in Haskell you only allow Null on types when you want it (you do this by using the Maybe type). Objects where you probe for values that could be undefined or could be defined but null doesn't exist as a concept but if you badly wanted it you could use a dictionary. In Haskell if you use Maybe and you process a Maybe object you have to deal with the null and not null case otherwise you get a compiler error.

All these checks seem annoying but we've all seen the bar chart of when you discover the bug vs. the cost of that bug. The compiler finding the bug is cheap. Haskell is a cheap language to program in, compared to JS!

* I forgot there is no compiler in JS but let's say the linter for argument sake.


Maybe/Nothing is a perfect example of the null object pattern. Either provide a sane default or a typed instance with empty values rather than checking for null/undefined. Ie the 'null object' in 'null object pattern' doesn't mean using null to indicate the absence of a value.

Null isn't used in JS to mark undefined variables, that's what 'undefined' is for. Unlike static/OOP languages, null is specifically reserved for use cases where a null value is necessary. Which was the point of my comment.

If you try to access an undefined variable in 'strict mode' it throws an undefined runtime error.

JSLint does, in fact, check for undefined variables and/or globals that are accessed before they're defined in the local source (ie unspecified external globals).

So... There's that...

-----

Did you happen to notice how I didn't even remotely mention Haskell or anything related to functional programming but, please, I'd love to hear for the thousandth time how Haskell's purity is going to cure the world of all it's ills. As if the Haskell community hasn't been over-promising and failing to deliver production-ready applications for the past decade.

Unlike Haskell, JS source can be actively linted while you write it rather than requiring a separate compile/build step.

With ES6 on the client-side, modules can be hot reloaded as changes are made for instant feedback. The obvious downside being, fewer coffe breaks that can blamed on the compiler.

Have fun implementing trampolines once Haskell's 'recursion all the things' default approach to lazy-loading inevitably overflows the stack and crashes your app in production. Static type checking can't save you from logic errors. See also 'memory profiling' the phrase that shall not be uttered.

Source: http://steve-yegge.blogspot.com/2010/12/haskell-researchers-...


Purity won't cure the world of all it's ills but it may fix a bug or two before it hits ... well the developers consciousness let alone production.

I am genuinely interested in your point about trampolines and can you give an example? In Haskell foldl' for example allows you to process a big finite list quite efficiently without trampolines but yes the name of the function is a bit sucky I admit.

It's funny how you can criticize trampolines but then imperative code like this:

    var sum = 0;
    for( var i = 0; i < elmt.length; i++ ){
        sum += parseInt( elmt[i], 10 ); //don't forget to add the base
    }

    var avg = sum/elmt.length;

    document.write( "The sum of all the elements is: " + sum + " The average is: " + avg );
Is a tramp in disguise!


Yeah, there are a few shitty examples on npm. It's an open system and anyone can upload anything. The market speaks on how valuable those are. Cherry picking poor modules says nothing about the rest.

Plus, if you think that's too small, write your own broader module that does a bunch of stuff. If people find it valuable, they'll use it. If they find it more valuable than a bunch of smaller modules, you'll get 10,000 downloads and they'll get 10.

The module you roundly ridicule has had 86 downloads in the last month, 53 of which were today (at the time of this writing). I imagine most of those 53 were after you posted. So that's 40 downloads in a month, as compared to the express framework which has had 5,653,990 downloads in the last month.

The wailing and gnashing of teeth over this module is ridiculous.


DRY taken to the dogmatic extreme where everything is made up and the github stars are the only thing that matters.

This article touches on things that are wrong in the javascript culture. I always had this nagging feeling when working with NPM, this article brings it to light. For what it's worth I never felt this while writing Ruby, C# or Go.

It's the -culture- that needs to change here, not the tools.


The recursive folder structure in npm-modules was the first indication. At least Java had a single tree with com.bigco.division.application.framework.library.submodule.NIHObject.java


That recursive node_modules or whatever it is called was what made me hate this whole npm thing, specially because it is not centralized somewhere in my computer. And that means the same files a few times repeated on my drive just eating space. Being a Java developer I don't understand why the approach was not more like maven.


npm does a decent enough job deduping shared packages. The reason is different packages consume different versions of things. Java only lets you have a single version of a package. If the API changes, it can be a pain when your dependencies were written for different versions. That slows adoption since you have to wait until everyone you depend on has updated first.


Are you thinking of the new flat-tree npm? Because in my 1.3.10 installation, I see this:

    $ find node_modules/ -type f | wc -l
    56278
    $ find node_modules/ -type f | xargs md5sum | gawk '{print $1}' | sort | uniq | wc -l
    8545
That's a lot of duplicate files.


its a long standing issue, and gets worse when there is node_modules inside node_modules inside node_modules ... so on that someone suggested renaming node_modules to n_m . https://github.com/nodejs/node-v0.x-archive/issues/6960#issu...


"This isn't your average everyday darkness.... this is Advanced Darkness." - S. Squarepants


You should then go dive into the passAll function:

https://github.com/tjmehta/101/blob/master/pass-all.js

It’s written in such a way that every time you call...

    passAll(f1, f2, ..., fn)(args..)
... there are something like 5 + 2n attribute accesses, 5 + 3n function calls, 3 + n new functions created, as well as some packing and unpacking of arguments, not including the actual application of the functions to the arguments that we care about. That’s in addition to the several functions defined in the dependent submodules, which you only have to pay for constructing once.

[From my quick eyeball count. These numbers could be a bit off.]


I'm more disappointed that it doesn't short-circuit the operation at all. It applies all the functions, THEN it determines whether all of them passed. Even worse, it uses `every` (which does short-circuit) to determine that all the functions are, indeed, functions, but apparently the ability to use that function to determine whether every predicate passes was missed.


It could have been a simple for loop with a break whenever one evaluates false. Blazing fast in every javascript engine, and easy to see all the code in one place.

Instead, it’s a slow tower of bloat, which you need to read 5 files to reason about.

Javascript implementation:

    function passAll() {
      var fns = [].slice.call(arguments);
      return function() {
        for (var i = 0, len = fns.length; i < len; i++) {
          if (!fns[i].apply(null, arguments)) { return false; }
        }
        return true;
      };
    }
Or Coffeescript (including the function check):

    passAll = (fns...) ->
        for fn in fns then if typeof fn isnt 'function'
            throw new TypeError 'all funcs should be functions'
        (args...) ->
            (return false if not fn args...) for fn in fns
            true


Lol, nice, I wrote "pass-any" first. I then copied the code and replaced "or" w/ "and" to create "pass-all".

I will probably have go back and change this now that I know about it. In general though, not gonna a lie, I am not very concerned about micro performance optimizations.


If it were being used only in places in the code where it was rarely called, it would be fine to create a handful of extra functions, make a few dozen function calls, etc. In my opinion, anything that aspires to be a library of basic infrastructure type code should try to have efficient and simple code at the expense of being slightly syntactically longer than the highly abstracted version.

Because if people start using it for such things as an "is positive integer" check (Which, to be fair, they should not be doing. Nobody should be constructing 2 new javascript functions every time they want to check if an object is a positive integer. But apparently they are...), then it could easily make its way into somebody’s inner loops.

The end result of this general culture (not picking on anyone in particular) is that trivial Javascript programs end up having their performance tank, because every tiny bit of useful real work gets weighed down by two orders of magnitude of incidental busywork creating and tearing down abstractions built on towers of other abstractions. Because CPUs aren’t very well optimized for this kind of workload, which involves lots of branching and cache misses, it’s worse still, probably more like 4–5 orders of magnitude slower than optimized code written in a low-level language.

Ultimately every idle facebook page and gmail page and news article in some background browser tab ends up sucking up noticeable amounts of my laptop’s CPU time and battery, even when they’re doing nothing at all for me as the user.


Yeah, I thought the dependency thing was a joke (I mean, to have a package for is positive integer is already a joke, but come on)

Really


I think it actually is not. That's from some years ago and my memory of it is fuzzy, but at that time it was surprisingly hard to check whether a variable is a positive integer – maybe it was a negative one though and that was harder? You'd think it is just checking whether it is an integer and bigger than 0, or just checking whether it is bigger than 0. And it is. But to get that code to work reliably, regardless of whether it gets a string or float or an undefined, with the JS type system of that time and in multiple browsers, even the crappy ones, that took some time. There was one specific edge case involved.

Not that it was impossible, but I still remember having to search for it and being astonished that that was necessary.

Sure, should not apply anymore like that.


Be that as it may, is-positive doesn't handle any edge cases https://github.com/kevva/is-positive/blob/master/index.js

That's half the problem with these stupid micromodules; they're not abstracting complexity, they're obfuscating simplicity.


Don't know. If that approach works reliably, I'd see some value in it. Maybe this does not need any more code if you do it that way. If it does not work reliably, than you are absolutely right and that were useless.


Yea. I don't think many argue against abstracting complexity into more easily understood orthogonal modules. But some of these modules aren't abstracting complexity. They are ludicrous. They are nonsense. They are a symptom of a very real problem with how JS is programmed, how people expect to program in JS, and the difficulty people have with decomposing problems.

So many people on this page have written about how these are well tested, performant, and correct modules. But, the modules aren't even correct in many cases, let alone providing incomplete coverage over edge cases or the slow performance and horrendous dependencies of many of the modules.


it's a module with exactly one dependent module -- by the same author. Daily WTF material but who cares beyond that?


Formerly 9 dependent modules... but who cares maybe it was a homework assignment or a joke.

I don't use NPM so maybe it doesn't really matter aside from the level of abstraction being implemented being relatively ridiculous.

However, if my build system had to go out and grab build files for every X number of basic functions I need to use, grab Y number of dependencies for those functions, run X * Y number of tests for all those dependent packages, AND then also fell apart if someone threw a tantrum and removed any one of those packages basically shutting me down for a day... then I'd question every single thing about my decisions to use that technology.

[Quick Edit] Basically I'm saying "Get off my lawn ya kids!"


> I don't use NPM so maybe it doesn't really matter aside from the level of abstraction being implemented being relatively ridiculous.

Except, again, it's no more ridiculous than pick a random bit of code from anywhere and isn't any more emblematic than that random bit because, again, it's not used anywhere.

> [Quick Edit] Basically I'm saying "Get off my lawn ya kids!"

tl;dr: going for buzzfeed headlines but for comments, got it. Good job, I guess?


Trying to keep the conversation light-hearted with a closing joke is more like it since everybody takes these things so seriously-- sorry I pissed in your cheerios good chap!


This implementation reads almost as parody, although I don't suspect that the author meant it as such. If you didn't have a sense of what lurked behind the abstraction, it would be kinda beautiful.


I can't decide what's crazier to me: that such a package exists, or that JavaScript is such a ridiculously bad language that an "is positive integer" function is non-trivial to write.


I end up spending most of my working life working on other peoples code, rather than new features I end up debugging and fixing bad code. (I actually rather like it)

The majority of code I have ever seen is awful (20 years across large + small companies) but that is why I am hired to fix awful code so I am skewed. The amount of times I have seen people implement something simple, in a convoluted error prone was is unbelievable.

I know this seems ridiculous but when you see time and time again how people fail to do the simplest things it seems like a good idea.


I had several arguments about JS and I was shocked how many developers consider this platform great. I am not sure why these extremely bad practices are defended by devs, what are they getting out of it? I am only hoping we are moving towards more sensible development environment, there are many of them with better best practices and more sane libraries.


Sure, because just writing

  isPositive = (x) => +x === x && x > 0 
is boring. Need more requires to require the requires...

Keep in mind though that absolutely not all JS programmers are like that. Not everyone wants to be an aforementioned Dick-from-a-mountain.


> They've already taken it to an entirely different level of insanity.

Why is this insane? What alternatives would be better?


You create unit tested functions and expose them to client code with a factory pattern, how is that so ridiculous?


I have an extremely hard time believing this module is not a joke, ditto for many other NPM modules.


What I want to know is how many systems does the author of is-positive-integer have implicit root access to?

Sounds to me like publishing oneliners on NPM is a trivial way to build a botnet.


Seems like the answer is "none" by default, from https://docs.npmjs.com/misc/scripts#user

> If npm was invoked with root privileges, then it will change the uid to the user account or uid specified by the user config, which defaults to nobody. Set the unsafe-perm flag to run scripts with root privileges.


That's good, even if nobody is perfectly enough to a botnet. I thought more of the user running the node application.


I think this is some sort of cargo cult UNIX minimalism - do one thing and one thing only.


No, its a byproduct of programming-interview-via-GitHub-repo


For some reason I misread that as "do one thing and do one thing ugly". Either way, it applies.

Lodash does this (and versioning, and clean code, and tests) really well though.


require the ultimate


Philistine! You should just be doing i things.


I can't stop laughing. I think you have to admire the elegance of the concept as performance art though: this is cheap insanity. In fact, I've got to hand it to them, this is the most fun I've had looking at something programming related in a while. I recall the opening lines of SICP,

> I think that it's extraordinarily important that we in computer science keep fun in computing. When it started out, it was an awful lot of fun. Of course, the paying customers got shafted every now and then, and after a while we began to take their complaints seriously. We began to feel as if we really were responsible for the successful, error-free perfect use of these machines. I don't think we are. I think we're responsible for stretching them, setting them off in new directions, and keeping fun in the house. I hope the field of computer science never loses its sense of fun. Above all, I hope we don't become missionaries. Don't feel as if you're Bible salesmen. The world has too many of those already. What you know about computing other people will learn. Don't feel as if the key to successful computing is only in your hands. What's in your hands, I think and hope, is intelligence: the ability to see the machine as more than when you were first led up to it, that you can make it more.

Quoted in The Structure and Interpretation of Computer Programs by Hal Abelson, Gerald Jay Sussman and Julie Sussman (McGraw-Hill, 2nd edition, 1996).


It really is the fault of the language that something this fundamentally horrible is allowed to exist in itself.

When people ask why Javascript is terrible, show them this.


The same could be said for your post. When people ask why commenting on message boards is terrible, show them this.

If you don't want to write modules this way, don't. Nothing about javascript requires that you even read articles about modules that you don't want to use. Or read articles and then follow up by posting on message boards about articles about modules you aren't going to use.

Bang some code out instead. Your opinion of javascript is about as valuable as the opinion of the person who wrote the modules.


When every major Node package depends on this, starting with Babel, then it actually does becomes something thats required.

There's no such thing as "don't use it if you don't like it!" in this case.

Javascript is a completely broken, shameful system that rightfully deserves all the scorn it's gotten. It really is the worst.

(note that i actually write Python & VanillaJS, and avoid Node packages entirely for now.)


Counter-argument:

A good micro-module removes complexity. It has one simple purpose, is tested, and you can read the code yourself in less than 30 seconds to know what's happening.

Take left-pad, for example. Super simple function, 1 minute to write, right? Yes.

But check out this PR that fixes an edge case: https://github.com/azer/left-pad/pull/1

The fact of the matter is: every line of code I write myself is a commitment: more to keep in mind, more to test, more to worry about.

If I can read left-pad's code in 30 seconds, know it's more likely to handle edge cases, and not have to write it myself, I'm happy.

The fault in this left-pad drama is not "people using micro-modules". The fault is in npm itself: all of this drama happened only because npm is mutable. We should focus on fixing that.


> every line of code I write myself is a commitment

That's true. However:

Every dependency you add to your project is also a commitment.

When you add a dependency, you're committing to deal with the fallout if the library you're pulling in gets stale, or gets taken over by an incompetent dev, or conflicts with something else you're using, or just plain disappears. If you add a dependency for just a few lines of code, you're making a way bigger commitment than if you'd just copy/pasted the code and maintained it yourself. That's why so many people are shaking our heads at a 17-line dependency. It's way more risk than it's worth. If you need a better stdlib for your language (some of us write PHP and feel your pain) then find one library that fills in the gaps and use that.


> If you add a dependency for just a few lines of code, you're making a way bigger commitment than if you'd just copy/pasted the code and maintained it yourself.

This is a problem with NPM, not with dependencies. With different package management systems with stable builds and lockfiles, then you pin to a specific version and there is no way upstream can cause problems. A lockfile is a pure win over vendoring.


Yes, there is. Just like NPM's left-pad case. The owner of the package remove the package from the repository. It doesn't matter if you pin to any version if there is no longer a code to download.

The only way to prevent this is to have your own local server for third party package repository.


There are package management systems out there that don't allow package owners to remove existing code in a way that breaks downstream users.


Your own mirror of third party packages is definitely the way to go. I'm continuously stunned this is not standard practice.


It's also a problem with NPM, just don't let people remove older versions of things.


You're talking about the purely technical aspects of package management that keep your build from breaking. My point is that there's a lot more to it than that. Lockfiles do not keep software from requiring maintenance. Introducing a dependency means handing off a chunk of your software project to someone else to maintain. If they do it wrong, you're on the hook for it.

For example, I was a maintainer for an admin UI component of the last version of Drupal core, and we decided to pull in a query string handling library written by Ben Alman. It was a good decision, and it hasn't caused any problems. But it still meant we were trusting him to maintain part of our codebase. It was also an implicit commitment to every user of Drupal that if Ben quit maintaining that library, we would step in and fix any problems that came up. You don't get rid of that commitment with a lockfile.


But if you write it yourself, you're also on the hook for it. So I don't see a meaningful difference here.


Here is an oversimplified model to illustrate my basic point:

A dependency introduces some constant amount of risk (d) that does not vary with the size of the dependency. Every line of code you write yourself also introduces a much smaller constant amount of risk (y).

If you introduce a separate dependency for every line of code in a 1000-line project, your risk is 1000d.

If you can pull in someone else's code for the whole thing and don't need to write any code yourself, your risk is d.

If 200 lines of your code can be replaced with an external library, your risk is d + 800y.

I think the real disagreement here is over the value of d. My experience leads me to put the value of d pretty high relative to y, so to me 1000d is the worst possible case. If someone sees d as equal to y, then they'd see dependencies as no problem whatsoever.

(Obviously in reality the risk of a dependency is not really constant - it's probably more like d + 0.1y or d + 0.01y or whatever, since a 10-line dependency is less risky than a 1000-line dependency. Hopefully my point still stands.)


OR: you depend on a specific version of the library, that you know that works, and you have none of those problems.


You can't escape problems by bundling specific library versions. You just get a different set of problems. When you require a specific version of a library, you're making your code incompatible with anything that requires a higher or lower version of that library. You're also assuming there will never be a security fix that requires you to update your dependency.


...you're making your code incompatible with anything that requires a higher or lower version of that library.

Actually that's not correct when using node/npm (or anything else in the ecosystem like browserify). That is one of the impressive things about this platform: any number of different versions of the same module can be required by the same app. It would be nuts to do that in your own code, but as long as the craziness is in your dependencies it really doesn't matter.


And that kind of works in a dynamic language. You could make it work in a statically-typed language, but then the problems will become more apparent. If X depends on Y and Z1 and Y depends on Z2, and Y exposes an object created by Z2 in its public API, the X developers might look at the Y api docs and try to call Z1 functions on that Z2 object! Worst of all, it might very well work in the normal cases, and the issue might not be noticed until it's in production.

Using multiple versions of the same library is code smell. It's a stability issue, a security issue, a complexity-breeder, and an absolute nightmare for packagers.

And npm allows it to happen silently.


Yeah I'm sure it sucks for distro packagers. Why are they using npm, though? It's not designed for their use case.

Actually though you're just talking about some bugs in X, or possibly some design flaws in Y. Passing [EDIT, because bare objects are fine:] class instances around like that is the real code smell. So much coupling, so little cohesion. We call them "modules" because we like modularity.


It's not that packagers are using npm. It's that they might want to package a node application for their distro's package system, and now they have to sift through thousands of npm packages (already a nightmare). They can't just make a system package for every npm package, not just because that would violate packaging guidelines for any reasonable distro, but because one project can pull in multiple versions of a single package. The Nix and Guix crews can handle that (and that they can is as much of a bug as it is a feature).

There is no clean way of packaging a typical node application.

Passing class instances around like that is the real code smell.

Often, yes, but not always. Allowing fine-grained control over performance is one good reason that a library might expose class instances from one of its dependencies.


Is Node.js itself appropriate for packaging? I think maybe not. It changes really quickly, and has done for some time. Anyone coding in Node installs the particular versions she needs without regard to the distro. Most Node modules are just libraries installed in and for particular projects. There are tools written in node, but for the most part they focus on coding-related tasks that also tie them to particular projects, e.g. beefy or gulp. There's no need to install such tools above the project level, and certainly no reason to install them on the system level.

A distro that still packages python 2 (i.e. all of them) has a particular "velocity", and therefore it has no business packaging Node or anything written in it. Maybe a distro could package TJ's "n" tool (helpfully, that's written in bash rather than node), which would actually be handy for distro users who also use Node, but that's it.


I'm not talking about packaging node libraries for developers. No node developers are going to use system packages to install their libraries. What I mean is packaging applications written in node for end users.

For example, you can install Wordpress on Arch with `pacman -S wordpress' and you'll have a managed wordpress installation in /usr/share/webapps/wordpress. Then you just edit some wordpress config files, set up your http server to serve php from that directory, and you have a wordpress blog.

It would be nice to be able to do the same with Ghost.


Ghost may be a special case. I wasn't familiar with it, but I just attempted to install in an empty directory without success. The first time I ran "npm i ghost", with node v5.9, it went into an infinite loop creating deeper and deeper ".staging/ghost-abc123/node_modules/" sub-directories of node_modules, which seems an... odd thing to do. After killing that, I noticed that they recommend Node LTS. Fair enough. I ran "sudo n lts", then "npm i ghost" again. This time, I didn't have to kill it because the preinstall script errored out. Based on the log, this script is installing semver, then requiring a file that can't possibly exist at preinstall time. Both of those are obnoxious, but at least it's possible to install semver.

I'm sure if I look hard enough there are some silly idiosyncratic steps one might take to install this module. Suffice it to say that it's not installing the "npm way", so it's misguided to blame npm for packaging difficulties.

More generally, I can certainly understand distro packagers' refusal to recreate towering pyramids of node dependencies in their own package system. Some lines have to be drawn somewhere, and "major" modules must bundle many of their dependencies when packaged for distros. If module maintainers don't do this, and distro packagers can't, then the modules can't be packaged.


...and now you can have all the bugs introduced in all the versions of the library! Yay!


Or you could have all the bugs introduced in everyone's hand-rolled implementations of it. I'll take multiple versions of the library instead. It's much easier to track issues and submit patches to update their dependencies later.


> Or you could have all the bugs introduced in everyone's hand-rolled implementations of it.

Only one buggy implementation per project. Compare this to including the same library in dozen different versions, because dependencies have their own dependencies. And you can neither track the versions nor update them.


More importantly, if you only use a specific version of a library, you're opting out of literally every one of the advantages of micro-libraries that people claim they offer. Tying yourself to a single version is the same as copy-pasting the code right into your project, except it doesn't force you to look at the code and vet it.


And writing the code yourself instead of taking on a dependency solves none of these problems. Your code becomes incompatible with anything, because you wrote it yourself. And you are responsible for making any security fixes yourself.


You just get a different set of problems. When you require a specific version of a library, you're making your code incompatible with anything that requires a higher or lower version of that library. You're also assuming there will never be a security fix that requires you to update your dependency.

If there is a security fix, you should bump your dependency by hand, the other problems that you pointed out do not exist in Node (and it's about time they disappear in Java)


I'll admit I'm at least a little tarnished in my practices due to time spent in enterprises where external dependencies require 6 manager sign offs and a security team exemption, but if this were the case that you didnt want updates to the package, just that one version that worked -

If its just a few lines of code, just copy the thing into your code base? throw a comment in saying "came from xxxx" so anyone reading your code knows that it might look like a overly generic function because it is.


...and then the publisher pulls their library off npm, and another shows up and drops one of the same name in its place, with compatible version numbers (by happenstance or otherwise).


That's exactly the problem the parent comment suggests we focus on fixing. Once a library is published, npm shouldn't allow anyone to use that name even if the library is pulled.


A version can't be republished.


True, but it's common to have requirements of the form "^1.0.0" (especially since this is the default of npm i --save). It's easy to publish a new version that would be installed by a project declaring a dependency in this form.


Yes, but it's trivial to pin your dependencies exactly. That's not a reason to avoid small modules.


Maintaining a dependency on a library should be much less effort than maintaining 17 lines of code. If it isn't that's a deficiency in your dependency infrastructure.


If you have 100 dependencies, then that's a 100 projects you need to follow and understand the updates for. The minute your dependencies bring in their own dependencies then you start having troubles keep up or even keeping track of the updates. The 17 lines of code you pull in is in most cases a one time deal, having it in a third party library means that you need to keep track of that library for ever.

Honestly, and this is maybe just me being biased against JavaScript, then this is what happens when you pick a language fully knowing it's limited in all sort of silly ways and attempt to use it as a general purpose language. It's not that you can't, but if you need say typing that can tell you if something is an array, then maybe picking JavaScript to begin with wasn't the brightest idea.

There's a ton of libraries and hacks out there, all attempting to fix the fact that JavaScript isn't really good general purpose language. ES6 is fixing a lot of these thing, but it's a little late in the game.


I wouldn't mind so much if these micro-modules were written in a style of thoroughness; heavily commented, heavily documented with pre-conditions, post-conditions and all imaginable inputs and outputs explicitly anticipated and formally reasoned about. I don't mind over-engineering when it comes to quality assurance.

Looking at that left-pad module though - no comments, abbreviated variable names, no documentation except a readme listing the minimally intended usage examples. This is not good enough, in my opinion, to upload to a public repository with the objective that other people will use it. It is indistinguishable from something one could throw up in a couple of minutes; I certainly have no reason to believe that the future evolution of this code will conform to any "expectation" or honour any "commitment" that I might have hopefully ascribed to it.

[EDIT: I've just noticed that there are a handful of tests as well. I wouldn't exactly call it "well tested", as said elsewhere in this thread, but it's still more than I gave it credit for. Hopefully my general point still stands.]

The benefits of reusing other people's code, to a code reuser, are supposed to be something like:

(a) It'll increase the quality of my program to reuse this code - the writer already hardened and polished this function to a greater extent than I would be bothered to do myself if I tried right now

(b) It'll save me time to reuse this code - with the support of appropriate documentation, I shouldn't need to read the code myself, yet still be able to use it correctly and safely.

Neither of those things are true for this module. It's not that the module is small, it's that it is bad.

(True that npm's mutability is a problem too - this is just a side-track.)


Completely agree here - the problem isn't micro-modules. It's partly just a lacking standard library for javascript and largely just exposing issues in npm that the community was pretty ignorant of until just now.

The whole "amaga, it's a whole package for just ten lines of code" is just elitism. Given the number of downloads on things like left-pad, it's clearly useful code.


Agreed as well. In fact, I would posit that this wasn't even really a problem until npm@3 came out and made installing dependencies far, far slower. Yet it was necessary; a standard project using babel + webpack installs nearly 300MB (!!!) of dependencies under npm@2, and about 120MB under npm@3. Both are unacceptable, but at least npm3 helps.

I want to plug ied (https://github.com/alexanderGugel/ied) here, which both installs about 5x faster than npm@2, yet deduplicates as well as npm@3.


No, no, no

Left padding is (almost in all languages) built-in, even C can do it with printf (edited)

The problem is not having a library that offers that, but having this micro-module thing as a whole NPM module. No other language does that.

If it was inside a string helpers, that's great.

But don't make me one single module for just left-padding (or is-integer-number)


I think there's two points related to that:

1) JS is unique in that it is delivered over the wire, so there is a benefit in having micro-modules instead of a bigger "string helpers" module. Things like webpack are changing that now (you can require lodash, and use only lodash.padStart).

2) JS's "standard" library is so small, because it's the intersection of all of the browser implementations of JS dating as far back as you care to support. As pointed out in sibling, a proposal for padLeft is included in ECMA2016. But we'll still need Left-Pad for years after it's adopted.


Point 1 was addressed years ago by Google Closure Compiler, which used "dead code elimination".

Also, the Haxe language, which compiles to JS, has DCE.

Micro-modules is certainly a solution if you don't want to use pre-processing or compilers. So is copy/pasting, or manually writing your own util libs, which seems safer than relying on third parties to provide one-liners for you.


Eh, to date, a large part of the JS community still recommends including .js files in script tags in HTML. So, while this has been possible for a while, there hasn't been widespread adoption.


I'm not sure dead code elimination works in that situation. Consider:

  var obj = {
   a: ...
   b: ...
   c: ...
  }
If a, b, and c are functions, there is not necessarily a way to determine at compile time whether they will be used at runtime.

  var prop = webrequest();
  obj[prop]();
In that scenario, a, b, and c cannot be eliminated. But it would be worth testing Google Closure Compiler to see what it does in what scenarios.

I've heard ES6 modules solve this problem, but it seems like dynamic access to an ES6 module might still be possible, which would cause the same problems for DCE. Perhaps no one writes code that way, so it doesn't necessarily matter. But what about eval?

There are lots of tricky corner cases. It seems better to use small atoms than a monolithic package.


In simple mode, Closure Compiler would rename the local variable "prop" but not alter the properties.

In advanced mode, Closure Compiler would globally rename properties and statically eliminate dead code. In your example, it would remove a, b, and c and break the dynamic invocation.

This behavior is all outlined in Closure's documentation with examples.

[1]: https://developers.google.com/closure/compiler/docs/limitati...


Im not really sure how this is an argument against DCE. If theres no way to tell at compile time if these functions will be used, then you have to include them in the final delivered code, whether or not you are using monolithic or micro packages or dead code elimination.

DCE will help you if you pull in huge-monolithic-package and only use one function from it. In that case its basically the same as if you had used the micro package.


Javascript must be written specifically to anticipate Google Closure Compiler's dead code elimination.

Since the vast, vast majority of Javascript doesn't work with it, the issue certainly wasn't addressed years ago.


"Addressed" doesn't mean "solved!" ;)

Yes, you're right, making use of dynamic behaviour will break DCE.


> 1) JS is unique in that it is delivered over the wire, so there is a benefit in having micro-modules instead of a bigger "string helpers" module. Things like webpack are changing that now (you can require lodash, and use only lodash.padStart).

JS isn't even remotely unique in this regard, almost every static language has had dead code removal for decades.


I agree with you, but they weren't saying that JS is unique for having dead-code elimination, but rather for having to be served over a network connection nearly every time it's needed (not accounting for caching and offline apps, etc), which presents an entirely new set of problems that webpack and others attempt to solve.


Which is why it's so unsuited for writing non in-browser applications. JS was made for a purpose. We hacked it and discovered (or at least made popular) the benefit of having a fully async STD lib, of using callbacks to manage incoming connection and so on.

The wise course of action would be to take those very good ideas and bake them in languages designed for web/system programming, and keep improving JS for in-browser tasks.


A in-browser web application requires the left-pad module. Should it include the 500kb library that includes a left-pad function, or import just the 10kb version, which having left-pad function as a module by itself allows?

Yes you can use left-pad module in a browser application using the npm install infrastructure.


JavaScript much to my dismay has no sprintf. It's one of the most annoying omissions ever for server side work.


The biggest thing I've run into is that there's no built-in `mkdir -p`. There is, of course, a module that is literally just mkdirp.


require('util').format gets you pretty close.


No it really doesn't. It is so far away from even Perl's printf (considered ancient by most JS programmers) that it is not even funny.


es6 has one


No it doesn't. It comes with template strings. Any sprintf type functionality requires you to call functions on the input. It is a tiny tiny step forward.


Uh, no Left Padding is NOT built-in in JavaScript. The proposal to add `String.prototype.padLeft()` was just added to ECMAScript 2016.

JavaScript had a very minimal standard library, it's pretty asinine of you to compare it to C or any other language with a pretty extensive standard library.


C didn't get a standard (or a standard library) until 1989. It had been around for 17 years at that point. Two years after its invention, JavaScript was standardized in 1997. That's almost twenty years ago.


I've never thought of C as being a particularly good example of "extensive standard library."


I wanted to say that it's built-in in pretty much every other language apart from JS


But alas, here we are, talking about the JavaScript language and it's ecosystem.

It's easy to say "I don't see why there's a need for an 11 line module to pad left on a string" when your language of choice has a robust standard library with such abilities built in.


Another interesting argument for micro-modules:

https://github.com/sindresorhus/ama/issues/10#issuecomment-1...


Wow, I feel like I could have written this. Back when I used Python, I had a folder full of functions I would copy-paste between my projects. (And maybe some of the projects contained unit-tests for the function. I didn't always keep those tests in sync.) Updating them was a pain because inevitably each one would get slightly modified over time in each project separately. Eventually, I bundled all of them into a bundle of completely unrelated utility functions in a folder on my computer somewhere, and I would import the folder with an absolute path. Sharing the code I wrote was a pain because of how much it referenced files on my local computer outside of the project. I never considered publishing my utility module because all of the stuff was completely unrelated. I'd rather publish nothing than a horrifying random amalgram that no single project of mine was even related to all of the subject matter present in it.

With npm and the popularity of small modules, it was obvious that I could just cheaply publish each of my utility functions as separate modules. Some of them are about a few dozen lines, but have hundreds of lines of tests and have had significant bugfixes that I am very happy that I haven't had to manually port to dozens of projects. I don't miss copy-pasting code across projects, no matter how many claim I've "forgotten how to program".


I feel exactly the same.

There is something about JavaScript that makes people go a little crazy both for and against it.

I've never seen so many programmers advocate copy/pasting code before...

But regardless of how many insults get thrown around, or how many people seem to think JS is useless or that it's a horrible language, its probably my favorite (and I've done professional work in non-trivial applications from C and C++, to Java and go, to python, Ruby, and PHP to BusinessBasic and even some lisp).

I'm going to keep writing stuff in JS, and I'm going to keep loving it. Regardless of how many people are telling me I'm wrong.


I'm going to keep writing stuff in JS, and I'm going to keep loving it.

Why do you like it? Serious question.


I'm very hesitant to answer this, as i know it will bring on angry comments and people telling me i'm wrong, but i'll give it a shot (this is all literally off the top of my head right now, so if you are going to poke holes in it, cut me some slack)

This got a lot bigger than i thought, so strap in!

* The lack of "private" anything. This sounds like a bad idea, but I firmly believe it was a major reason for JS's success. The ability to "monkey patch" anything including built-in functions and other libraries means that everything is extendible. It isn't something i do very often (mucking around with internals of another module/system) but when i do it's really fun and generally solves a problem that otherwise would be unsolvable.

* The debugging. Oh the debugging! It's magnitudes better than anything i've ever used before. And i don't just mean in features (i know that other langs have feature X that JS doesn't have, or can do Y better). I can use multiple debuggers, inspect EVERYTHING, breakpoints, live inline code editing, remote-debugging (on pretty much every mobile device), world-class profiling tools with memory usage, cpu usage, JIT performance, optimizations/deoptimizations, etc... Hell Edge is even getting "time travel debugging" where i can step BACKWARDS in code, edit it in place, then play it forward again! Also, sourcemaps! I can compile python/coffeescript/typescript/javascript to javascript and then minify it and combine multiple files, but when i open the debugger i see my source files, with their full filenames, and the execution is synced statement-by-statement. And they work for CSS too! And I almost forgot about the best part. Since they can be separate files, i can include them in my production build with literally 0 overhead. So if there are any problems with the live build, i can open the debugger and have the full development-mode first-class debugging experience, on the live site, even on a user's PC if i need to. Hell i can even edit the code in-place to see if my fix works! This one is probably one of my favorite features of javascript and it's ecosystem.

* async programming. Yeah, i know other languages have it, but JS is the first time where i would consider it a "first class citizen" Everything is async, it's amazing, and it's to the point that if something isn't async, it's almost a bug. And this combined with the event system and the single-threaded-ness means writing performant code is more "straightforward" than i've experienced in other languages. Combine this with web-workers (or threads in the node ecosystem) and you get the best of both worlds.

* the mix of functional and OOP programming. Functional programming sucks for some things, OOP sucks for others. I feel like in practice JS lets me use the best of both. Yeah, it's not "pure" or "proper", yeah you can use the worst of both, but i love it. You can add the mix of immutable vs mutable in this as well. By having both, it lets me choose which i want to work with for the current problem, even switching in a single project.

* it's fast. JS is pretty fucking fast in the grand scheme of things. Yeah, it's not C, but with typed arrays and some profiling (which JS makes oh so easy!) it's possible to wipe the floor with Python, Ruby, PHP, and can even give Java and Go a run for their money. For such a dynamic language, that's impressive.

* the compilation options. From coffeescript/typescript/flow, to just compiling between js "dialects", and adding non-standard (or extremely new) features to the language is "easy". It took me a little while to get used to being that disconnected from the final output, but once i "let go" of that, i found i loved it. With babel plugins i can add extra tooling, or extra type-checking, or even completely non-standard stuff like JSX or automatic optimizations into the code that i output. Combined with some good tooling i can even change how the code executes based on the "build" i'm generating (for example, i have some babel plugins that optimize react elements to improve first-load speed, but i only run it on staging/production builds because it is pretty verbose (which gets removed when gzipped) and is difficult to debug)

* the tooling. auto-refresh, hot-module replacement, automated testing, integration testing, beautiful output in multiple formats, linting, minifying, compressing, optimizing and more task runners than you'll ever need. The fact that i can write code, save, and have that code hot-replace the code currently running in my page on my PC, tablet, phone, laptop, and VM all at the same time. There is nothing that even comes close to this. At all.

* and i guess finally, npm. The fact that there are 5+ different modules for everything i could ever want. The fact that i can choose to install a 3-line program, or write it myself, or install it first and write it myself later, or vice versa. The fact that i can choose a module optimized for speed, or one for size. The fact that i can get a pure-js bcrypt and a natively-compiled bcrypt with the exact same API and install the native and if that fails fallback to pure-js. The fact that NPM installs are so effortless that i have a project with about 100 direct dependencies (and most likely about 1000 when it's all said and done), and there isn't even a hint of a problem is wonderful (this is a bit of an edge case though, most of the packages installed here are "plugins" like babel plugins, postcss plugins, and i'm purposely avoiding bundled deps kind of for shits-n-giggles.) And no matter how many internet commenters keep telling me i'm wrong, i haven't had any issues with it.

This got a lot bigger than i had intended, but the point is that while JS might not do any one thing very well, it does many things pretty damn well. And the benefits far outweigh the downsides for me.

I'm going to bed for the night, so if you reply don't expect an instant reply, but despite the "standoffish" nature of a lot of this, I want to hear responses.


Thank you for taking the time to write this out. This is probably the best description I have seen of why it is enjoyable to write JavaScript. I myself have been programming professionally for 15 years, writing C, C++, Scheme, Java, Rust, PHP, Python, Ruby, shell script, etc. I actually enjoy C and Rust, have a tremendous respect for Scheme, Clojure, Haskell, Scala, ML, etc., yet I always reach for Node and JavaScript because of the great JIT, instant startup time, great debugging tools, and ease of use of npm.

To add to the part about extensibility, many times I have jumped into a "node debug" session, in my own code or in 3rd party modules installed to node_modules. Many times I have added breakpoints or tweaked 3rd party code right in node_modules, or monkey-patched methods temporarily. This kind of thing is often nearly impossible, very time consuming, or just plain difficult to do in other languages.


Interestingly to me, a lot of your points apply to my own favourite language, Lisp.

Regarding a lack of private anything, it's possible to monkey-patch any Lisp package or class however one wants. And of course, one can get true privacy in JavaScript if one wants, by using closures — the same trick applies in Common Lisp.

Lisp debugging is great: one can set up all sorts of condition handlers and restarts, and invoke them from the debugger.

Lisp is a great blend of imperative, functional & object-oriented programming styles, enabling you to use the right tool for the problem at hand.

Lisp is incredibly fast, faster than C & C++ in a few cases and almost always Fast Enough™. It's dynamic, but inner loops can be made very static for performance. There's even a standard way to trade off safety and performance, if that's what's important in a particular case.

I don't know for certain, but I believe that Lisp was the language that invented hot-patching (well, I suppose one could always have done it from assembler …). It was even used to debug a problem on a NASA probe[0]: 'Debugging a program running on a $100M piece of hardware that is 100 million miles away is an interesting experience. Having a read-eval-print loop running on the spacecraft proved invaluable in finding and fixing the problem.' As with JavaScript, the Lisp debugger is part of the standard and is always available. This can be profoundly useful.

And Quicklisp is a nice, modern way to pull down numerous libraries.

Lisp does of course support async programming since function are first-class, although I'm not personally as much of a fan as you are. Generally, I think that callback hell should generally be avoided.

I'm not aware of a lot of compile-to-Lisp projects, but given that the language is great at treating its code as data, it's an excellent target.

It certainly doesn't have the huge ecosystem that JavaScript does, but that improves with every developer who starts a project.

I really, really wish more folks would take a look at it. The more I use it, the more I realise that a 22-year-old standard has well-thought-out solutions to problems that people still face in other language environments today.

[0] http://www.flownet.com/gat/jpl-lisp.html


I agree on pretty much all accounts.

The only thing i have to add is that while callback hell sucks, there have been some pretty recent (in the grand scheme of things) additions to the async programming field. Async/await is beautiful, and it has made me fall in love with async programming all over again.


  >  I can use multiple debuggers
What does this look like, and how does it improve the experience over using a single debugger?


I meant that more that I have multiple options to choose from.

Multiple browsers means there are multiple competing sets of debugging tools, each are good at some things and worse at others. For example, Firefox was among the first to be able to properly debug promises, while chrome still let it swallow unhandled errors.

After writing that, i think the reason i love working with JS is because of the choice. A lot of that choice isn't necessarily because of the language (you could easily have that debugging experience in other languages), but it's currently in javascript.


Not sure I agree with some of your points but you most likely have used Javascript more than I since most of my professional experience is with enterprise Java. Let me try to show you how I see the programming world through my Java-colored glasses :P

"The lack of 'private' anything" - Just hearing this caused immediate revulsion and I am sorry to say that. Where I come from, the best practice is to try to keep everything closed from modification but still open enough for extension: http://www.cs.utexas.edu/users/downing/papers/OCP.pdf

"The debugging" - The features you mentioned are all available in the Java ecosystem as well. It is a very mature ecosystem with great IDEs, debuggers, performance testers, etc. The step-back and edit feature of debugging has been around for awhile now. Heck, you can even write your own debugger fairly easily due to good support of other tools in the ecosystem.

"async programming" - Not sure what you mean by "first-class citizen", but asynchronous programming can also be done with Java as well. Callbacks and futures are used widely (at least where I work). But even better: Java is multi-threaded. What happens to the Node.js server if a thread is getting bogged down?

"the mix of functional and OOP" - I admit I have no experience with functional programming so I can't say anything about mixing the two paradigms together. But I have seen OOP with Javascript and frankly, it is confusing and unwieldy. I don't even think the concept of class as an object blueprint exists. How do you even create a class that inherits the properties of another Javascript class? It is one of the basic ideas of OOP but I don't think Javascript supports it. From my brief time with it, it really looks like you only have simple objects with properties and methods which can be set up off of a prototype but that's it.

"it's fast" - Not sure where you got this idea. Looking at various benchmarks on the internet, they show that Javascript is significantly slower (sometimes by an order of magnitude) compared to Java, Go, and C++. I did notice that it looks to be faster than Python and Ruby. https://www.techempower.com/benchmarks/#section=data-r12 https://benchmarksgame.alioth.debian.org/u64q/javascript.htm...

"the compilation options" - I'm assuming you're are talking about transpilers and yes, I've been noticing more transpilers that target Javascript. I honestly don't know why one would want to do that though. It just seems an unnecessary layer. Why not just directly write Javascript code? Is the Javascript syntax so bad that you want to code in pseudo-Ruby (Coffeescript)? :)

"the tooling" - Hot swapping, test frameworks, linting, optimizing, ...these are also available in the Java ecosystem and have been for quite some time now. Notice I didn't mention auto-refresh, minifying, and compressing since I am not sure what exactly those are and I don't think they apply to compiled languages.

"npm" - The available libraries in the Java ecosystem is vast and a great number of them have been developed and iterated upon by some of the best engineers and computer scientists in the past ~20 years. And the Java libraries do not seem to have the problems that npm is suffering at the moment :P


Per privacy, i had the same reaction at first. But at one point i had a bit of a realization that i'm protecting my code from being "used" incorrectly. Me writing extremely private code and only allowing what i want to be used externally is not going to make my code any more "secure", it's going to make sure other people don't misuse it. With that in mind, i've found that documentation and comments provide the exact same assurances, while still allowing someone to go and poke around in your internal code if they need to (and they are willing to take on the possibility that the code will change out from under them). It's almost always a "last resort" thing, but without this the web wouldn't have grown at the pace it did. This is what allowed polyfills, this is what allows me to patch in old software versions, or what allows me to in 3 lines modify a library to work with another library (literally just last week i found that a storage library had a nearly identical API to another library except for one function was a different name. Because of "nothing private", i was able to hang a new function on the library's object at runtime and re-direct it to the original function name, meaning the storage lib was now compatable with about 3 added lines, it's a pretty weak example of this, but it's the most recent one i can think of).

per the debugging, i might have to take another look at this. I hadn't realized that it was that nice!

per async, yeah java can do async programming, but in js you MUST do async programming. Because of the single-threaded nature, if you aren't async you are blocking which ruins the performance instantly. This means that every library, function, and module is built for async from the start. "What happens to the Node.js server if a thread is getting bogged down?", it runs slowly or in some cases not at all. Yeah, that sucks, but this constraint forced better and more widely available async code. Plus if you really need multiple threads you can have them (webworkers on the web, threads on node), but you need to control them absolutely. It's more work, but it's the same outcome. I'd prefer this to be different, but trying to bring multi-threaded execution to javascript is like trying to stop a tornado with your bare hands...

per functional/oop, JS's OOP is lacking (or was, recently with ES6 it's gotten MUCH better). Now you can inherit from another class, now you can use a class as an object blueprint. There's still work to be done here, but it's getting better. That being said, there are ways to solve those same problems, but they are functional. And even though we are getting inheritance, it's largely an anti-pattern (at least to me). Composition is almost always prefered. Going back to my monkey-patched library from above, i was able to modify the object at runtime to add more functions and to "redirect" others. In something like Java i'd need to wrap that class in another class and add what i want there. It's largely the same result when used alone, but when you combine that, with a polyfill that's fixing something in all objects, and with a plugin for that library (where the lib wasn't meant to have plugins in the first place), in Java land you quickly end up with a big mess, while in JS land you can keep hanging more and more crap on an object if you want at runtime. And because the language was "meant" to be used this way, everyone expects and guards against it.

per speed. It's not as fast as java/go 90% of the time, but there are times where it can be. Yeah, they are the minority, but i was more just trying to dispel the myth that JS is dog slow. It's impressively fast these days. My favorite set of benchmarks to show off is the asm.js suite from "arewefastyet"[1]. It's more for comparing JS engines against each other, but there is a "Native C++" compiled version of the same code and they are comparing their JS against that.

The compilation options. It seems like an unnecessary layer, but in practice it's not as bad as many make it out to be. You still need to know JS to do it, so you are doubling the amount of languages you need to know to work on the project, but it does allow for some cool stuff. Typescript (microsoft's strong-er typed javascript/C# hybrid compile-to-js language) actually pushed a bunch of features into the newest version of javascript. Coffeescript did as well. These compile-to-js langs are partly a symptom of a problem, and by their nature they let people "solve" the problem now and when it gets fixed they can migrate back to "pure" js if they want. Also, it's this "transpiling" that lets things like React's JSX to exist. Adding entirely new, unrelated to javascript itself, parts to the language. JSX allows you to embed HTML directly into JS and it's basically a wrapper around the "document.createElement" function (in reality it's MUCH more, but that's the gist of it). It's really strange if you haven't used it before, but it's extremely powerful. And it could be done in other languages (and it is, look at go's generate command), but it's already here in js, and i love it!

the tooling, java is probably the only other one in my "list of languages" that is on the same level as JS in terms of tooling. The problem i have with Java's tooling is they tend to be built into IDEs instead of standalone tools. So that means i'm married to my IDE if i want those features. In JS land for the most part they are standalone and can be used with many different editors. This is a pain-point at my work currently as we are switching from a Business basic stack that marries you to the IDE and everyone wants different things in their next editor. It's a small problem though in the grand scheme of things, but it's a bit of a pet-peeve of mine.

and npm. Maven is great, but it just doesn't have the same number of options that something like NPM has. I know this isn't the languages fault (the best package manager doesn't mean shit if there are no packages), but it's a pain point. Many people seem to think of having "too many options" as a problem, but I think i'm spoiled by it now. If i want a lib to handle authentication in my app, i have 5 or more major choices. They all have their upsides and downsides, they are all made for different use cases. In something like the java world i just haven't seen that amount of choice. The only other one that comes close is surprisingly go. I really think the number of packages stems from ease of publishing, and npm (and go) have that down pat. I also think that this comes down to the languages being more oriented towards different things.

I appreciate the comment, and i'm in no way trying to say that JS is the only one that has these things (not that it sounded like you were implying it), but when combined, it makes for a nice experience.

[1]https://arewefastyet.com/#machine=28&view=breakdown&suite=as...


> But regardless of how many insults get thrown around, or how many people seem to think JS is useless or that it's a horrible language, its probably my favorite (and I've done professional work in non-trivial applications from C and C++, to Java and go, to python, Ruby, and PHP to BusinessBasic and even some lisp).

I see one common thread between all those languages you list: none of them has a decent type system.

If you ever get the chance I'd strongly recommend trying a small project in a strongly-typed functional language - PureScript if you're targeting the browser, otherwise Haskell or Scala, or maybe F# or OCaml. (Scala and OCaml also have compile-to-JS options). If you find you don't like that style then fair enough, but it's well worth trying one language in that space and getting a sense of the techniques that it enables - it will make you a better programmer even if you end up going back to JavaScript or another language from your list.


I've actually played with OCaml a bit, and Haskell a bit less. The problem is that I don't know what "problems to solve" with them, and there is no way I'm going to use something like that at work, so I kind of run out of steam before I really get into it.

I might shoot for Scala next time. We don't use Java anywhere at my current job, but I might play around with it in a personal project for a while.

I really like the functional style, and I can see how strong typing works REALLY well with it, but I've already found that it's pretty hard to bring other devs up to speed on it. And that really limits where I use it.


I tried to write a bit about my use cases at http://m50d.github.io/2014/05/16/signs-your-java-program.htm... and http://m50d.github.io/2015/04/21/effects.html (apologies for formatting issues). Basically I find the really useful problem that advanced type systems solve is keeping track of cross-cutting concerns (e.g. database transactions, audit logging - the kind of thing you'd be tempted to use AOP for) in a consistent way.


If you like Javascript and you want to try a language with a good static type system, you might like Elm (http://elm-lang.org/). As a bonus, it has fantastic documentation and examples of small in-browser projets -- a clock, Pong, and so on.


My dependency strategy over time has moved towards more static, project-owns-everything behavior, and specifically "one synchronization script per dependency" - including my own utilities library. The script leaves a echo of the timestamp so that I can also see when it was updated.

That way, different projects can have different versions of everything, and system environment is only important to the synchronization step - trivial to fix up if needed, trivial to copy between machines.


What I see is that a module has a non-zero overhead in complexity in itself. That is, ten 10 line modules and twenty 5 line modules do not yield the same complexity. The modules themselves have a complexity overhead associated, and submodules have their own complexity overhead associated, albeit smaller than first party modules. That complexity is easily seen from the recent situation of unpublishing modules, which resulting in modules multiple steps removed having problems building.

So, when I read "It doesn't matter if the module is one line or hundreds." I call bullshit. There is overhead, it's usually just fairly small (at may event begin to rival the gains from using a module at that level), but that small amount adds up. Once you've had to deal with a dependency graph that's 10 levels deep and contains hundreds or thousands of modules, that small extra complexity imposed by using a module is no longer in total, and comes at a real cost, as we've just seen.

Other module ecosystems have gone through some of the same problems. There was a movement in Perl/CPAN a few years back to supply smaller, more tightly focused modules a while back, to combat the sprawling dependencies that were popping up. The module names were generally suffixed with "Tiny"[1] and the goals where multiple:

- Where possible, clean up APIs where consensus had generally been built over what the most convenient usage idioms were.

- Try to eliminate or reduce non-core dependencies where possible.

- Try to keep the modules themselves and their scope fairly small.

- Remove features in comparison to the "everything included" competitor modules.

This has yielded quite a few very useful and strong modules that are commonly includes in any project. They aren't always tiny, but they attack their problem space efficiently and concisely. Even so, I'm not sure there's ever a module that's a single line of code (or less than 10, given the required statements to namespace, etc), as the point is to serve a problem, not an action.

1: https://metacpan.org/search?size=20&q=%3A%3Atiny&search_type...


I'd say you're playing fast and loose with the term "edge case" there. I call that a bugfix, for a feature that does not work.


It doesn't handle edge cases, it doesn't perform well and it isn't well tested. There is also no documentation. Obviously 30 seconds wasn't enough for you to verify anything at all about this module (namely that it's complete garbage).

And just because some random guy didn't get something as trivial as this right the first time, doesn't mean nobody else can. Also the de facto standard library lodash already has padding utilities, made by people who have a proven track record.


I don't agree with the explosion of micro-modules. There's a reason the vast majority of languages doesn't have them, at least not at function level.

IMO in the Javascript world they're only there in order to minimize script size for front end work. See lodash & lodash-something1 / lodash-something2 / ..., where there's an option of using the whole module or just including 1-function long scripts, precisely to avoid the script size issue.

Is there a solution for this? I know that the Google Closure compiler can remove dead code, ergo making inclusion of large modules less costly in terms of code size. Am I missing some ES6 feature that also helps with this?


You're just trading in the complexity of the code you'd have to write for the delayed complexity of dealing with dependency issues down the line. It's a waste of a trade off for tiny things like this.


Padding a string is a one liner. I'm perfectly happy to write it, test it, convince myself that it works, and then forget about it and just use it.


Completely agree.

Combining this with isomorphic code, cross-browser development and micro-services.

I even wrote a blog post couple of days ago about it [1].

1 : http://www.drinchev.com/blog/increase-your-dependencies/


I agree with the points you've made, but I would also posit that adding a dependency using this mutable package manager is making a commitment to maintain the integrity of that dependency, which is arguably more work than maintaining the handful of lines of code.


What's wrong with copy/paste?

Why do you need an external dependency on something so small?


Nobody has forgotten. These people never knew to begin with.

NPM/JS has subsumed the class of programmer who would have previously felt at home inside PHPs battery-included ecosystem. Before that, a similar set of devs would have felt at home with Visual Basic. Seriously, go visit the comments section on archived copies of the PHP documentation. You'll find code of a similar nature. If PHP had had a module system 10+ years ago you would have seen this phenomenon then. Instead it was copy and paste.

This isn't elitism, it's just the way it is. The cost of a low barrier to entry in to a software ecosystem is taking in those who don't yet have software engineering experience.

Nobody should be surprised that NPM, which I believe has more packages than any other platform, is 90% garbage. There are only so many problems to solve and so few who can solve them well, in any language. Put 100 programmers in a room, each with 10 years experience, and you'll be lucky to find 1 who has written a good library. Writing libraries is really hard.


This is the answer, 100%. All it takes to publish an npm package, is the command npm publish, and you're done. So of course it is no surprise that there are tons upon tons of seemingly useless or tiny projects (gotta pad out that github profile for those recruiters!), or that there are then plenty of packages that use them.

Add into that the fact that:

1) Javascript has a huge number of developers, and is often an entry-level language

2) The developers on this thread (I like to think of HN as at least slightly above average) are divided whether having small packages / large dependencies trees is a good or bad thing

3) Dependency management is something that matters mostly to long term (professional / enterprise / etc) applications, which is a subset of programming, and I wonder if not a minority subset of node.js projects in general.

4) If I'm writing a throwaway app or proof of concept, and therefore don't care about dependency maintenance, using as many dependencies as possible is a major time savor,

and of course you get this situation, and it seems to make perfect sense.

Personally, I wish there was an NPM Stable, where packages underwent much more scrutiny and security in order to get it, but nonetheless, nothing I've read so far about npm really scares me given the the above context. If you are a dev creating an unmanageable dependency tree for your enterprise app, you're a shitty dev. That doesn't necessarily mean that NPM is wrong for being so open in allowing others to publish their packages, or that smaller / more worthless packages shouldn't be allowed to publish.

That said, I would really like to hear a response to this post, as I have limited experience with different package management systems.


The huge difference is that PHP package manager support namespaces and dependencies ARE FLAT. You cannot import 2 versions of the same package under the same namespace. Which

1/ forces package authors to write stable libraries

2/ forces dependencies to narrow the versions of their dependencies

3/ prevents name squatting to some extent. You cannot have a package named "forms" and then sell the name for real money, like seen on NPM. your package needs to be "namespace"/"name". NPM made a huge mistake with its gems like global namespace and it explains half the problems it is having today.


For a post that is claiming to not be elitist, it reads pretty elitist.

Can you expand on how to identify the class of programmers you're referring to? Are they the type that copy / paste code directly from StackOverflow? They lack a classical computer science education? They haven't worked on a large, enterprise-grade project?


From what I've seen, there's one division between programmers that's hard to overcome. Some see it as a tool to get certain results for a job. Some of them are bad, some of them are lazy, but most of them are good enough to get to their objective.

Others see programming more as an art. They take care to make the code not only efficient but also elegant. They'll read up on new and interesting algorithms and incorporate them in novel ways. They might often be behind deadlines, but when they are, they create things like GNU Hurd that inspire a lot of interest and lead to interesting results, maybe even teach people a few things. Their code is interesting to read. They tend to write the libraries that the first group uses.

Both groups contribute a lot, but it's not easy to get them to understand that about each other.


I think the languages themselves give rise to such code because of the lack of consistency and all the weird quirks everywhere.

When you can't even reason about the truthiness of a variable because the language coerces everything, of course things end up screwy.


PHP has had an extension module repository since 1999


Comparing NPM to PECL/PEAR doesn't make much sense when talking about PHP developers. With PECL, the overhead of building a module in C is waay too high to make it viable for micro packages. And PEAR didn't just accept any random stuff, they were shooting for the one-solution-fits-all libraries and not tons of user-defined micro libraries like ecosystems like NPM encourage.

Compare NPM to Composer/Packagist and you get a better comparision. I've personally seen only very few micro packages on Packagist, thankfully this never seemed to gain traction in the PHP world.


Going down the "lots of tiny modules" route is about these three things:

a) No standard lib in JS

b) JS is delivered over the internet to web pages in a time sensitive manner ... so we don't want to bundle huge "do everything" libs. Sometimes its convenient to just grab a tiny module that does one thing well. There isn't the same restriction on any other platform

c) Npm makes it really easy to publish/consume modules

d) And because of c) the community is going "all in" with the approach. It's a sort of experiment. I think that's cool ... if the benefits can be reaped, while the pitfalls understood and avoided then JS development will be in an interesting and unique place. Problems like today can help because they highlight the issues, and the community can optimise to avoid them.

Everyone likes to bash the JS community around, we know that. And this sort of snafu gives a good opportunity. But there many JS developers working happily every day with their lots of tiny modules and being hugely productive. These are diverse people from varied technical backgrounds getting stuff done. We're investigating an approach and seeing how far we can take it.

We don't use tiny modules because we're lazy or can't program, we use them because we're interested in a grand experiment of distributing coding effort across the community.

I can't necessarily defend some of the micro modules being cited as ridiculous in this thread, but you can't judge an entire approach by the most extreme examples.


I think b) is true only because JavaScript tooling cannot perform dead code elimination. Other languages have big grab-bag utility libraries like lodash that don't hinder performance because a linker or runtime can avoid loading unused portions.



Yes it can https://github.com/rollup/rollup. Webpack 2.x can also do this.


Note for b): If you include libraries such as jQuery on you website via CDN, I believe browsers will be able to use the cached version even if they never visited your website before (given that they've cached this version from the same CDN before).


I don't see anything wrong with using a pre-made left pad function. Why waste time and lines of code implementing something so trivial when there is already a solution available?

However, I agree it is ridiculous to have a dedicated module for that one function. For most nontrivial projects I just include lodash, which contains tons and tons of handy utility functions that save time and provide efficient, fast implementations of solutions for common tasks.

Lodash includes `padStart` by the way (https://lodash.com/docs#padStart).


I think the article's thesis is essentially that every dependency your project pulls in -- which includes all the dependencies your dependencies pull in -- is a point of potential failure. I understand the "don't re-invent the wheel" defense, but the Node/JavaScript ecosystem tacitly encourages its users to build vehicles by chaining together dozens of pre-made wheels, all of which depend on more wheels, and each and every one of those wheels has a small but non-zero chance of exploding the next time you type "npm update."

(And, y'know, maybe it's because I'm not a JS programmer, but the notion of looking for a module to implement a string padding function would never have even occurred to me.)


Worse yet, a backdoor or legitimate bug in any of these module could leave huge exploits in the entire Node.js ecosystem.


A backdoor or legitimate bug in any line of custom code could leave huge exploits in your system. A widely-used published module is likely to be much more reliable, at least on average.


Except when you are using a library like boost or pandas you know the people behind it know what they are doing. When you are importing from a thousand different package authors any one of those people could be incompetent and/or malicious and screw up your entire code base.


Worse yet, the OS is compromised. This is why I hand roll all my encryption libraries. /s


Riiiight. Because cryptography is identical to an 12-line string padding function.


I think that was smokeyj's point... the left-pad module is not going to have a "backdoor". nv-vn was creating a bit of a straw man, as no example or particular scenario in this article involved crypto.


No, I disagree with smokeyj drawing a false parallel to encryption to try to justify why you should use an external dependency for 12 lines of code -- because "you should never roll your own crypto" is not applicable here.


I just used crypto as a random example. I could have said "this is why I write my own input sanitation library" or "HTTP" library.

My point is OSS is a collaborative effort by often times anonymous contributors. Therefore there will always be a risk of bugs or back doors - regardless of the distribution mechanism.

In my opinion any criticism against micro packages is equally valid against large packages. There's no guarantee that a pull request would receive any more scrutiny than a external dependency. I mean look at heart bleed. Surely this doesn't mean that OSS is broken, but rather stricter security protocols should be in place. My 2 cents.


The problem is not that, the problem is depending on unreleased versions, instead of simply depending on the version that was written when u wrote your code.

a Git submodule like approach would be much better


> Why waste time and lines of code implementing something so trivial when there is already a solution available?

Because it's so trivial? I can't wrap my head around why this is an argument in the first place. It makes no sense to bring in a module from a third party adding yet another dependency and potential point of failure when reimplementing it yourself literally takes as long as it takes to find the module, add it to package.json and run npm install.

People should be trying to limit dependencies where possible. Reproducible builds are really important if it costs you almost no time you should have it in your code base IMO.

People taking the DRY principle to the most extreme degree always makes for the worst code to debug and maintain.


This entire comment thread is such a breath of fresh air. I was beginning to think that I was that guy who was crazy for thinking that all of the people doing this were crazy. This thread is like my new support group.


> It makes no sense to bring in a module from a third party adding yet another dependency and potential point of failure when reimplementing it yourself literally takes as long as it takes to find the module, add it to package.json and run npm install.

Even if it does take the same amount of time (which it shouldn't), a 1-line call to a standard module imposes less of a future maintenance burden than 14 lines of custom code.

> People should be trying to limit dependencies where possible. Reproducible builds are really important if it costs you almost no time you should have it in your code base IMO.

That's a non sequitur. Reproducible builds are important, but unless you write code with 0 external dependencies you already have a system in place for handling library dependencies in a reproducible way. So why not use it?

> People taking the DRY principle to the most extreme degree always makes for the worst code to debug and maintain.

This is the opposite of my experience.


> Even if it does take the same amount of time (which it shouldn't), a 1-line call to a standard module imposes less of a future maintenance burden than 14 lines of custom code.

In my experience with using npm since it's release, module authors will spit out modules very quickly then, after some period of time, abandon them without passing them onto other people. At which point I have to assume all future maintenance anyway. This has happened to me so many times, in fact, that I try to make even picking my dependencies based on the author's interests. For example if it's owned by a company or organization that still uses the module then it's usually one of the safest to pick.

Regardless I don't think I'd ever call very elementary code a "maintenance burden". Ever.

> That's a non sequitur. Reproducible builds are important, but unless you write code with 0 external dependencies you already have a system in place for handling library dependencies in a reproducible way. So why not use it?

Completely disagree here. As we saw with this "npm gate", even if you're using a shrinkfile, npm doesn't completely provide handling dependencies in a reproducible way. Not always. Maybe most of the time though our build server certainly has logs where npm was unreachable, having issues, etc on a very regular basis.

The point being: where it's possible to mitigate and remove dependencies I think you'd be crazy not to. Every dependency you can lose is another potential build issue or attack surface you're removing from your project.

> This is the opposite of my experience.

That's fine. In my experience people will take DRY so far that even meta data and comments will be abstracted so you can't even understand a piece of code without opening multiple files. I think it's perfectly reasonable to repeat yourself at times but those cases where you have to open up 5 files just to understand what a REST endpoint accepts as input is crazy.

I think DRY in general is fine as long as it's not used as an absolute "we have to do it this way because DRY". :)


> Regardless I don't think I'd ever call very elementary code a "maintenance burden". Ever.

Every line is another line that maintainers have to read and understand.

> The point being: where it's possible to mitigate and remove dependencies I think you'd be crazy not to. Every dependency you can lose is another potential build issue or attack surface you're removing from your project.

Disagree. If there are issues with npm shrinking not working then you absolutely need to resolve them - but resolving them is an O(1) problem no matter how many dependencies you have. Just like if you've already written a good general-purpose sorting function, there's no point writing a separate integer sort routine, even if the implementation could be simpler than the general-purpose one. You already depend on your packaging/dependency tools, so you might as well use them all the time.


> Every line is another line that maintainers have to read and understand.

Seriously? This is what you're going with? We're talking about very simple, elementary programming. To be worried about maintaining code that you learn how to do in the first few classes of any programming 101 class is absolute insanity.

> Disagree. If there are issues with npm shrinking not working then you absolutely need to resolve them

Can't resolve them if the module disappears or is replaced with a malicious module. Nor if you or npm are having connectivity issues (which, on the npm side, happens very frequently).

> Just like if you've already written a good general-purpose sorting function, there's no point writing a separate integer sort routine, even if the implementation could be simpler than the general-purpose one.

Not sure what you're getting at. If you need a sorting function you can probably use whatever is built into the language unless you need to sort across a distributed data set in which case write something or find a dependency to use.

> You already depend on your packaging/dependency tools, so you might as well use them all the time.

Absolutely, unequivocally, no. What you're saying is you should install and use dependencies, from third parties which you do not know anything about, for every single, possible thing just so you can use the tools "all the time". That's so irresponsible and backwards.

Use the tools for the job they were meant to be used for. Need a dependency because someone can do it better / faster / cheaper then you? Then grab it by all means. But don't use it for every tiny function and for loop just because you want someone else to maintain it.


> Seriously? This is what you're going with? We're talking about very simple, elementary programming. To be worried about maintaining code that you learn how to do in the first few classes of any programming 101 class is absolute insanity.

Every line is a maintenance burden - just reading and understanding the code is what takes most of the time. Lines of code (and notably not any measure of "complexity" of those lines that's been tried) is the one thing that correlates with bug rates.

> Can't resolve them if the module disappears or is replaced with a malicious module. Nor if you or npm are having connectivity issues (which, on the npm side, happens very frequently).

So figure out a process and resolve that, once and for all. There's no point just cutting 10% (say) of your dependencies and hoping that you won't encounter the problem on your more important dependencies.

> Use the tools for the job they were meant to be used for. Need a dependency because someone can do it better / faster / cheaper then you?

That's exactly why people were depending on this library.


> Every line is a maintenance burden - just reading and understanding the code is what takes most of the time. Lines of code (and notably not any measure of "complexity" of those lines that's been tried) is the one thing that correlates with bug rates.

I'm sorry but that is just a horrible way to look at programming.

You shouldn't NEED to go out and look for an already done solution if it's elementary and takes minutes, if that, to write. Ever.

This is just sloppy.

> So figure out a process and resolve that, once and for all.

Tell yourself that; that was simply a counter argument to your false claim. Regardless it's fixable.

> There's no point just cutting 10% (say) of your dependencies and hoping that you won't encounter the problem on your more important dependencies.

This doesn't even make sense. What are you trying to convey here? The more dependencies you can cut out the more reproducible your builds will be. Period. Which is important when you're dealing with code that gets rapidly deployed to many production boxes.

> That's exactly why people were depending on this library.

If using left pad gave them time back because the original author could do it better, faster and cheaper...I'm not sure programming is the right type of work for these people.

What's next, are you going to outsource all your for and while loops to a module? You know, so you have less things to "maintain"?


> You shouldn't NEED to go out and look for an already done solution if it's elementary and takes minutes, if that, to write. Ever.

If writing it would take minutes and adding the dependency would take seconds, add the dependency. And how long it takes to look for is beside the point - code is read more than it's written, so how long it takes to read is much more important.

> This doesn't even make sense. What are you trying to convey here? The more dependencies you can cut out the more reproducible your builds will be. Period. Which is important when you're dealing with code that gets rapidly deployed to many production boxes.

No, look, if you have some kind of problem where dependencies maker your builds unreproducible or break your deployments, you need to fix that problem. If you have that problem when you have 100 dependencies, you're still going to have that problem when you have 90 dependencies. Unless you're going to cut every dependency, cutting dependencies is not the way to fix that problem.

> What's next, are you going to outsource all your for and while loops to a module? You know, so you have less things to "maintain"?

for and while probably should be ordinary functions (smalltalk style) and probably should be in a library somewhere rather than having everyone reimplement them, yes. Almost all languages have a for or while in their standard library so I don't know what you're really saying?


> However, I agree it is ridiculous to have a dedicated module for that one function. For most nontrivial projects I just include lodash, which contains tons and tons of handy utility functions that save time and provide efficient, fast implementations of solutions for common tasks.

I think that was largely the OP's point tbh. Using something like lodash [a utility library] is fine while using a module [for a single function] is not.

It might have gotten lost in the ranting from on high but I don't think the author truly meant more than that.


For what it's worth, that's what I read in to it too.


Some of the libraries people were mentioning broke yesterday already have lodash as dependencies, I have no idea why they wouldn't have just been using this...


Someone mentioned below that lodash had some breaking changes related to the padding functions a couple times, which could be a totally valid reason to avoid using those. I was under the impression that the lodash API was more stable than to have that kind of thing happening.


Lodash renamed padding on a major version bump (which requires opting into) to better align with the ES7 proposal.

Nothing was done in a way to pull the rug out from underneath folks.


> I don't see anything wrong with using a pre-made left pad function. Why waste time and lines of code implementing something so trivial when there is already a solution available?

I'll tell you why.

The least important ones is that downloading such trivial module wastes bandwidth and resources in general (now multiply this by several hundred times, because of dependency fractal JS sloshes in). I would also spend much more time searching for such module than I would implementing the damn function.

More important is that you give up the control over any and every bug you could introduce in such trivial function or module. You don't make it less probable to have those bugs (because battle-tested package! except, not so much in JavaScript, or Ruby, for that matter), you just make it much harder to fix them.

And then, dependencies have their own cost later. You actually need a longer project, not a throw-away one, to see this cost. It manifests in much slower bug fixing (make a fix, find the author or maintainer, send him/her an e-mail with the fix, wait for upstream release, vs. make a fix and commit it), it manifests when upstream unexpectedly introduces a bug (especially between you making a change and you running `npm install' on production installation), it manifests when upstream does anything weird to the module, and it manifests in many, many other subtle and annoying ways.


TIL Java/.NET/Perl/Python/PHP/Ruby are all wasting bandwidth forcing people to download there leftPad bundled in their SDK.


> You don't make it less probable to have those bugs (because battle-tested package! except, not so much in JavaScript, or Ruby, for that matter)

Battle-tested still applies - if you have that many people using a line of code they're more likely to find any bugs. (Formal proof is better than any amount of testing, but no mainstream language requires formal proof on libraries yet)

> And then, dependencies have their own cost later. You actually need a longer project, not a throw-away one, to see this cost. It manifests in much slower bug fixing (make a fix, find the author or maintainer, send him/her an e-mail with the fix, wait for upstream release, vs. make a fix and commit it), it manifests when upstream unexpectedly introduces a bug (especially between you making a change and you running `npm install' on production installation), it manifests when upstream does anything weird to the module, and it manifests in many, many other subtle and annoying ways.

Large monolithic dependencies have this kind of problem - "we upgraded rails to fix our string padding bug and now database transactions are broken". But atomised dependencies like this avoid that kind of problem, since you can update (or not) each one independently. Regarding fixing upstream bugs, you need a good process around this in any case (unless you're writing with no dependencies at all).


Finding this module on NPM or npmsearch.com is pretty trivial compared to ensuring you implement this in a way that catches every edge case.

> It manifests in much slower bug fixing

I don't buy this at all, because I've done it myself many times. If you're waiting on a PR from the original repo owner to fix a Production bug, you're doing it wrong. It's trivial to copy the dependency out of node_modules and into your src, and then fix the bug yourself. Then when the owner accepts your PR, swap it back in. I don't understand the problem here.


> If you're waiting on a PR from the original repo owner to fix a Production bug, you're doing it wrong. It's trivial to copy the dependency out of node_modules and into your src, and then fix the bug yourself. Then when the owner accepts your PR, swap it back in. I don't understand the problem here.

You're working the problem around instead of having it solved. You're moving a library in your repository back and forth, while the library should never land there in the first place (or stay there until it stops being used).

But even if you don't agree with this strategy, it's still much more work than to just commit the fix and be done with it. And you still don't control who introduces bugs to your code with modules upgrades, having much bigger surface to random external programmers than you would if you only used things large enough to pay for themselves.


What are these arcane edge cases everyone is so afraid of?


I think it's because you never know what you're going to get with JS variables, and from what I've seen there's a strong tendency towards trying to fix or squash input errors to an insane degree within the JS culture.

Just let it crash if the user is trying to leftpad a boolean, for crying out loud.


Every function in Lodash is also available as a dedicated module, for the same reason left-pad existed: https://www.npmjs.com/package/lodash.padstart


If it was a good implementation, I'd agree with you, but it's not: https://github.com/azer/left-pad/pulls


I agree that Lodash would be a better choice because it seems like a well maintained project. There could be two counter args, in theory:

- if the programmer uses other functions included in Lodash his code will have a single larger point of failure. For example, if Lodash is unpublished (intentionally as in this case, or unintentionally) then the programmer will have a lot more work to redo.

- Lodash introduces a lot of code, while the programmer only needs one of its functions to pad a string.


I'd be amazed if lodash were unpublished.

Using a library like lodash makes a lot more sense once you use a module bundler that allows tree shaking (like Rollup or Webpack 2.0) along with the ES6 module syntax. Heck, even if you're just using babel with Browserify or Webpack 1.x, you can use babel-plugin-lodash [0] so it'll update your imports and you only pull in what you need.

[0] https://github.com/lodash/babel-plugin-lodash


I think it speaks to just how lacking the baseline Javascript standard library is. The libraries that come with node help, but all of this stuff seems like it should be built-in, or at least available in some sort of prelude-like standard addon library. The lack of either leads to all these (apparently ephemeral) dependencies for really simple functions like these.

That said, I work with Java, Clojure and Python mostly so I may be more used to having a huge standard library to lean on than is typical.


So many people use lodash as a drop-in standard addon library that I'm surprised people aren't just using the padding functions that are right in there... Some of the packages that broke yesterday even have lodash included as dependencies already!


Looking at the changelog, there have been more than 70 versions of Lodash in less than four years. The first was in April 2012. [1]

  _.padleft
does not exist. It was added as part of version 3.0.0 January 26, last year and renamed to _.padstart in Version 4.0 on January 12, this year.

So in less than a year "padleft" came and went away because all strings don't start on the left and someone decided that "left" means "start" except that the reason that it doesn't is the reason that it was changed. Even worse, the 4.0 documentation does not document that _.padstart renamed _.padleft. It's hard to grok what cannot be grepped.

Why blame someone for depending on padleft in a world where libraries swap out abstractions in less than a year? Breaking changes are bad for other people. Semantic versioning doesn't change that.

[1]: https://github.com/lodash/lodash/wiki/Changelog


Yes, I found a number of the stylistic changes made in Lodash 4.0 made it more complicated to upgrade than needed.

Dropping the this param with no period of deprecation? Pretty breaking change to make with no warning. Renaming first/rest to head/tail? Was it really worth it? Particularly when they go the opposite direction of replacing traditional functional names with more explanatory names by removing the foldr alias for reduceRight.

All this in a language that doesn't make it easy to do automated refactoring means that you basically break everything upgrading to Lodash 4.0 so you can't refactor parts to the newer style piece by piece.


The reason for the name change and the Parma change is that String.prototype.padStart is being added in the next ECMAScript release, and lodash was changed to be compatible with the newly built-in version (so it's a polyfill).


An easy solution would be to have both padStart and padLeft, with a note in tbe changelog saying padLeft is deprecated and will be removed in a future version.


Damn, yeah, that's a totally valid reason. I was under the impression that lodash was more stable than to have that kind of stuff happen.


What if lodash itself was unpublished? I'm having a hard time drawing a line here, obviously a 10 line function is too far on the bad side of lazy, but I can't tell what is an acceptable dependency.


I think it would be annoying but recoverable. It's used so widely that somebody would republish it, at worst under a different name.

Edit: But there would sure be a period of chaos.


So it's the same with pad-left. But some how people are wrong for depending on pad-left.


If you depend on Lodash, you depend on Lodash. You have 1 point of failure.

If you depend on 2,000 tiny individual modules, each from different authors, you depend on 2,000 tiny individual modules. You have 2,000 different points of failure. Any one of those authors going rogue will break your build or compromise your system, and every one of those tiny modules has a lot less attention and care paid to it than a larger library like Lodash.


Probably they do use that if they need left padding but also have a dependency on another package that thinks 'lodash is too big when all I need is left padding' so we get to this situation


This seems like the right answer to me. It's not that we forgot how to program, it's that Javascript forgot a stdlib. You could easily write your own left-pad function in any language, but a stdlib (or this module) gives you a standard way to reference it, so you don't have to look up what you named it or which order the args go in.


Agreed the JavaScript standard library is poor and instead of addressing it they've mostly just added syntax changes to ECMAScript 6 and 7. It's incredibly disappointing.

For instance I added a utility to my own library (msngr.js) so I could make HTTP calls that work in node and the browser because even the fetch API isn't universal for some insane reason.


I think in some ways a good standard library is a measure of programming language maturity. I remember when C++ had a lot of these problems back before you had the STL etc. In the early 90's it was a dog's breakfast.

We have a large internal C++ app at my work of that vintage (~1992) it uses its own proprietary super library (called tools.h++) which is just different enough from how the C++ standard evolved that its not a simple task to migrate our codebase. So now every time we change hardware platforms (has happened a few times in last 30 years) we have to source a new version of this tools++ library as well.

I find it amusing Javascript hasn't learnt from this.


Usually, dependency hell doesn't bite you, until it does. Try to rebuild that thousand-dependencies app in three years from now and you'll see ;-)

I recently had to rebuild a large RoR app from circa 2011 and it took me longer to solve dependencies issues than to familiarise myself with the code base.

Excessive dependencies are a huge anti-pattern and, in our respective developers communities, we should try to circulate the idea that, while it's silly to reinvent the wheel, it's even worse to add unnecessary dependencies.


> Try to rebuild that thousand-dependencies app in three years from now and you'll see ;-)

Let's be honest though, in the current trendy javascript ecosystem these people will already be two or three jobs away before the consequences of their decisions become obvious. Most of the stuff built with this is basically disposable.


Not to mention a good chunk of the companies they were writing this type of code for won't exist by then either...


I never can believe how often frontend developers talk about "you're just going to rebuild it all in 2 years" anyway. I guess it's a good way to keep yourself employed.


Shouldnt the Gemfile.lock have prevented that? It takes your gemfile and makes a specific-version locked file of those gems.


The gemfile.lock must have been "gitignored" at some point, because it had much older packages than the ones in Gemfile. Background: all we had was a git repo and did not have access to any "living" installation.


> Try to rebuild that thousand-dependencies app in three years from now and you'll see ;-)

This is your fault for expecting free resources to remain free forever. If you care about build reproduction, dedicate resources to maintain a mirror for your dependencies. These are trivial to setup for any module system worth mentioning (and trivial to write if your module system is so new or esoteric that one wasn't already written for you). If you don't want to do this, you have no place to complain when your free resource disappears in the future.


I agree. But I find two problems with your proposal:

1- Maintaining a mirror of dependencies can be a non-trivial overhead. In this app that I was working on, the previous devs had forked some gems on github, and then added that specific github repo to the requirements. But they did not do it for every dependency, probably they did not have time/resources to do that.

2- As a corollary to the above, sometimes the problem is not the package itself but compatibility among packages. E.g. package A requires version <=2.5 of package B, but package C requires version >= 2.8 of package B. Now I hear you asking "then how did it compile in the frist place?" probably the requirement was for package A v.2.9 and package C latest version, so while A was frozen, C got updated. This kind of problems is not solved by forking on Github, unless you mantain a different fork of each library for each of your project, but that's even more problematic than maintaining dependencies themselves.

P.S. At least for once, it wasn't "my fault", I didn't build that app LOL ;-)


> 1- Maintaining a mirror of dependencies can be a non-trivial overhead. In this app that I was working on, the previous devs had forked some gems on github, and then added that specific github repo to the requirements. But they did not do it for every dependency, probably they did not have time/resources to do that.

You've precisely identified the trade-off. You basically have three options. You can

1. Maintain a local repo of your dependencies (high effort)

2. No dependencies, include everything as 'first-class' code (lower upfront effort, but v. messy)

3. Rely on third-party repos (easiest, riskiest)


Yes I agree, yours is a very good summary of the main alternatives at play. They all have pros and cons as you have rightly noted.


This problem is solved by mirror dependencies and pinning the versions. Even against a git repo, pinning to a particular sha is something that is possible.

Automatically upgrading versions (i.e. not pinning versions) in a production build is an anti-pattern.

These sound like problems incurred due to a previous lack of software engineering rigor. As an industry, when we encounter challenges like this we should be learning how to solve them without reinventing the wheel. Pinning versions and maintaining mirrors of dependencies (whether that's an http caching proxy for npm/pypi/maven/etc or keeping a snapshot of all dependencies in a directory or a filesystem somewhere) is something that any company requiring stability needs to take seriously.


Of course pinning the versions and identifying the particular commit in the Gemfile would have solved it, as long as it was done for every package, otherwise we are back at problem n. 2 in my post above.

In this particular case, there were just 3-4 requirements (out of more than 100) that were pointing to a git repo, and only one of them also specified a particular commit. The other "git-requirements" were just cloning the latest commit from the respective repo.

> Automatically upgrading versions (i.e. not pinning versions) in a production build is an anti-pattern.

We did not have access to a production version, only to a git repo, that's the very reason why we had to rebuild in the first place. I can imagine all versions were locked when the system went into production years ago.


There's more to dependency hell than "oops, the package disappeared." Try updating one of those dependencies because of a security fix, and finding that it now depends on Gizmo 7.0 when one of your other dependencies requires Gizmo < 6.0.


Yes that's exactly the kind of issues that I had experienced.


Most of the time this is something you inherit. Not something you wrote.


Yes that was my case.


A maintenance programmer should be raising to management the risk if they do not have reproducible builds.

The issue isn't that the company's software has a dependency. The issue is that the company is taking for granted the generosity of others. If they did not get a reproducible build before, they should attempt to get one as soon as they are aware of the problem. If the package is no longer available, they must now accept the punishment in terms of lost staff time or dollars to work around the lack of the dependency.


I agree with you, but s..t happens.


So, in the context of this discussion... you should make use of micro-modules to reduce code duplication, avoid defects, etc. However, don't expect those micro-modules to be maintained or available in the future; so you need to set up your own package cache to be maintained in perpetuity.

Or, you can implement the functionality yourself (or copy/paste if the license allows) and avoid the hassle.


Yeah, I think the takeaway here is something like: "find the right middle ground between reinventing the wheel and dependency hell"


I've been in the same situation as OP many times (although in most cases I've been brought in fix someone else's code).

In the Ruby ecosystem, library authors didn't really start caring about semantic versioning and backwards compatability until a few years ago. Even finding a changelog circa 2011 was a godsend.

I think this was mainly caused by the language itself not caring about those either. 10 years ago upgrading between patch releases of Ruby (MRI) was likely to break something.

At least this is one thing JavaScript seems to do better.


Why did you have to rebuild it?


I can't speak for him, but upgrading really old Rails apps can get complicated very quickly. Especially when you're going across multiple major versions and have to deal with significant changes in Rails behavior and broken gems. "Rebuild" might not be the most accurate way to describe the slow, steady incremental approach you're forced to take (you aren't redoing huge swaths of your domain logic, for instance), but it gets the gist across.

The same goes for almost any legacy app.


Rebuilding for most cases seems silly to me.

I've been working on a .net web app, that's been around since 2008. It's been continually evolved, so it's running on the latest MVC framework, uses microservices etc

As result it's build up a huge amount of automated tests. The business logic has been built up from experience and is well tested even for odd cases.

You throw away a lot of stuff for a rebuild.


Oh, definitely. It's not something I'd recommend ever doing for the hell of it unless you're a real masochist. Even then, it might be easier if just broke out the floggers.

All kidding aside, if the concern is just for continued security patches, there's always Rails LTS [0]. You have to pay to play, but it's cheaper than a security breach or weeks of development time. But if you're dealing with significant performance issues or looking at major feature changes/additions, it might be more effective in the long-term to consider an upgrade. You just need to be aware of just how large a project that can turn out to be.

0. https://railslts.com/


I could not not-rebuild... All I had was a git repo.


PS I did not downvote you and I find your question legitimate...


Long story... we had the git repo but lost access to any working installation. I had to rebuild a dev vagrant VM first, and later on a production-ish setup on a server.


I find large dependencies like RoR itself cause a lot more dependency hell than zillions of small dependencies like this one. What kind of dependency hell could possibly happen for a module like this?


I wanted to write this post after the left-pad debacle but I've been beaten to it.

I think we got to this state because everyone was optimizing js code for load time-- include only what you need, use closure compiler when it matters, etc. For front end development, this makes perfect sense.

Somewhere along the line, front end developers forgot about closure compiler, decided lodash was too big, and decided to do manual tree shaking by breaking code into modules. The close-contact between nodejs and front end javascript resulted in this silly idea transiting out of front-end land and into back-end land.

Long time developers easily recognize the stupidity of this, but since they don't typically work in nodejs projects they weren't around to prevent it from happening.

New developers: listen to your elders. Don't get all defensive about how this promised land of function-as-a-module is hyper-efficient and the be-all end-all of programming efficiency. It's not. Often times, you already know you're handing a string, you don't need to vary the character that you're using for padding and you know how many characters to pad. Write a for loop; it's easy.

Note that this is exactly the sort of question I ask in coding interviews: I expect a candidate to demonstrate their ability to solve a simple problems in a simple manner; I'm not going to ask for a binary search. Separately, I'll ask a candidate to break down a bigger problem into smaller problems. In my experience, a good programmer is someone who finds simple solutions to complex problems.

Note: rails is similarly pushing back against developers that have too many dependencies:

https://www.mikeperham.com/2016/02/09/kill-your-dependencies...


Everything in this article is categorically wrong and antithetical to every principle of good programming ever articulated. The only problem here, as others have already noted, is that NPM allows people to delete published packages.

Small modules are not evidence of a problem, and they certainly aren't evidence of an inability to implement these things on the part of the people depending on them. Why would I implement left-pad myself when there is already a well-tested implementation that I can install? Building up an ecosystem of tiny abstractions, bit by bit, iteratively and evolutionarily, is how we get robust, well-designed complex systems. We don't get there by everyone reinventing the left-pad function to sate some misplaced appetite for self-reliance.

The author seems to make some arbitrary distinction between things that are 'large enough' to be packaged and 'pure functions' which are 'too small' to be their own modules, and I just couldn't disagree more. Tiny, pure functions are ideal modules. They facilitate the greatest degree of re-use, most clearly articulate what they ought to be used for, and stateless things are, in general, more composable than stateful things. There is no better unit of re-use than a tiny, pure function.


I don't know why you were downvoted, but I agree with you 100%.

Pure functions are indeed a good target for modularity and deserve proper documention and proper testing, at the very least.

For the readers of this comment:

* How many functions did you not commented last time you wrote some code?

* How many functions do you leave untested?

* How many functions did you wrote more than once?

* How many "trivial" functions did you wrote that actually took you 3 hours, because it's actually tricky, so you checked other implementations and tried to wrap your mind around it.

Check haskell's hoogle to a small sample of this concept.


> Why would I implement left-pad myself when there is already a well-tested implementation that I can install?

You call 4 basic assertions "well-tested"?

https://github.com/azer/left-pad/blob/master/test.js


a) I think that's a fine number of tests for a module this simple, which is itself an argument in favor of small modules. And I think it's four more assertions than anyone implementing this inline in a project would have.

b) The details of this particular project are orthogonal to the philosophy of small modules generally. Whether or not this module is well implemented or well tested has no real relation to whether or not it is a good idea to compose tiny modules.


a) It's about half of what I'd write for my own, where I would make sure to test all edge cases (e.g. input string being longer than padding length) as well as Unicode characters. Writing expectations is cheap, fixing bugs later isn't.

b) I agree, but you were the one offering "well-testedness" as an argument :)


You are assuming well tested is referring to unit tests, rather than the wealth of developers depending on and running it in their projects daily.

Don't forget how many different targets there are for JavaScript, even if you had a test environment with limitless budget, it wouldn't compare to having a popular package on NPM.


What if it was well-tested?

What if it was well-typed?

What if it was awesomely documented?

Would that be ok? I'm asking, because a lot of the discussion in this post comes a single simple function being a dependency.


I wish I could say you were joking. At best it comes off as ivory tower idealism, at worst the complete naivete of someone who doesnt code.


I'm not sure if you're saying this of my comment or the OP, but I code professionally every day in this style and it's working out just fine. And the smaller my modules get the better it seems to work.


It's not working out fine. You just haven't been around long enough to understand it.

JS code is the most disposable piece of any infrastructure. In all companies(mine included) that I know of, npm and the JS jenga tower of hell is the most brittle element that breaks every fucking day. It's the constant pain you can count on being around.

The stack and the dependencies are a moving target. Like.. every minute. If you had coded in any other language other then JS you would know that.

Come to me after 5 years and tell me if your "professional" JS code you're writing today is used by anyone and then we'll talk.

I'll be waiting.


Perhaps you should be looking inwards, rather than blaming your tools, for the reason your npm 'jenga tower of hell' exists in the first place. I experience no such pain, and i've been doing this for many years.

And i've coded plenty in other languages. None with anywhere near as good an experience as NPM if you know how to use it properly.


Tell me the languages and package managers you're talking about.

Don't worry, we ain't going nowhere. Think hard and take as much time as you want.


"Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function."

--John Carmack


He wouldn't put that small function in a package (or even a separate file) though.


Functions are too small to make into a package and dependency. Pure functions don’t have cohesion; they are random snippets of code and nothing more. Who really wants a “cosine” dependency? We’d all really like a “trigonometry” dependency instead which encompasses many “tricky” functions that we don’t want to have to write ourselves.

This is a pretty weak argument. What is "cohesion" and why do we care that modules have it? Joe Armstrong, one of the creators of Erlang, has argued the opposite (http://erlang.org/pipermail/erlang-questions/2011-May/058768): that lots of small, individual-function modules are better than a "misc" module that grows endlessly and may overlap with other people's "misc" modules.

Calling a function instead of writing the code yourself doesn't mean you've forgotten how to program! The real problem here is the cost and risks associated with dependencies in general (both of which are actually lower for single-function modules), and the broken package removal policies of npm.


There's a big difference between a "misc" module and a "trigonometry" module.


I would retort with: Packages are too big to make into a functional dependency. In the end, in this functions-as-dependencies world, a trig package would be something like:

    (name) => 
        switch (name) {
            case 'sin': 
               (x) => sin(x);
               break;
            case 'cos':
               (x) => cos(x);
               break;
        }


Packages are only too big if your infrastructure makes them too big.

A trig package like yours could be implemented on top of sin and cos packages if it's something you find useful.


While in general I agree with the article I must admit that I also strongly DISAGREE with the overall message. Especially with this: "Finally, stringing APIs together and calling it programming doesn’t make it programming."

Stringing APIs together is what actually programming is. This is building software and for instance when i use .toString() method I can easily forget how it is done, focus on other high level things and don't care about dependencies, as long as everything works fine.

Let's admit that the main problem here is with broken npm, rather than packages themselves. If someone has written the "leftpad" function, it is so I don't have to write it again, and I can save probably 15-40 min programming and checking some corner cases.

Also please note that javascript can be really tricky down in the details. So if there's anything that can help, it's better that it exists, rather than not.


> Let's admit that the main problem here is with broken npm

It is absurd to have packages suddenly retracted and important parts of the ecosystem stop functioning. This never happened with other languages I have used. Maybe we need a way to make sure the packages are always going to exist. Checksumming and adding the checksum to the version number would be useful too.


That's why there are proposals for immutable and distributed packages managers. Look at gx for instance. This is probably the future for package managers maintained by community.


The funniest thing about this entire debacle is the thousand of self-assured programmers coming out to show the JS/NPM world how it's done, only to have their short, simple, no-nonsense functions fail miserably on some edge cases they didn't think about.

This discussion about the "isarray" package is probably my favorite: https://www.reddit.com/r/programming/comments/4bjss2/an_11_l...


Yes, or more accurately a large new generation of coders is entering the workforce who know how to code only in a superficial sense and think this is a good thing.

Programming, and especially startup programming, is being taken over by people who are primarily technicians rather than engineers. They want to assemble prefab components in standardized ways rather than invent new things. They are plumbers who know how to install from a menu of standard components, rather than civil engineers desigining purpose built one-off aqueducts.

It is the inverse of the "not invented here syndrome." The technician-programmer is trained to minimize time spent thinking about or working on a problem, and to minimize the amount of in-house code that exists. The goal is to seek quick fix solutions in the form of copy/paste from StackOverflow, libraries, and external dependencies to the greatest extent possible.

In house coding should, they believe, ideally be limited to duct-taping together prebuilt 3rd party libraries and services. Those who want to reinvent the wheel are pompous showboating wankers (they believe); creating your own code when you don't absolutely have to is a self-indulgent waste of time for impractical people who just like to show off their hotshot skills and don't care about getting things done. Move fast and break things and all that.

This began with stuff like PHP but really got going with Rails, which preached convention over configuration as a religion, and supplied a standardized framework into which you could easily fit any generic CRUD app that shuttles data between HTML forms and a database (but is painful if you want to deviate from that template in any way.) Note that Rails doesn't use foreign keys and treats the relational database as little more than a glorified persistent hash table.

This set the stage for Node.js (why bother learning more than 1 programming language?) and NoSQL (why bother learning how database schemas work?)


an engineer is more like a plumber than an inventor though.

you don't need to design a new type of screw to make a one off aquaduct, you don't need to design new bearings to make a gear box, you don't need to design a new opamp to make an amplifier, nor do you need to design a new MVC framework to make a one off CRUD app.

You use off the shelf parts and combine them with your knowledge and skills to produce and effective solution for your constraints. If you can't do it with existing stuff, then you design something new that can do it.


The point is that for most things, there were no such off-the-shelf parts in software engineering until recently. You had to invent each screw and bearing yourself, because you couldn't just order a bunch of screws and bearings. Nobody sold them.

Moreover, the difference is that today's technician-programmers don't know how screws and bearings work at all. The engineer who uses prefab screws and bearings still understands them in detail and knows when to deploy them and when not to. Rather than understanding what bearings are for, the technician-programmer has a problem X, reads a blog post that says, "I solved Y by adding some bearings," and thinks, "gee, X is similar enough to Y that adding some bearings may work."

They add some random bearings, some screws, some cams and driveshafts here and there, without any deep understanding at all of why you ought to use these things in a design... and over time build a Rube Goldberg monolith with 10,000 moving parts sticking out randomly that nobody actually understands at all, so as a result everyone is terrified to work on any part of it for fear of inadvertently breaking the other 60 hidden dependencies of that piece.


Bravo. Saving this comment.

For substance: Consider how this is all part of the effort by management to make programmers feel as interchangeable and insignificant as possible.

And it's not even in the name of quality. Plenty of software out there breaks because of its multiple single points of failure in the form of dependencies.


Yes, totally. I can understand why management does this. Coders have a very high turnover rate as a profession. When your genius Clojure or Rust programmer leaves, it's much harder and more expensive to find a replacement than when your PHP or Rails programmer quits.

This then incentivizes new coders to take up one of the popular languages, because that's what most of the advertised jobs are for, creating a self-reinforcing positive feedback loop.


The thing is, it actually doesn't matter. No one needs to be a certain way. Cool things will be done by people that can and will do those cool things. In the end the prescriptivity of this whole space is only ascribed by people internal to the whole system: externally nothing is 'supposed' to be done. This is just a symptom of more people programming.

The thing that will speak the loudest is actions and results. If people don't like depending on modules, don't. If you do, do. Eventually everything will be lost and forgotten like teardrops in the rain.


This "that's just like, your opinion, man" attitude may be fine if you have no worldly goal affinity and are planning on spending the rest of your days in a remote mountain monastery. If you don't care about achieving any particular goal, then yes, all possible attitudes are equivalent. But that sort of handwavey quietism is not actually relevant to almost all people's situations.

If you do have specific short term goals -- such as, say, "building a profitable software product before we run out of money" -- then all methods of approaching the task are demonstrably not equal. Some will work much better than others in terms of achieving that goal.

We can study the empirical results of the numerous attempts already made by other people and thus avoid repeating their mistakes.


This makes sense. But I don't think the two are mutually exclusive. I think goal-motivation is different from goal-attachment. Being attached to "I will do my best to make x happen" rather than x itself focuses on actions and growth (within realm of control) than consequences (often outside of realm of control). Like a product failing because people are just not into it.

I think we can avoid those mistakes but what I see here is people telling other people what to do in a way that sounds unconditional: "x is better regardless of your situation." Some techs are better for x, other for y. Merging someone else's experience tree into your own is good, but it must be contextualized by their perspective--they're working on mobile embedded soft vs. you are on web, maybe, for instance. Or games vs. databasey things. Or their job is a performance engr but you are UI...


What would you recommend to a junior CS major to avoid falling into this pattern?


Specialize. Pick a subfield of CS that you really enjoy, that really resonates with you -- machine learning, NLP, algorithms, whatever -- and become an absolute expert at it. Know your subfield inside and out.

Start thinking about what specialty you want now, today. Do some research, talk to professors and other students, etc. By the time you are a senior, you should know what you want to specialize in and be well on your way.

Then, you can get a job that actually requires deep knowledge. Since you actually care about this, you'll quickly become bored and frustrated if you take a standard startup code monkey job where you are expected to write Rails or PHP code all day, but then that's the only experience you'll have. You'll also be competing with less well educated programmers who are willing to work for less.


Relevant:

'"The Excel development team will never accept it," he said. "You know their motto? 'Find the dependencies -- and eliminate them.' They'll never go for something with so many dependencies."

In-ter-est-ing. I hadn't known that. I guess that explained why Excel had its own C compiler.'

http://www.joelonsoftware.com/articles/fog0000000007.html


Considering the fact that Excel had to run on multiple versions of Windows and also run on the Apple Mac I can understand why they would hate hidden dependencies.

I've experienced the joy of trying to make sure Win32 code runs on Windows 3.11 (with Win32s), Windows 95, Windows 98, Windows Me, NT, 2000 and XP.

Generally it all works, but sure enough there are times when a small code change breaks one or more of those platforms.

Luckily these days things are much better as Win32 is a lot more consistent.


Thanks for a great post.


Yes, we have.

The entire Javascript ecosystem is a huge catastrophe. It will collapse any time soon. It's complex, fragmented and no one really likes it. There are a dozen different tools to get started. No one even understands how to get started easily. There are no fundamental tools. Everything is changing every week. You can't just build a product and then rebuild it even a month later. Nothing works anymore a month later - your dependencies have changed their APIs, your tools have different flags that do different things, there are new data models that you never needed and shouldn't even care about.

The developers are in high stress. Devops engineers are in even higher stress because they get to see what developers don't.

It's a huge mess and my advice to "prefer core-language solutions to small abstractions to small helper libraries to general libraries to frameworks" (http://bit.ly/1UlQzcH) hasn't been more relevant than today.

Software should be developed using least amount of complexity, dependencies, effort and using fundamental tools that have been and will be here for the next 20 years. Cut those dependencies, you don't need them. They're here today and won't be here tomorrow.


So, I suppose you could do something like this instead.

    function leftPad(str, width, pad = ' ') {
      const actualWidth = Math.max(str.length, width);
      return `${pad[0].repeat(actualWidth - str.length)}${str}`;
    }
And that would do a leftPad pretty well, and be reasonably robust to stuff like the required width being less than the string width, the padding character being multiple characters long, and so forth. It doesn't do any type-checking of course.

It also doesn't work on older browsers - both string.repeat and template strings are new. You could fake it with string addition, but addition behaves oddly in the case your arguments are numerical, whereas template strings handle that. There's also a trick where you can say (new Array(desiredLength + 1)).join(' ') to make a string that is the appropriate length, but you've got OBOEs to worry about if you're not paying attention (Array.join puts the character between the elements, so you need an n+1 array for an n-length string). Also, at least on some browsers, Array.join is pretty cruddy, and you really ought to construct the string with an old-fashioned for loop.

Javascript has all kinds of weird corner cases and lots of browser compatibility problems. The fact that someone's written a decent implementation of something that should have been standard in the String object means I don't have to worry about it.

Of course, I do have to worry about stuff like losing access to left-pad when someone throws an npm tantrum, or dealing with future build issues if npm becomes untrustworthy. A cryptographically sound package manager seems like a reasonable want, especially after this week's issues.

But if your take-away from this whole problem is "meh, javascript devs are lazy", you're missing the point.


In a way this shows what a great job NPM did at making it easy to publish packages. It's so easy that people decide to package up extremely easy functions.

As a python developer I would never publish a small package, simply due to the overhead of setting up a PIP package.


Oh God yes. After being spoiled in npm-land for a couple of years, I went back and tried to re-release a very small python module I had written before. I simply couldn't do it. Oh well!


Very true. Although pypi is in a better position than npmjs.org on a few fronts (including security), it's also such a pain in the arse to deal with. Packaging is by very far my least favourite part of python.


> In my opinion, if you cannot write a left-pad, is-positive-integer, or isArray function in 5 minutes flat (including the time you spend Googling), then you don’t actually know how to code.

Spoken like someone who writes functions in 5 minutes that I find bugs in later.

Just because a problem is simple to describe informally doesn't mean it is simple to implement without bugs.


It would be trivial if the language lent itself to trivial solutions and there wasn't a culture of trying to anticipate and catch really messed up error cases that results largely from user errors.

In Python I would write ´if num > 0´, if it matters that it is an integer I would cast it to int, and that handles everything you should be handling at that level. If your user passes in a list instead of a number type, the code should crash because then there's probably an input error somewhere. If a user has monkeypatched a base class to make a number into something else the code should also fail and you deserve whatever happens to you.

Catching every possible error is a mistake.


So, apparently some guys managed to build a system where it is very easy to re-use small parts of other people's code and now the author is complaining that "too much code re-use is happening" ?

I'm fairly old, so I remember the complaints a decade or two ago that people had where "We can compose hardware from IC's and you don't have to know what's going on inside and it's all standard and just works! Why can we not do that with software?!?! (of course that ended up with things like CORBA and DCOM, which was all wrong)"

aaaand here we are in a situation where code re-use is actually happening on a wide scale and now you're complainig about that?

28k lines in an empty project? ha, how many lines of code does the preprocessor generate for #include <stdio.h> I haven't actually measured, but I bet it isn't that far off from 28k lines.


minor nitpick: it's not 28k lines, it is 28k FILES


Which, given what I understand about most packages on npm, corresponds closely to 28k lines :P


The elephant in the room here is Javascript. No other scripting language has been so bloody awful in the past that a five-line module that saves you typing `array.prototype.forEach.call` or something that papers over the language's awful idea of equality comparison with three functions has been "useful" or "more than five minutes coding without references".

Granted, these modules don't do such useful things, but that's the environment their creators were immersed in.


^^ Nice, sane point.


What concerns me here is that so many packages took on a dependency for a simple left padding string function, rather than taking 2 minutes to write such a basic function themselves.

It takes more than two minutes. That little module has a test suite, if you're including it then you have some assurance it does what it says it does. If you write it you've got to worry about whether it works or not.


As a dot net developer I avoid adding packages and references at all costs due to all of these same reasons. Usually for something simple like this left pad example, I just copy the code and put it in to my project in some sort of class of helper functions or extension methods.

Seems like a lot of these basic Javascript functions need to be built in to javascript/node itself or consolidated down to a single package of common core functions that extend Javascript. Like padding, is array, etc. As others mentioned, these are fundamental things in other languages.


Have we forgotten how to program? No, we have changed the way we program. We leverage the work of others to solve ever more complex problems. When we do this we get more than just the simple functionality we require. We get all the testing that comes from a package being used by thousands of projects. We get updates when people find a better faster way. We get to lower the number of lines of code we have to maintain.

Yes, these are simple tasks but do we gain anything doing these simple tasks ourselves? I find there is a finite amount of focus I have when programming, and I'd rather spend that solving the bigger problem.

This reminds me of a discussion I overheard between two professors when I was an undergrad. Prof 1 "This new language makes it so much easier to x, y and z" Prof 2 "Yes but what's the point, I can do all these things in Java" Prof 1 "I could do all these things in NAND gates, but I'll get more work done if I have this tool."


In the case of left-pad, 2538464 of its 2550569 downloads last month are attributed to dependents of the line-numbers package (https://www.npmjs.com/package/line-numbers). So it would appear that relatively few people directly rely on left-pad, which highlights the importance of vetting the dependencies of dependencies.


This is in the description of line-numbers:

DEPRECATED. This is a rather silly package that I do not recommend using. It's easier to copy the ~20 lines of code of this package and customize that code, rather than downloading and learning how to use this package.


Committed five hours ago: https://github.com/lydell/line-numbers/commit/711f6ad0eb1771...

This mess appears to have convinced the author of that.


About 2/3 of those (1.7M) were from babel-code-frame, which is a core dependency of babel:

https://www.npmjs.com/package/babel-code-frame

And the fix was just to drop the functionality and inline the function as a no-op:

https://github.com/babel/babel/commit/09287643c712bcd203bbd6...


Nearly everyone has had it drilled into them the "Don't reinvent the wheel." nonsense for better or worse.

I use lodash in almost every javascript project I start, big or small because it makes my life easier.

I'd rather use the lodash isArray than roll my own.

https://lodash.com/docs#isArray


Do you support IE 8? If not, why not use the one provided by the language, Array.isArray()?


The latest version of lodash (4.0.0) doesn't support IE 8, so you would need to polyfill isArray() yourself or use a pre-modern lodash version.


Yeah I agree that polyfilling makes more sense than depending on lodash just to get isArray(). I wonder if we'll start seeing the sorts of "you don't need..." rants about lodash that we've seen about jquery for some time now?


Yeah but you're loading an entire library to use one function?


Tree shaking.


What's the problem of writing a function with a few lines of code and exports as a module?

I think it's totally fine. Like other people said, it's the mindset we borrow from Unix, do one thing and do one thing well. The function would be well tested, and could be reusable.

I don't understand why so many people just require lodash into their project (when they start project) while they only use one or only minimum set of the functions. I mean lodash is a very great library with clean and well tested code, but it's also quite bulky like a big utility lib, and for me most of the time I only need one or two of the functions I would just go to npm and find a module just do that thing.


Also, Node.JS modules can be used browser side. On the browser you only want to include code you're using so you're not making the client downloading excessive code. A large 500kb module with one useful function is worse than a one 10kb module with the one function you need.


When I was prevented from upgrading Xcode/Swift for a project last year because of podfile dependencies, that cemented in my mind that every time I include a dependency, I'm putting myself at risk.

If I can spend half a day writing the code myself, I will -- because I know it will prevent headaches down the road. Yes, yes, I know any code I write adds the probability of bugs down the road. But at least the roadblock would be of my own doing, and not dependent on if/when the package maintainer upgrades their stuff.


There are lots of useful titbits often left out of a languages corelib.

Back in the day we would have built up various pieces of library code, which we would have utilised rather than continually guessing best practices. For example, the isArray method cited may be trivial but is also non obvious. We'd probably have something like that in our library of useful snippets.

Sometimes we may have shared the code on forums and the like and people would copy and paste the code, sometimes into their own libraries. We would locate them by browsing known locations or inefficiently querying search engines

Now we utilise a central resource and simple tools to have a global library of code. Instead of searching far and wide we query a handful of tools that effectively does the copying and pasting for us.

How that can be considered a bad thing is beyond me. It's not a question of knowing how to code, it's a question of using your time effectively.

Granted, there is the problem of so-called modules being removed and dependencies breaking. This can be alleviated by vendoring your modules, a simple task with most dependency management tools.

Personally I think that published modules should persistent indefinitely based on the license the code utilises, although I'm not clear on the actual legalities of the recent npm issue (although if it's due to a trademark complaint, I don't see how it would ever be enforceable for completely unrelated code in any slightly sane country).


Have we forgotten how to program?

Maybe we've forgotten how to /just/ program. Everyone bangs the drum so hard of "let github be your resume." Incentivizing putting every brain fart you ever had out into the universe instead of just keeping it to yourself.

Just a thought.


The problem is not that we forgot how to program, the problem is that we never actually learned how to create a good programming language which avoids these problems.

Just look at the whole build system and module hell of C and C++...

I totally like the language C++ but I hate the tooling around it with a passion. As long as you only need stuff from stdlib then you are fine, but as soon as you want to create some custom library for use in your other projects? Which should compile on Linux and Windows? Either you use make and Visual Studio solutions or you have to fight with cmake... Just thinking about it makes me angry.

For this reason I just don't use C++ very often. It is way easier to just use Python because most stuff is just an 'import library' away and you can concentrate on your actual program instead of fighting against the build system.

But god forbid you actually try to ship your Python program to your customers...

Why do I as a programmer have to deal with all this crap? I want to focus on programming and not on library management amd writing make files. My CPU is idle 95% of the time so there is enough processing power which could solve these problems.

I have high hopes for Rust and it's integrated build/module system!


It sounds like your problem with C/C++ tooling is that it's hard to support Windows. The reason for that is simply that microsoft bundles its own proprietary C++ toolchain with the platform-specific tools that Windows developers need. Windows developers don't want to install a second posix-like toolchain, and os x/bsd/linux developers don't want to install a Windows-like toolchain because in both cases the new toolchain wouldn't integrate as well with the rest of the system, and it would add unnecessary complexity to the operating system.

The problem with proper unix tools, the ones that do one thing well and integrate with other programs as a part of their design, is that they make doing cross platform work a problem, because those other programs might not exist on other platforms. GNU Make works just fine on Windows, but you're not using just make when you write a makefile. Makefiles typically depend on GNU Make, pkg-config, gcc/clang (or another compiler with the same CLI), coreutils, bourne shell, and an assortment of a few other utilities. That means that Makefiles by design will only work on a single platform.

The other operating systems have essentially made themselves into the same platform for these purposes through the use of system package managers. I've heard Windows is kind of getting one? More like homebrew than what we have in linux/bsd-land but that might still help. But maybe Windows developers will still be reluctant to install software with MinGW dependencies. That would be unfortunate, but fair; I wouldn't install a package that had Wine/Mono dependencies just because some asshole wanted to use their Windows build system on Linux! Hell, I avoid installing smaller things with a Qt or GTK3 dependency simply because none of the software I use needs those and I don't want to clutter my installed-packages list for something that isn't important.

I'm not a huge fan of Rust's Cargo. The list of requirements that the Rust team had for their package manager and build system made sense, and if I had to design a tool that met those requirements, it probably would have looked quite a bit like Cargo, but it makes me sad that the most reasonable design is that which abandons the unix-like simplicity of the python and C (and perl?) toolchains.


> It sounds like your problem with C/C++ tooling is that it's hard to support Windows

No, the problem is that there is no standardized way to deal with modules and libraries in C and C++. If it would be in the standard the cross-plattform support would be much better.

> the unix-like simplicity of the python and C (and perl?) toolchains.

For small C projects the make/autotools system might be "simple", but for larger projects? It is horrible.


Supporting multiple platforms is simple. Assume the user has an existing build pipeline and you need to integrate into it. That means doing one of:

1) provide a single, amalgamated .h/.cpp pair 2) provide per-platform, per-config precompiled static and/or dynamic libs 3) provide all dependencies in the form of 1 or 2

Voila! Your goal should be to provide something that can be trivially added to an existing pipeline.


Of course we learned how to create good programming languages which deal with this problem. A very good and thorough approach was introduced with Modula-2, which, as the name hints to, had modules as a main focus point. Building Modula programs was an easy and quick process. The module system lives on today in the form of Golang - one of the founders was a student or Wirth.


If your package manager is so cumbersome that it makes a 14-line package not worth it, get a better package manager.

We haven't forgotten how to program. We've got better at it.


I totally agree. The npm ecosystem and the generally accepted practices in js these days come close to the bash style of composing & chaining a bunch of solid, specific little programs together to solve problems trivially that would have been a bear to do in many other platforms like java, C++, etc. I cringe at the idea that you should find yourself implementing l-pad in 2016, at least for the vast majority of projects that a typical engineer will encounter in their career. A similar case can be made for something as small as "is positive integer".

While I agree, there are two counter arguments:

1) As noted in other comments, this is a reflection of the core libs of javascript not covering enough of the basics. This is a subjective thing when it comes to where to draw the line, but wherever the border lies between "core" and "community" libs, on the other side you start running into things like "L-pad" and "Lib-L-Pad" and "L-pad2". If there's a great enough fundamental need, you experience a lot of crowding where lots of people offer up their solution to this, and reconciling that across larger dep. chains that disagree on which one to use can become a real burden.

2) Have you ever had the conversation with an auditor (PCI, HIPAA, etc) that your application is actually built on top of a huge substrate of software written by unaffiliated 3rd parties? And that between your own iterations you could easily have different versions for any/all of them? It's a difficult conversation. Much less the explanations to QA about why a build failed because lib X was updated to a new hotfix version in the 14 hours since yesterday's build, after a couple hours of wasted time of initially suspecting your own diffs, and trying to navigate through all the indirection between the actual stack trace and what actually caused the blow-up...


"There are no small parts, only small actors". - Constantin Stanislavski

If there's a flaw to this debacle, it's that packages can be un-published. That is some grade A+ BS.

But no, there is no such thing as a package too small. Coding is hard. Collaboration should be default-on, not default-off.


I completely agree with the author.

The loudest people in the Node community have been evangelizing this practice for as long as I can remember. This shouldn't come as a surprise.

The argument, "If I didn't write it I don't have to think about it" is ludicrous. I just have to point at the left-pad incident disprove the premise of this argument.

The analogy of building things with a bunch of npm lego blocks is laughable. Those responsible for advocating the use of trivial functions by acquiring module dependencies are leading the masses astray.

"But, If I find that there's a bug in a module I can AUTOMATICALLY fix it everywhere!"

No.

You still need to assess how the change to that module impacts any code that depends on it. Just by updating a module and posting a "minor" bug fix can lead to other bugs that RELIED on the behavior as it was originally written.

It's simple, write your own trivial functions. Test them. Maintain them.

P.S.

Another module that can easily be in-lined to every code base you own. (3 million downloads this week).

https://www.npmjs.com/package/escape-string-regexp


Just because it's a one-liner doesn't mean that this code is not complicated. If you forgot something in this one-liner, you could have something that's not working in every case.


> There’s a package called isArray that has 880,000 downloads a day, and 18 million downloads in February of 2016. It has 72 dependent NPM packages. Here’s it’s entire 1 line of code: return toString.call(arr) == '[object Array]';

How anyone can deal with JavaScript for more than 5 minutes is absolutely beyond me


Sensationalist stuff. isArray doesn't depend on anything, but lots of other packages depend on it. Why? Because it's actually kind of hard to tell the difference between an array of things and an object with integer keys. The Array.isArray function wasn't added to the language until ES5, so you need a way to shim older tests.

If you're writing only for modern browsers, you don't need it (if you actually visit the code in question you'll see it defaults to Array.isArray - in that sense, it's a polyfill or whatever). But if your code might run on old browsers, it can't hurt to have it, and it cleanly encapsulates a trick that you no longer have to remember the syntax for.


This was probably grotesquely naive of me, but I literally had no idea how jenga-tower like the javascript ecosystem was. Eye opener!


...maybe it's time a committee of really smart people sit and sip through all the most used modules below N lines of code or smth, and just write an opensource JS-stdlib, hopefully merging in top 30% most used methods of Lodash too? Node/NPM is a great example of why too much democracy and decentralization is bad. Just gather some experts and have them centrally plan a "standard library" then impose it as "industry standard" have a recommended "no fragmentation policy" like "no forking" and "use it all or not at all", the hell with your web app's need for "performance"... even a few hundred Ks of code will not hurt anyone nowadays ff sake...

I even consider PHP a "more sane" language because you at least have most of the useful utility functions in a global namespace and everyone uses them. Of course, the real ideal on this is Python's solution: a nice set of standard libraries that you know are baked in, but most of them you still import explicitly - hence it's pretty easy to write even large Python applications that have a small and comprehensible number of dependencies!

(And more generally: our strike for "more efficiency" in programming is stupid imho! I'd always take a less efficient solution, even "less safe/tested", if it's more "understandable" and "explainable" and sometimes, paradoxically, making things a bit more monolithic and centrally planned makes then orders of magnitude easier to reason about for our tiny ape brains...)


There is also a big programmer efficiency to be gained by having batteries included library: The new person coming to a project has learned the same batteries already, and it need not to re-learn new set of minipackages the last person working on a project has used.

In fact this should be the key selling point for stdlib to JavaScript. It actually saves money from their employers if trivial functions are mostly in the same place.

However pushing a stdlib is difficult there is a lot of hate for Python's "kitchen sink" library in JavaScript world which is totally misplaced. It maybe founded on fact that you should then minify the whole package to use one function in it, but this could be solved with dead code elimination (with proper type checking) in the minifier.


> there is a lot of hate for Python's "kitchen sink" library in JavaScript world

Is there? Most "js people" have never had the pleasureful experience of working with a "pleasantly opinionated" language like Python and the similar feel of its libraries, even the 3rd party ones.

Really, there's something really awesome in the Python ecosystem that should be spread to others before node takes over the world: If done right, being opinionated ("one way to do it") and centrally planned (a BDFL or smth like that) is A GOOD THING for an open-source language/infrastructure project! And it's also good if you extend the philosophy to the libraries and frameworks. Don't ditch the baby with the bathwater!

But I have a really hard time translating this message to "node-speak" unfortunately... Thanks for the "saves money" tip though, never thought of that, will try it ;)


Rob Pike: "A little copying is better than a little dependency"


After working with JS/Coffeescript/Python/Ruby for years, Go has been an enormous breath of fresh air for me. Of course it's all a matter of preference.


Man, I have to quote: `In my opinion, if you cannot write a left-pad, is-positive-integer, or isArray function in 5 minutes flat (including the time you spend Googling), then you don’t actually know how to code.`

You would be surprised of how many developer these days have 'afraid' to write such functions, or how lazy they are, they found this thing and just add to a project, then push to some google list and the project got a lot of followers, and in the next day the project has changed about 90%. I saw this happen over and over again in this ecosystem, this is insane dude.

A lesson I learn is: you _need_ to read every module source code before add to any project, the NPM ecosystem has so many "shits" out there. You cannot trust in any npm module, recently I tried to trust in a module that has more than 5k stars, but I found a such ugly bug on that, that I feel my soul die, and I swear I hear the angels cry, thats not how open source supposed to be.

These days, seems that people dont care about the 'bug free' as long as it work a half way.


The Python community has a proper response: https://pypi.python.org/pypi/left-pad/ /s


Then you can do variants.

A Java left pad implementing a Padder class instanciated by a PadderFactory that you can get by a PadderInjector from a PadderRegistry all that accepting an AbstractPaddingString.

Then one in Ruby where you monkey patch the string to that it pads, then add an AUTO_PAD settings set to True by default and a fluent API to chain padding.

Then one version PHP version containing pad_string, then real_pad_string that deals with unicode, then real_pad_string_i_swear_this_time that that call str() on parameters automatically.

Then a Haskell one with a monad and a trampolin and some |=> giberrish to mix it all.

Then a Go one that creates a string padding microservice. With a docker file, just in case.

And the last trend in Python, an aio_string_pad which gives you an asyncronous padding coroutine, but under the wood calling run_in_executor, and optionally auto start an event loop.

This is 99 bottles of beer all over again.


So any developer could make this in five minutes, but for some reason they can't verify whether or not it works? That doesn't make sense.

In reality it could take an hour to get this working properly, but it does take only a couple minutes to verify that the solution here is correct. There are certainly good reasons for not adding extra dependencies to your project, but trading a known amount of time to check that an existing project does what you want for an unknown amount of time to redo it yourself is probably not a great bet.


Unzipped, the source code for GNU coreutils is 30MB (zipped 4MB). This is a great example of a collection of single purpose functions you should never rewrite yourself. There's only one dependency if you want to use them because they're packaged together. With normal desktop code, 30MB doesn't really matter and you can link only what you need. Can you do that with the usual Javascript package managers/bundlers, or would you need to send the whole 30MB package to the client to use one function from it?


It's not that anyone has forgotten, it's that a lot of people never learned how to in the first place. Every programmer community is riddled with these problems but the NPM world seems to be the worst. The ruby gem "american_date" annoys me to no end. It's just a highly-specific implementation of Time#strptime. Gah


node is can be a good idea. But people don't take JavaScript programming seriously. Most libraries objectively suck.

Google, authors of V8 and #1 subject matter experts on V8, have published coding standard. Does someone use it in the node community? No. Everyone loves "standard", a lousy standard that allows everyone put a badge on their github page while still having a code base full of vomit.

JSDoc. A great solution for documentation. You would expect major libraries to adopt it, or a similar thing. But again, no. Major libraries such as busboy do not use them. The documentation resembles a napkin.

Then everything else: input validation, error handling, consistency... etc. Take "request" for instance, one of the most widely used libraries. The state machine it implements is inconsistent. You abort a request and get a timeout, you can abort a request without starting it and get an exception. Issues that will drive you insane while debugging.

Express, one of the most widely used web frameworks on node.js. Do this on a route: setTimeout(function(){ throw new Error(); });. Great, now you have broken out of the error handling context. Great job.

Node libraries suck all across the board. It's the PHP of the 21st century. There are exceptions, like: lodash, bluebird, and others.


We have. I spent some time optimizing [0] a String.repeat function over at Stackoverflow and I was surprised that many developers today don't know what they are doing, including core team members [1]. Specifically,

    function repeatString(str, len) {
      return Array.apply(null, {
        length: len + 1
      }).join(str).slice(0, len)
    }
[0]: http://stackoverflow.com/questions/202605/repeat-string-java...

[1]: http://stackoverflow.com/questions/202605/repeat-string-java...


So I don't think this is an example of core team members who don't know what you're doing.

This function repeats a string up to a certain length. e.g.

repeatString(foo, 10) = foofoofoof

This could entirely be what the developers needed in their code base.

The fact that it doesn't repeat a string N times, is at best, an example of bad naming.

The issue here is that someone unrelated to the core team grepped the node codebase for the term they wanted and threw a function up as a SO answer without understanding what it did.


Allocate and join an array of blanks just to repeat a string is acceptable?!


Joining an array of strings is sometimes better than concatenating a string in place, yes.

Consider the following:

  1: var str = 'foo';
  2: str += 'bar';
  3: str += 'baz';
At program load, you have three string constants, 'foo', 'bar', and 'baz'. At 1, 'str' becomes a pointer to the string constant 'foo'. At 2, 'str' becomes a pointer to a string 'foobar'. Memory is allocated to store this string. At 3, 'str' becomes a pointer to a stinrg 'foobarbaz'. Memory is allocated to store this string.

Now consider the following:

  1: var str = ['foo', 'bar', 'baz'].join('');
At program load, you have three string constants, 'foo', 'bar', and 'baz'. At 1, 'str' becomes a pointer to the string constant 'foobarbaz'. In the array concatenation, the JS interpreter can create a single string buffer large enough to contain the contents of each array element and the separator string * N - 1 (where N is the number of elements), and return a pointer.

Once you have a large number of elements, or are joining large strings, the performance difference (especially regarding garbage collection) could matter.


You'll be surprised, but my implementation is, roughly speaking, 10 (Chrome) to 100 (Safari) to 200 (Firefox) times faster [0], depending on the browser!

[0]: https://jsfiddle.net/nikolay/Lgshxrk8/


I guess I'm also as guilty of looking at input/output rather than implementation here.


Here's a benchmark [0]!

[0]: https://jsfiddle.net/nikolay/Lgshxrk8/


One of the things I've enjoyed the most while programming in Go for the past couple years is the standard library and how much the Go community emphasizes keeping external dependencies to a minimum. For most projects I find myself using few if any external packages.

Now of course there are times you'll want to reach for a library, say for something like an http router, and up until recently the dependency management side of Go has been lacking. But when a pattern or library arises that many find useful the core team is open to pulling that in to the standard library if a strong enough case is made, for example in the context package (https://github.com/golang/go/issues/14660).


To me, this looks like a symptom not of bad programmers but of a terrible standard library.


Well put. When a hammer requires endless accessories to drive a nail, is it the right tool for the job?


Formula for a top HN article:

1. Make an observation about a popular thing. 2. Blindly extrapolate. 3. Make one or more broad, controversial statements. 4. (Optional) Nuance.


The main reason there is a fetish for these micropackages is a fetish for github stars. The formula seems to be: Overly generic + Simple + Javascript = Lots of stars.

That being said, there is something to be said for using these micropackages. Left padding a string is easy, but you might just have forgotten about that one edge case where in browser X and language Y you have to do things different. It's not really the case here, but things that seem simple at first often turn out to be hard because of some edge cases. One might hope these edge cases are solved if they use a library.


I call this kind of attitude the "Tea Party" of JavaScript development. The reason why we currently have JavaScript tooling fatigue is exactly because Tea Party developers insist on writing everything themselves instead of trying to build a better abstraction. The lesson here isn't not fewer dependencies: it's managing dependencies. NPM should not allow someone to arbitrarily remove modules that other's may be depending on. It's like building a bridge and them deciding to remove it after a whole city now depends on it.


It's more like planting a bush in your front yard, and for some reason the metropolitan train system decides to use your bush's shadow to tell the time of day, and builds that into every aspect of the entire city's transportation network. Then you cut the bush down and everyone starves.


Here's the funny thing that gets forgotten: in a lot of commercial software, 3rd party dependencies need to go through legal to get properly vetted and attributed and so on. This also usually requires an engineer (to be able to answer things like if it's dynamically linked or not, etc.).

As staid and corporate as it might sound initially, it's a very smart thing to do. One screw-up with licenses could be catastrophic. Are you all really checking that carefully?

I can't even imagine how any sort of proper legal checks could be done with a trillion micro libraries.


I'm content and happy not knowing what the fuck this is all about. Pad left? Are you kidding me? If your tower og babel fell because of your reliance on some script kiddie's toy project, I am happy and content knowing you get what you deserve. Law of leaky abstractions, motherfucker.


If I engage in as much hyperbole as the author, where does "write it yourself" stop? If I'm working on a team of two, should we each write our own left-pad? How about a team of three? Four? Five? Fifty? At a certain point, it makes sense for that to be written once for the project. We spent 30 years in software engineering trying to figure out how to get code re-use, and now that's it common and widespread, we want to go back to NIH?


I think about this every time I write a UTF-8 decoder. It's a task which is simple enough that I can bang it out from scratch about as fast as I can find a library that does it, so I can't really stomach adding a full-blown dependency to deal with it - but it's also just complex enough that I have to think about it, write some tests to make sure I didn't forget something, and wonder whether this implementation is any different from the last one I wrote.


I have 'tools' repository on my local gitolite server just for that. Saved a lot of time already.


I pretty much came to the comments to post something similar. I'm sure there was a long comment thread here on HN discussing the perils of NIH this week. This seems like the end point of the alternative.


NPM modules can be used on browsers. On browsers, space is a premium.

Why would you want to install a 500kb dependency that has only one function you need, when you can install a 10kb dependency that has it?

Would you want each of your five 20kb dependencies to re-implement the same 5kb function, increasing the code you must send to the client by 20%, or would it be more optimal for each of those dependency to use the same 5kb function?

The author rants about practices of developers from different programming environment, without experience, without figuring how things came to be. If he did give an effort to think from the perspective from Node.JS developers he’d have addressed the previous two points.

This is like going to a friend’s house and complaining everything is put in the wrong place. It would have been wise to immerse in Node.JS conventions and observe for a while before making comment.

EDIT: Reply to scrollaway:

I've also understated the problem.

Let's look at the problem in the current Node.js environment, it's not uncommon for a web app to have 20 dependencies, each of those have 10, and each of those 10 have 5. That's a total of 20 times 10 times 5 = 1000 dependencies in total.

Let's say you were to remove a 10 line library function that's "standard library-like", used by 15% of those dependencies, and have each of the existing dependencies re-implement that in each of those dependencies that uses it.

15% times 1000 times 10 lines is 1500 lines of code.

So if you're going to troll a solid argument by nitpicking, do it properly and get the details right.


FYI, you can reply to my comments if you click on their timestamp, in case the reply box isn't showing up

> So if you're going to troll a solid argument by nitpicking, do it properly and get the details right.

First of all, I don't appreciate you calling me a troll. Someone who mentions "5kb functions" clearly has no idea of what 5kb represents, period.

And second, this is not a solid argument at all. There is logic behind it, which is why we usually have high quality stdlibs in popular languages. Javascript lacks that. So instead, javascript gets this absolute mess of an ecosystem, where things like this can happen.

Several people have brought up various issues with the way it's done now. Dependency hell, for one. Lack of discoverability, which in turn leads to duplicate libraries, which in turn leads to a system where you have 1000 dependencies, but despite your "modular" idealism you still have hundreds of duplicate dependencies. Not to mention all the duplicates of different versions of the same dependency.

This "saving lines of code" math is completely broken exactly because this stuff is not in the stdlib. The various issues with the JS ecosystem mean that the actual results are nowhere close to ideal, and have a net negative impact.

I also love when people mention how much "one time cost" is saved and back it up with similar math, completely forgetting the amount of continuous time wasted downloading and installing these dependencies every time.


You're mixing up development time & bandwidth used by client browser. I was addressing the latter. Not having to download a 500 kilobyte library in order to use a single function is a big reason why the node.js ecosystem is the way it is today. Yet it is completely unaccounted for in the article. The author did not go into how the Node.js community came about to this practice in the first place, before going on a rant about it. That is what I was criticising. Whether having minimal modules maximises utility in society is completely irrelevant, because I was addressing perspective and etiquette.

You are a troll because talking about the actual number I used and how good a grasp I have in my mind completely misses the point. You're criticising the javascript ecosystem, I get that, but you completely missed the point of my comment. If you're not going to try to understand how Node.js conventions came to be, what it's strengths and weaknesses are, what the trade-offs made were, then it's inappropriate to focus on particular weaknesses of its practices.

It's like going on about how Objective-C development sucks because it's hard to do on Linux. But you're missing the point about what Objective-C's strengths are that attracted Objective-C developers in the first place.

The author should spend some time acclimating to Node.js best practices before writing this article. And you should do the same before you start knocking down straw men to prove how right you are.


I'd respect your comment quite a lot more if you'd stop calling people who disagree with you "trolls".


You would not have been accused of being a troll if you had not explicitly said I lacked a "good grasp" in my mind.

I agree with https://news.ycombinator.com/item?id=11313092

You can dish it out, but you can't take it.


It's quite ironic how you're lecturing people about dependencies and bandwidth when you yourself don't seem to have a good grasp in your mind of how immense even 5 kilobytes is (you talk about 5kb functions). 5 kilobytes (well, KiB) is 5120 bytes. That's about 200-300 lines of code. If you're talking about minimized code, maybe up to 500 lines.


While a 5kb function is immense, I work on a legacy code base and I have seen functions up to 2000 lines long. You possibly overestimate the quality of code that people in business actually write. I've worked in a few places and all have parts of the code which I would thought were made up if submitted to the dailywtf.

Also, have you ever seen a commercial library require >2MB of minified JS? Here's an example:

http://cdn.wijmo.com/jquery.wijmo-pro.all.3.20153.84.min.js

That file is almost 2MB minified (5.6MB unminified).

And that is is only 1 of the few different files required for our dependency on it, there is a bunch more javascript/css also required. Would I use that library on a new product? I'd probably advise against it due to the size, but a legacy product has dependencies and you don't have the freedom to rewrite everything all the time.


I've dealt with such functions, but they're seldom the type of functions that other GGP is talking about (small, simple, exportable functions).


A lot of the discussion here really isn't talking about the problem at hand.

From the perspective of Babelify users, a major bug was introduced into software they depended on. I don't know how much money in developer time was lost due to this but it would almost certainly be in the thousands of dollars.

And it could have been a lot worse. It could have been something more complicated than left-pad. The author could have introduced a vulnerability or outright malicious code, or been hacked and done the same, and millions of people would have downloaded it and run it.

Arguably, small modules are good if you control them. Maybe they are more composable, maybe they enable better testing, maybe they encourage code reuse. I am not going to argue for our against small modules.

But none of the positives of small modules matter if an unknown developer who you have no reason to trust can change or unpublish the module out from under you. It's irresponsible as developers to risk our employers' and clients' businesses in this way for a function we could write in five minutes.


Its interesting how everyone used this as a chance to attack the small modules approach. This approach definitely has downsides, but the problem caused by leftPad being unpublished wasn't one of them.

If jdalton declared a jihad on arrays tomorrow and decided to pull all array related functions from lodash, we would have the exact same problem.

If kriskowal decided that Q must mirror built in Promise and published a version that does this tomorrow, we would again have the exact same problem.

There is only one connection between this problem and the small module approach. As the size of a module decreases, the number of dependencies increases and so does the number of authors that produced your dependencies. With the number of authors increasing, the chances that some author decides to go rouge or protest for some reason also significantly increases.

Therefore, its irresponsible to use this approach with a package manager that allows an old, established module with many dependents to be unpublished so easily by the original author.


Would people automatically update to the new versions of the libraries you mentioned?

The problem with unpublishing is that it changes an existing version. If instead of depublishing a correctly versioned empty module named leftpad was pushed to npm (increment major, because a non-implementation is incompatible with an implementation), there would not be half as much pain.

As long as unpublishing exists, micromodules increase the "attack surface" to this specific method of changing the contents of a specific published version.


Which means unpublishing is the problem, not micromodules (in this particular case).


Absolutely true, but the scale of the problem multiplies with micromodules. More unpublishables are worse than less.


This is a non-issue and taking focus away from the real issue. The issue is the security hole that NPM opens up when a namespace can be grabbed up by anyone if the original developer pulls out.


Personally, it's not about the 'original' developer pulling out, but about the simple question: who or what do you trust?

1. The developer, 2. The content distributor, 3. The code (by git SHA ref perhaps), 4. The contract of the code (using formal verification), 5. The legal contract with the code supplier.

And if you trust 1, do you expect of him to sign the stable releases using GPG tags?

All the focus on OSS nowadays seems to be on 1, but as professional engineers, shouldn't we focus more on 4 and 5?


The OP is deliberately talking about a completely different issue that just happened to be spawned from the recent drama.

He is talking about learning to code vs asking google for implementations of even the most trivial things and about what a useful library is.


I know I wasn't interested in doing any JavaScript until they started modularizing it and npm was that on crack. Possibly once the kids had their shiny new npm, some went mad with power and took it too far. Take the good with the bad though as the JavaScript language is known for.


This culture of tiny one-function modules sounds like Joe Armtrong's proposal about the "Key-Value database of all functions".

http://erlang.org/pipermail/erlang-questions/2011-May/058768...


I'm not really sure whether or not I should to add my voice to the din, but I feel like this whole thing is more a problem with npm and what it allows vs. what it encourages (and the rather paltry standard libraries in Node & browsers), rather than a problem with developers feeling entitled to not have to write their sorters and padding functions.

npm actively encourages structuring projects as many tiny individual modules and dealing with the resultant dependency trees and deduplication. Both of these things (along with the ease of publication) combine to encourage people to share their packages.

They make it incredibly easy to consume code from other people, but but at the same time provide a similarly low-barrier mechanism to retroactively change published code. That combination seems like a way more deserving topic of criticism than the unending refrain of "developers these days are so lazy".


Joe Armstrong had an interesting related comment here:

http://erlang.org/pipermail/erlang-questions/2011-May/058768...

Maybe the unit of modularity should be a single function and we can do away with modules?


I find NPM packaging ridiculous. Awhile I go I used NPM on windows, where the folder hierarchy became so deep it broke windows file handling. I could not delete the modules folder. I had install a npm package which allowed me to delete it. I think this is fixed in new versions by flattening the hierarchy, but still.


I've been there. If I recall correctly, I found that a "rmdir /s" from a command prompt does the trick.


There's so much wrong here, it can't even all be seen at once. The part we can perceive is only a projection from a higher dimension into our three-dimensional space.


The insanity is that JavaScript doesn't have a standard string library. More a case of forgetting how to design a programming language than how to program.


Contrast with the Scheme Request for Implementation (SRFI) process, see <http://srfi.schemers.org/>.


Agreed. Most development groups should be building a local collection of utilities that contains all of these snippets, and most importantly, some documentation of what they do and some unit tests to demonstrate that they are correct.

No need to have global dependencies on small snippets that really should be in a core library anyway. C has libc, Java and C# have the creator's (Oracle or Microsoft) standard set of libraries, Python has the "batteries included" stuff in all the different distros. And so on. All of these snippets rightly belong elsewhere, not in packages.

And even if you did get them added to the right libraries, I guarantee you that you will not get rid of the need for a collection of small, and somewhat random, in-house functions, classes and libraries.


I started out really thinking Kik was wrong to sue this guy but like with all things, the longer this goes on the less sympathetic I grow.

Write your own goddamn utility classes, people. Or learn how to suspend package releases, include them in your projects, and smoke test your releases.


These issues are ortogonal.


After reading this, i don’t think i’m capable of ever complain about OOP again. Whenever you think you’ve seen it all, web developers always manage to come up with something worse. The only thing more depressing than this is the comment section below the article.


For a less sensational review of this situation, NPM published a post-mortem:

http://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm


Insight from this story: If it is important that a thing be done correctly, it should not be made so easy to do that it will end up being done by people who shouldn't be allowed to do it.

I may have just invented a rule for choosing programming language and systems there.


Another explanation for the flourishing of these one function modules may be the fact that some people feel a kind of high/achievement from being able to say they have a published module out there. A sort of bragging rights if you will.


"I may not be Mick Jagger, but I am playing in a band and we did have some gigs (before we had that 50% decline in audience when my mother got bored and stopped showing up)" - just about every twentysomething before coding became a desirable career


It beats the alternative - pulling in some huge package with lots of little functions, all of which end up in the output. At least you're not loading huge amounts of unreachable code into the running system.

In languages with linkers, the better linkers would discard all unreachable functions. Then came DLLs. Use one function in a DLL/.so, and the whole thing has to be loaded. Go and Rust are usually statically linked, reflecting the fact that pulling in big DLLs was usually a lose. You rarely have two different programs on the same machine using the same DLLs, except for some standard low-level ones such as the C library.


I know the discussion revolves around the amount of dependencies, but I want to add a comment about semver, which has a part in this mess:

In my opinion it will not be done right in 80% of cases.

Every breaking change requires updating the major versions, but developer are hesitating to go from 1.0.0 to 6.0.0 in a month.

The way out is staying in the 0.x range therefore abandoning semver alltogether.

A nice write up about how packages are not following semver in java-land:

http://avandeursen.com/2014/10/09/semantic-versioning-in-mav...


I wouldn't even think of looking for a package that does something as simple as the ones mentioned. If I need to pad a string, my first thought would be to create a new function, not look for a package...


I think we missed the point here. The fact that it's 11 lines is meaningless, this could have been babel itself.


The problem with shared hyper-modularisation is that it assumes the name of a function is unambiguous with only one valid implementation. If that were true, it should be encouraged but given it isn't, the practice will crushed by ambiguity and unintended consequences.

My app might well have an is-positive-integer function but it will include a range of context dependent choices about e.g. floating point, infinities, zero, null, "9", "9 ", "09", boxed numbers, string representations exceeding js max int, etc. etc.


I can't take this author seriously at all, one of his most egregious cases is the is-positive-integer library which until today had around 30 downloads in the last month.... No one was really using this and furthermore of course you can find bad/iffy code on NPM for the same reason you can find bad/iffy code on github. ANYONE can publish code. I could write a similar module for any other library, publish it to their repo, then scream LOOK! python is stupid and python devs are stupid.

I firmly believe that building on what has already been done allows for much safer code written at a quicker pace. Why should we constantly repeat ourselves? Also by using npm modules we can abstract logic and prevent someone on the team from going in and modifying it for their own use. It is a documented/constant function that we can use knowing exactly what it does. Is it a better world where everyone just copies code out other's repos and then has to include the licence/docs/tests along with it? It's much easier to just pull in the repo which contains everything and make it trivial to see where a function came from.

People are blowing this whole thing way out of proportion just because it makes a good headline "11 lines of code broke node"... You can all try to shame people who build on what's come before and chant "Not invented here" but I'll opt to build on what is proven to work instead of rewriting everything. At the end of the day that's what ships products.


This isn't forgetting to program, it's a deliberate choice as to how library code needs to be organised. We can disagree about choices made, but let's not assume other people aren't able to code as well as we think we can code.

That being said, I think this is a perfect example of where a good concept (small, tightly scoped modules) is applied dogmatically at the cost of the codebase. It's the node.js equivalent of AbstractRequestFactoryFactoryFactory stuff you see in Java, and the Mock Messes you see in Ruby.


> What concerns me here is that so many packages took on a dependency for a simple left padding string function

Clearly to mitigate such a tightly coupled dependency, Left-Pad should be a micro-service. :-\


Every single dependency you import should make you nervous. You would look at it like hiring someone. You are letting go of some responsibility. You are letting go of some control. The end result might be better than you could do in-house, but it might not. But you are already hoping that it's better, and giving up control.

Save these for the things you can not do in house, like NaCL. Don't write that yourself.

But string padding, sorry. Any developer worth their salt would laugh at adding a dependency for this. It's irresponsible, and comes across as amateur hour.

This is a simple case of optimizing for responsibility. The inexperienced programmer does not know how to do this, because they have not spent enough time being responsible, and having to deal with the fallout of managing responsibility poorly.

An experienced programmer carefully manages responsibility. A simple function, that is easy to understand, easy to test, and easy to reason about, is something that makes more sense to either pull all the way into your codebase, or write yourself.

Years of doing this means that managing dependencies should never be just slap it into your packaging system and start using the function. If you are on the line for making the wheels turn for a large scale platform that is directly connected to a monetary transaction of some nature, you will quickly find yourself preferring to remain responsible for everything that you possibly can control. There is certainly enough that you can't control to keep you on your toes.


I think this is the fundamental thing people don't understand about NPM and JavaScript, and the web in general:

Nothing is included. And that's a feature. The web is not trying to be the kitchen sink. That's iOS. They provide high level APIs for everything. And as a result, the platform is architecturally only as vibrant as Apple can make it.

Now maybe you're happy with Apple, maybe you love the iOS APIs. But if you don't, you're stuck. There's not a rich bed of alternative view layers that you can draw from to build your own vision of how software should work.

Node and the web browser strive to be lowest common denominators. They provide just the very basics: a document format, a very simple programming language, and an http server. The rest is up to you.

That's pretty scary, and so the JavaScript world has dabbled in frameworks. In the end all-inclusive frameworks are antithetical to the spirit I'm talking about, so things trend towards small modules that do one thing pretty well. People go overboard sometimes. I would argue left-pad should just be a copy-pasted snippet rather than module. But that's not a sickness in the community, it's just developers feeling out how far to go.

If you like every application to look the same, and you don't mind being chained to enormous and restrictive standard libraries and frameworks, then you will hate JavaScript. If you like writing software from scratch, forming opinions about all of the different parts of the system, and allowing each application to be built out of different parts, out of the right parts, then you should give JavaScript a closer look.


This is flawed. Basic things like checking if something is an array or a integer should be part of a stdlib. No one is saying whole frameworks need to be included, just basic functions, partially because JS isn't sane in the first place. Nothing in C's stdlib forces apps to look the same.


You are talking about extremes here, but there are nice place somewhere in the middle, where open source libraries/modules exist to help you solving complex problems and you are free to write whatever you want.


The fact that anyone has to even think about code size is the real problem.

Yes, downloading a giant JS lib for one function is insane, hence the ton of tiny dependencies.

However, it is equally insane that basic SDK expectations found in every other language has yet to come to be pre-implemented by the JS engine in the web browser itself. Some basic code ought to already be on the localhost the moment your app arrives.


Need more of this WTF stuff? Have a look at the docker registry/repository/hub and those pearls of wisdom of the new DevOps guilde.


Have to put a little clarity over my "so sad yet so true".

When the developers of such a serious library as React start to depend on a third-party one-function module made by some Dick-from-a-mountain (a russian idiom that means a random person who did nothing significant but tries to show out in all possible means), that means React developers are even more to blame than that Dick-from-a-mountain himself.

If you make any really popular piece of software, you absolutely must have a failover plan. No excuses for not having it.

But what's even sadder is that this issue had spawned a new wave of pseudo-elitist attacks on the entire JS dev community. Calm down guys, things like that could have happened for any language that has a widely-used centralized package system (Perl's CPAN, Python's pip, Ruby's gems etc).

Let me repeat that again: languages don't make software bad, people do. Just don't let such Dicks-from-a-mountain rule over your own modules with elementary stuff like leftpad, and you'll be safe.


This is literally one of the funniest things I've heard about in months. Look for my new python library isDict, coming soon.


Here's a proposal (that I'm sure others have come up with in the past) -- why not create one big, community backed "batteries included"-type module that would implement all the small, commonly used functions. This could combine all these ridiculously small libraries and greatly reduce the number of necessary dependencies for a package. Extending the standard library should be just that: standardized. If the entire community focused on one project like that they could just as easily write the same code (but with smaller package.jsons, less require()s, and less time spent learning new libraries/searching for the right libraries. In fact, it would be great if something like that could be packaged as a standard node module so you'd get the same sort of quality assurance as you get with official projects.


because everyone would stop using it in a week when the next one arrived


<rant> The problem here is JS developers have baked in the notion of having NPM as the alternative to Google + StackOverflow + own thoughts. It's really a no-brainer (literally) to just slap another package than to bother thinking about what a piece of code does, the edge cases and pitfalls. Move fast and break things, right?

Sure there was some argument about Unix philosophy, small module doing one thing and does it very well. Did anyone bother considering the quality of most NPM packages? Quality is not reflected with passed Travis CI or extensive community testing and feedbacks. Not at all. Look at the those packages on apt-get. They are modular and robust. They do what they were supposed to do.

Now take a long hard look at the state of NPM. What do we have? People clamoring for reusability and whatnots. Most of them don't even know what they're talking about, just reciting the latest hip statement from the Internet. Being mature about development means accountability for what you do, not pushing shit around that you don't even have knowledge off. As a self-proclaimed polyglot, I love JavaScript as a language but not the ecosystem. It's like watching a dog chasing its tails:

- Endless loops of discussion that help stroke the egos but not improve anything else.

- Craps for resume/repository padding, not for actual developers to use.

- Bandwagon mentality that just pushes the latest fad along, and the herd towards the cliff.

The notion that JS developers are kids playing grown-up, has been reinforced with this NPM incident. If we want to discard that notion slowly, we need to be more mature developers. It's that simple. Here's what I think we could do: - Have a clear idea on what dependency you need. Browser, IDE, terminal etc are dependencies. Basic type checking is not. - Be better craftsmen. Roll and maintain your own toolboxes. Only share a working hammer, not a broken nail or a wood chip. - Note that for each package you publish, thousands more hours would be spent on learning, adapting, using and reporting mistakes. Collectively, the current community wastes so much time with finding the right things to use. Often times, we learn much more by playing with code, even posting on StackOverflow. That's hands-on, `npm i` is not. - Own the code better. The idea that teams like Babel and React devs with all the brilliant developers choose to put their eggs in a private corp's whims is just scary. You can't hope to build robust software while playing Jenga tower.


I agree that some packages might be too much but I don't think `left-pad` is one of them.

I wrote my own left-pad for a project I'm working now and I had to revisit a few times for tiny problems and lack of time to write tests. I would definitely use `left-pad` module if I knew the existence at that time.


Separate from the discussion of whether super small modules and hundreds or thousands of deps are a good idea, is the point of operational stability.

Putting on your devops hat, whatever your dependencies, from a reliability and reproducibility point of view, you should control your reliance on unexpected decisions of npm or third-party developers. A lot of the panic with npm issues comes from people blindly using the npm registry and then seeing breakages, with no safety net. I hate to say "I told you so" but this is an issue we worried about a lot when considering Node productionization last year: https://medium.com/@ojoshe/fast-reproducible-node-builds-c02...


I am posting the article "No silver bullets" again, in the wake of the npm fiasco. I think it is an essential reading for every programmer, every year!

https://news.ycombinator.com/item?id=11350728


This problem speaks volumes about the myriad shortcomings of the JavaScript standard library, in my opinion.


Couple of things:

* If you're in favor of the micro-module approach, you shouldn't be relying directly on NPM, and should have something like Sinopia in place. After all, external code isn't the only thing you're vendoring, right?

* Micro modules are fine - but your application code should depend on a privately published utils module whose entry point is a prebuilt distribution of all your external micro-modules exposed through a facade. Your utils module deps are all installed as dev dependencies to avoid the Fractal Nightmare.

* Yay, now you have your own 'standard library' which still manages to leverage the NPM philosophy of distributed code. And if some twit decides to throw a tantrum, it will only impact future builds of your custom std lib - and you'll know about it at build time.


Yeah look at all these modern languages that do so much for you, like .empty() on an array -- have we forgotten how to do simple comparisons?! You could just take a second to consider the properties of an empty array, namely it contains no items (.count() == 0).

My point being, if something is a completely reusable and basic feature, a dependency is totally just. I remember a few years ago when I and all devs I knew (which weren't many, I was maybe 17) had our own libraries to include in all personal projects we made. It contained features we had to look up once and from then on just automated and imported, stuff like password hashing or input checking. This went out of style as single-programmer programs are going out of style, but the little useful features are still there.


One can argue that modules are good, but them depending blindly on newer versions like that was bad dependency management.

I say this because I strongly believe that reinventing the wheel is unnecessary and can bring more problems than not.

There are many examples, and I could come up with a made up one, but here's a very real bug that I debugged from another programmer not so long ago:

So, he came up with a JNI for SPSS's C library, applied it correctly, and got haunted for lots of months with an unsolvable bug. The problem? he miswrote the final copy of the file, and sometimes, some bytes where copied twice.

He tried to solve this problem for a long time (and eventually lived with it because SPSS was still resilient to this)

Is this concrete example of a 'module' ridiculously short? yes, but my logic still holds IMO.


This shows the beauty and success of npm in making it very easy and cheap to publish small modules.

It's not that we can't write a left pad function ourselves. It's that we might easily miss an edge case or make a mistake in doing so.

The author seems to be hung up on a preconceived idea of what a package "should" be without actually offering a compelling argument for why a single short function can't be a module.

Yes, every dependency you introduce is a liability. But so is every line of code you write. I'd much rather take the risk on a shared library which can be audited and battle tested by the entire community.

If a function is so easy to write, it's trivial to check out the module's code, review it does the right thing, and then lock the dependency.


One should think of these requires as like .h files and the underlying code as something like a .c file. They're public definitions with potentially changing underlying code.

It's good to have small packages. Don't forget that the underlying ECMA script is changing so the implementation of these 'libraries' are (or will be) different over time from what they used to be. If somebody finds a faster way to do the method, then it will be done.

Finally, anybody who has used js in the real world understands how many corner cases there are and how difficult it is to make durable methods (i.e. how to know if an array is empty - which requires like 4 different conditions).


On DigitalOcean instance I cannot even use browserify+minify+babel6 cause npm process is killed by host (it consumes > 512Mb of RAM). So I have to manually run browserify + babel then minify. Still it produces 500kb of bundle.js :D


The problem is not of small modules. The problem is lack of dependability. If the language patrons stand behind a set of modules and guarantee continuity and availability, it really doesn't matter what is in them and the world can continue regardless of how insane the module or the whims of any one author. This is not about the technical merits of having or not having a stdlib. The module in question could have been anything.

Making this about is-positive-integer misses the point that this is a social/political problem not a technical one. A language ecosystem must address concerns of business continuity as first class concerns.


My current programming project, my goal has been to do as much in-app as possible. Does that mean I'm more likely to have bugs in my own code? Yes. But I've learned a ton doing it, and I know that my code doesn't have a giant monster of bloat hidden behind some random dependency somewhere. And yeah, that means when I wanted to handle email, I learned a heck of a lot about how programs handle email. Did it take more time? Yup. Education is time well spent.

I've got two dependencies besides the basic framework my project is built on: A scheduling engine, and a database interface. I eventually hope to factor out both.


Spoken like someone who doesn't have a tight deadline.


That's true, I don't. But if your deadline is too tight to do it right, maybe you need to reevaluate your project plan.

I mean, I can certainly see using more dependencies than I'm using. Mail is a great example of something that probably should be handled by dedicated, well-tested code. But to carelessly dump piles of third-party dependencies in your project to save a few minutes, as talked about in this article... just crazy.


"If you don't have time to do it right, you will be forced to make time to do it again."


Spoken like someone who isn't on a tight deadline.


Somehow, someone always forgets that engineering is about trade-offs, and so every few years we can an indignant series of articles about how stupid and ignorant today's programmers are and how we should all go back to the same processes that they called stupid and ignorant 4-5 years ago.

Relying on reimplementation, copy-paste, npm shrinkwrap, or various other ways of importing third-party code into your repository results in the following advantages:

1. You know exactly what goes into your product, and can audit everything for security, performance, or coding standards.

2. You often end up importing less, as third-party modules may have functionality that you don't need but other clients do.

3. You can modify the resulting code to add showstopper functionality, even if upstream doesn't want to.

4. You aren't subject to the whims of someone removing your dependency from the Internet or replacing it with a version that does something you don't want.

Relying on lots of little libraries installed via package manager gives you the following advantages:

1. You can easily install & try out modules that other people have written, letting you test out new features on users more quickly.

2. You can share code with other modules that have the same dependencies, often reducing the overall size of your system. This is important when there's a cost (eg. download size) to your total bundle.

3. You have less code for your engineers to read & maintain.

4. You can easily track licensing & contact information for your dependencies.

5. You automatically get any new features released by your upstream dependencies.

6. You automatically get security updates and performance enhancements released by your upstream dependencies.

The last is nothing to scoff at: imagine if the headline, instead of 'left-pad breaks the Internet!', had been a security vulnerability in left-pad which literally broke the Internet. Imagine how hard that would be to fix if everyone had copy/pasted the code or re-implemented it. This is not an academic scenario either: remember "Nearly all binary searches and mergesorts are broken", published by the guy who wrote the broken binary search implementation in the Java standard libraries?

http://googleresearch.blogspot.com/2006/06/extra-extra-read-...

Always copying your dependencies into your source tree is not the answer to this, no more than always relying on npm modules was the answer to updating your dependencies. They both have pluses and minuses, and if you really want to be a good programmer, you need to weigh both of them. For my projects, I tend to use whatever libraries I need when building them out (via npm, if possible), and then periodically audit the dependencies to make sure I'm still using them and they wouldn't be better off incorporated directly into the project. I wish more products did this, but I don't control what other programmers do.


7. You automatically get malwares and backdoors released by your upstream dependencies.


Without addressing the wisdom or lack thereof of including dependencies for small functions, perhaps the problem of disappearing/changing small dependencies could be solved with an option along the lines of

    npm install <small-dependency> <destination> --save-inline
Which would just copy the dependency verbatim to <destination>. Maybe have a "<dependency> is 100kb. Are you sure you wish to copy the entire source to <destination> instead of using a 'require' reference? y/n" prompt for the inevitable silly types who'd do it with Angular.


Dependencies are one side of the problem. Unavailability of binaries and dogmatic use of dynamic linkage are the other side.

When I installed a simple lines-of-code counting tool through Macports the other day I accidentally opened the door to dependency hell as gigabytes of not even remotely related stuff started to build [1].

Without a doubt something is going very wrong with Free Software and package managers. On the other hand, never look a gift horse in the mouth so I may not even be the right guy to complain here.

[1] http://pastebin.com/cAZgbaFN


This is from a client-side web dev perspective:

I'm hoping that proper support for ES6 modules to enable tree-shaking bundle builds (in the short term), and HTTP2 with client support for modules (in the long term), will allow us to head towards a world where we converge around a handful of large utility libraries.

In theory, tiny dependencies was supposed to allow us to only include code we actually needed in our bundled code. Bu the reality is that everyone uses different tiny dependencies for solving the same problem. So you end up with enormous bundles made up of different solutions to the same tiny problems.


It's not that we have forgotten how to program. It's that everybody and their dog is now a programmer.

Professional racers don't need an automatic transmission, but it's quite helpful for an unprofessional driver.


What would happen to all those NPM projects if GitHub is destroyed? I don't think it will close anytime soon, but lets say, a meteor shower hits GitHub data center or something along the lines of this.


I still find it hilarious that, upon the bulk of SCM use shifting to a DVCS, the first[1] thing people did is run out to build a central hub.

[1] For the pedantic, it probably was not literally the first thing; I'm sure some folks went to the rest room, others ate something, still others took a nap.


The thing with DCVS is not that central hub does not exist. It is that you do not need specific central hub. That means if github dies, I can take any of my git repos I checked from github and continue working with them, and create a local git server that would allow others to work with it. Of course, there is a convenience value in having central hub. The point is not denying that, the point is turning it from necessity to option.


That's because central hubs are very useful...


Well npm doesn't use GitHub for hosting of modules so honestly npm would be fine. Bower on the other hand would be completely screwed.


It is the nature of git that most repos on GitHub also exist in many other places.


Smart people created software that lowered the barriers of entry to making software. It was inevitable that not-so-smart people would eventually be writing software that others would build upon.


The author brings up an excellent point, but I disagree with the solution. We should of course reuse existing, well-tested code if it available, even for simple things like left-padding. The real issue here is that there is a module for left-pad alone. If it were something like StringUtils module with a bunch of commonly used string functionality, it would have been great.

What is it about the node community that triggered this misunderstanding of package management and code reuse?


Hell, the java programmers I work with seem to never use each others simple functions and instead recreate the will every single time.

As for the issue with such a short piece of code being reused by many, why score on number of lines? If it works and is a useful function is more important to me. I am not familiar with bundling within the usage the article covers but we tend to bundle like functions together and the compiler drops unused ones


I guess this is the way one is supposed to use node, preventing one to write non-DRY code. (Golang takes the exact opposite approach, having it's own drawbacks of course.) However, when using React, I kind of trust that the maintainers don't include packages than require 12 star projects, and if, that they fork this stuff themselves.

BTW, isn't that a Facebook project, so aren't they supposed to use a CI? ;P


I couldn't agree more. I've been using the new ES6 style module syntax for a few days now because a co-worker forced me to, so he would use my library.

I'm not convinced its worth it compared to the simplicity of commonjs and module.exports. You have to pull in babel, which has over 40k files to do all this.

Why are people destroying the beautiful simplicity that is javascript? Can those people please go back to java?


Regardless of whether micro-modules are good or bad, I think that if you are the owner/manager of a project, you should be able, given its full flat list of dependencies, to explain why each one of them is useful for you.

Every project I've seen that uses npm always required 100s or 1000s of dependencies.

If building or running your project requires something and you can't explain why, I think there's a problem.


On the one hand, I ,love how the JS community is constantly coming up with new [versions of old] things. Even though most of it is creative waste, it's still creative and out of that churn we get some pretty awesome apps and tools.

On the other hand, there's a lot of bad practices disguised as "simple" and "efficient." Using NPM for one line functions is a great example of this.


Of course I would never create a dependency on a small module like left-pad. I would simply copy the function wholesale into the body of my code!


So funny, just few weeks back I had an argument with somebody about writing a simple functions vs. importing libs when you need less then 5% of the functionality. I am more convinced than ever that it is better off to have the least amount of external dependencies. Of course I would not want to rewrite a 2M+ LOC library with very complex code, but left pad is not one of those use cases.


I'd just like to point out that React does not have a dependency on left-pad. React has no dependencies at all[0]. devDependencies have no impact on third party consumers.

[0] https://github.com/facebook/react/blob/master/package.json


The author missed the point about modularity in Javascript.

Small packages done right, well tested = maintenable, reusable, stable code.

The problem does NOT comes from packages. The problem comes from un-publishing public packages and centralized repository server.

I have a java project with a lot of dependencies. Does it mean it's bad ? No, but if maven repos are closing tomorrow, my project will not build as well.


Why was it removed anyway? I agree that the ability to unpublish something is the real problem, but I wonder why the author actually unpublished it. I wonder if the author knew about all the projects that depend(ed) on it. Maybe he/she actually did it as an evil experiment, though a very interesting and eye-opening experiment. Does anyone know?



I'm content and happy not knowing what the fuck this is all about. Pad left? Are you kidding me? If your tower og babel fell because of your reliance on some script kiddie's toy project, I am happy and content knowing you get what you deserve. Law of leaky abstractions, motherfucker. Spolsky...do you read it?


Maybe some people just want to claim they have a published module. Making them feel some sort of achievement or glory.


Not just making them feel... I think this may be targeted towards recreuiters, who may be impressed by the sheer amount of npm modules someone has published.



Well it has always been like this. In early 2000s most developers I knew would simply copy-paste code from all over the Internet.

Nowadays you use NPM search instead of Google search

The fact is that lazy programmers are lazy. The methods change but the principle remains the same. In 90s people typed in code from magazines and books.


If the code is open source, what difference does it make if the code is in my module or someone else's?


I just removed the tilde and caret of all my dependencies (in my package.json) and maybe that's the way to go. Seal a local version of your packages and don't update unless is completely needed. But I'm still worried about the fragility of the package environment.


For more fun, my commentary on leftpad code: http://www.win-vector.com/blog/2016/03/more-on-npm-leftpad/


The core issue here is that there is no included standard package in Javascript. It happened because Javascript did not have authoritative implementation when it first began. Next ECMA should require to pack some batteries in to avoid this micro-module hell.


It seems to me that the JavaScript VMs should get together and start including standard library. That would also give the benefit that those would be highly optimized. They can keep it small at first and focus on number and string manipulation.


Packaging is the new programming.


Clearly this is insane - but what the solution?

Part of the problem, in my opinion, is that packages are not subject to any review process. These dependency chains get out of hand because they are rarely top-of-mind.


By the way, ES6 syntax (works in modern foxes and chromes):

leftpad = (str, len, pd = ' ') => Array(len > str.length ? 1+len-str.length : 0).join(pd) + str

WTF are you talking about? Making this into a module?!


Here's a quick rule of thumb. If it's the kind of function you would ask a candidate to write at the start of a job interview, you shouldn't be importing a separate module to do it.


Should you rewrite it for every project you need it in, or copy and paste from the last one?

Should tests also be rewritten or copied each time you do this?

What do you do when you've used it in multiple projects and you find a bug or performance issue?


.... isn't this a problem with node, and not developers? Wouldn't you say this is a symptom of a systemic problem of the framework that it is lacking common features that everyone needs?


That's what you get when you let JavaScript into the server side.


  function foo(arr) {
    var str = "";
    var leftpad = require("leftpad");
    for(var i=0; i<arr.length; i++) str += leftpad(arr[i]);
    return str;
  }


What's wrong with reusing small fragments of code?

The usual complains about many dependencies are mostly void (it's not bloat if you only depend on single functions you actually use).


When am I developing some large project for a long time, sometimes I find, that I have reimplemented the same function at several places (few years between implementations).


"Small modules are easy to reason about"

No... "appropriately sized modules are easy to reason about"

In this case... "Appropriate" has gone out of the window!


Everyone keeps mentioning the lack of a standard library for JavaScript as an excuse for this shit show. IMO this is just a futile attempt to mask incompetence.


This.

Any modern standrards-compliant implementation of JS (following ES5 and especially ES6/ES2015 standard) already has everything that a sane programmer would ever need.


What actually happens when you try to update a required package, but it is gone from upstream? Is there no way to keep the existing package you already have?


I find it interesting that blaming code reuse is a valid thing, but blaming a lack of test coverage and CI build testing is not.

The problem is the lack of a test culture.


Check out the work on Morte for a more reasoned approach for how to take micro-modularization to it's natural (or insane) conclusion.


So sad yet so true.

We haven't. React developers probably have.


It's up to you how to go about it on server side you for example can go with express or hapi (minimal external dependencies).


There's nuance to the discussion that both sides are missing. People argue forcefully whether these small modules are good or bad, but I'm not seeing much evidence that they understand the other side.

First: why small modules are bad. Lots of dependencies complicate your build, and you end up with the dreaded diamond dependency issue. Failures in a dependency become more likely to affect you. It gets you in the habit of using prebuilt modules even if maybe it's not quite what you need and it would have been better to write yourself. With `npm` specifically, we've seen how its mutability can break the build, though that's about `npm` and not the idea necessarily.

I think most software developers' gut responses are that something is wrong and crazy in the npm ecosystem.

That said, there are benefits that this blog post and others aren't mentioning, related to the javascript situation specifically.

The first one is that javascript is a surprisingly difficult language to get right. Sure, the final solution is only a few lines, but which lines are hard. You have to navigate the mindfield that are is V8 in nodejs, v8 in chrome, spidermonkey, chakra, etc. I've had code work in Chrome before but blow up in IE, and it's really hard to track down and test.

The comments in the blog post are illustrative:

One line of code package:

    return toString.call(arr) == '[object Array]';
Crazy right? And my first stab probably wouldn't have been to implement it that way. Why not:

    (testvar.constructor === Array)
that a commenter suggested, which should be faster? Well another commenter said:

    The constructor comparison will fail if the array comes from a different context (window).
I've run into issues before with cross-browser compatibility stuff, and it's frustrating and hard to test. If there's some de facto standard package that implements it for you, hopefully the community can iron out edge cases.

The other thing that people don't bring up, is that there's not much JS standard library, and in the browser context you have to send all your code to the front end.

So maybe you write these 11 lines yourself, and then another package writes these 11 lines, and another... it adds up. But if everyone uses the same package, the code only gets sent once and they all share it.

Lastly, people talk about how `sin` should be a part of a "trigonometry" package and not by itself. Well, again you're faced with sending a bunch of unnecessary code to the frontend. With webpack2 and tree shaking, or e.g. Google's Closure compiler, it can strip out dead code and so this issue will go away in the future, but we're not quite there yet. So package authors still bundle all these things separately.

So pros and cons.


It suddenly feels like the 90s all over again.


You mean like the sad story of PKzip?


May you share the story ?


https://en.wikipedia.org/wiki/Phil_Katz because I took your comment to maybe be about Kik's legal bullying and PKZIP came to mind as a sad story of the 90s.


Surely there is a need to standardise on a set of well-maintained "batteries included" packages.


That is what PHP has, and it received countless attacks on that approach during the years; although most of those attacks were due to the inconsistent function naming.


npm had the solution one year ago (namespacing) https://docs.npmjs.com/getting-started/scoped-packages if only developer can embrace the "change"


I'm generally not a big fan of IPFS, but IPFS seems like the perfect solution to this problem.


when you go out for food or order in, do you ask yourself "have I forgotten how to cook?"

Sure in some ways NPM has packages that don't deserve the title of a package, but isn't the convenience of not having to reinvent every code worth it?


Isn't the problem that there is a lack of a good standard library in Javascript?


This is why one guy can now compete with say Google or Microsoft, because that guy uses code written and managed by more engineers then both Google and Microsoft have combined. Instead of paying hundreds of dollars to said companies, you can just NPM install "what you need".


Libraries/modules should help to solve complex problems where writing your own version is not optimal. Single line functions as modules, on the other hand, won't help you compete with Google or Microsoft and result in more problems than they solve. If a simple padding or "is this int positive?" is a complex problem, well, there are different professions than programmer's..


The less code you write, the less code you have to maintain. Even if it's just one less thing, it's a win.

The comonJS module pattern also helps tremendously, as your requires are block/function scoped. It makes it trivial to refactor (remove/rewrite) code.


In a perfect world yes, but in the real world, with unreliable dependencies and their imperfect management, it doesn't come for free.


IMHO, this is all due to lack of a standard library in the Javascript world.


Seems that we need a tool to crawl all the repos that a developer owns and report lines of code in each package that they wrote. If there are lots of small packages, then this is not the kind of person you want to hire, except maybe to do PR.

Real developers do not do stuff like this.


> Even if correct, is it the most optimal solution possible?

"most optimal"?


JavaScript was a mistake.


I don't have an issue with modules or the Unix philosophy, I have an issue with using NPM for all these tiny modules. Hint: you can make your own modules, store them within the project, and add them to your package.json to be required anywhere.


It's not about programming, it's about fast progression.


It does seem pretty insane, but how many of these are polyfills?


Forgotten? Most people who develop on Node.js never knew...


Re-usability is a good concept but can be overused easily.


This is acceptable because web developers expect a certain level of coddling. Many developers are quick to defend this insanity because they simply cannot see their own level of entitlement.


What's next? A hello-world package and Node tutorial about hello world program being include this package as dependency and call the exported function.


All the comments here have answered the question quite well. Yes we have forgotten how to program and we want to argue about it.


I don't see nobody criticizing 'std::min()'. Perhaps what we really need is a 'std' for js?


`std::min` isn't a library of its own, it's a function in the `<algorithms>` header of the Standard Template Library. The STL also encompasses a wide range of other things. There are some efforts to provide "the missing standard library" for JS, e.g. lodash.


Then the problem isn't that people don't know how to program, rather that js is missing standard functions. In the C++ world there is a large push to avoid rolling your own things, indeed even simple constructs like `for` loops are starting to be frowned upon.


No saner alternative presented.


What I don't get about Left-Pad, shouldn't they have used Arrays.join for better perormance?


Array performance isn't always better than string concatenation.

In this test case it comes out slower: https://jsperf.com/left-pad-arrays/3

But ultimately it doesn't matter in the least for 99.9% of use cases:

https://blog.codinghorror.com/the-sad-tragedy-of-micro-optim...


Interesting - well I thought if it is being used by thousands of libraries it might be worth optimizing.

I just tried in NodeJS, you could also just instantiate an Array of a given length and join it with spaces, like this.

var x = new Array(5);x[4] = "abc";x.join(" ");

No idea if it could be faster - but the test you linked to pushes spaces into the Array.


here is one of the comments to original post:

"Immutability at the centralized authority level and more decentralization of package distribution is the solution, not 'write more functions yourself'."

what the fuck does that mean?? they just don't give up, do they... Fucking retards.


I remember how I have been downvoted to oblivion for comparing JavaScript madness with Java EE "packer's" paradise years ago.

The Programmer's Stone first essay is actual as it never been before.

Actually, it is a common pattern. When some activity becomes popular due to very low barrier to enter it will end up in a such kind of mess. It seems like nowadays everyone is either a programmer or a scientist and researcher.

This is the quality of their software and research.

There has been a reason why good schools taught principles (algorithms and data structures) not particulars (objects and classes). But since MIT and Berkeley dropped Scheme-based courses in favor of "pragmatic" Python (thank god not JavaScript) based courses we are heading to a disaster. Java madness taught us nothing.

History is full of examples where assault by mediocrity ruined the whole branches of philosophy, arts and crafts. Instead we have fastfood, mass media, social media and now this mass coding, which combines worst from mass and social.

Just try to compare things like Smalltalk or Plan9 or R4RS Scheme or Zeta LISP of Symbolics or with this stuff.


This is, finally, the "Fractal Of Bad Design" moment for JavaScript.


LOOL..I agree with this one.


What's with these kids and their "calculators". Back in my day we used a slide rule and we liked it!

But seriously this is stupid. Programming shouldn't be the goal. Just because you can write a function doesn't mean you should. Every line of code you write is overhead that must be tested and maintained. I guarantee that if the author chose to hand roll code instead of using packages he'd have a lot more bugs. But he wouldn't know that until he hit some mundane edge case scenario in production.


"Every line of code you write is overhead that must be tested and maintained."

The problem is, that modules doesn't solve problems magically. Every dependency should be tested, maintained and kept online as well, by somebody else, whom you have no control of. In turn, you have a bunch of black boxes that can blow up your application anytime. Of course, another extreme of write everything yourself is also bad, so it's a trade-off that must be considered carefully. I've build many systems during my career and one of the biggest nightmare I encounter is managing dependencies.


> What concerns me here is that so many packages took on a dependency for a simple left padding string function, rather than taking 2 minutes to write such a basic function themselves.

Wait -- code reuse is bad now??


YES... it is... its a trade off... code reuse has a upside and a downside...


  module.exports = leftpad;
   function leftpad (str, len, ch) {
     str = String(str);
     var i = -1;
     if (!ch && ch !== 0) ch = ' ';
     len = len - str.length;
     while (++i < len) {
      str = ch + str;
     }
   return str;
  }

Isn't that the least efficient way to do that function? Prepending a string has always been very expensive operation.

Calculating needed length. Using repeat and just concatenating 2 strings would be faster.


V8 internally uses cons-style strings for these operations, and only flattens the string when necessary for other operations.


> if you cannot write a left-pad, is-positive-integer, or isArray function in 5 minutes flat (including the time you spend Googling), then you don’t actually know how to code.

I'd probably get 2 of those wrong in some weird way, but I blame javascript. I mean without Google of course.


I see two kinds of programmers here.

Good programmers understand the risks of making your system depend on something you don't have control really well; They know how keeping system complexity low is like an good investment which makes your life later easier (low maintaining costs).

Bad programmers stacks up technical debts such as including unnecessary dependencies until the system no longer works.


> Good programmers understand the risks of making your system depend on something you don't have control really well

For example, the quality of your own code.


Technical debt doesn't stop being technical debt just because it's your code.


[flagged]


This comment as well as https://news.ycombinator.com/item?id=11354704 break the HN guidelines. Please don't post uncivil comments regardless of how wrong or ignorant someone seems.

We detached this subthread from https://news.ycombinator.com/item?id=11351657 and marked it off-topic.


If you're new to technical discussions, people normally provide supporting arguments. Both of those handle sequences of tasks that need to complete in order. "No you don't understand" without any further info is not a response.


I think if you don't even know the basics of something, you should go learn them before discussing it.


Ahh job security :D


Ahhh job security :D


This post is too dismissive and confuses some topics.

"Packages", "modules", "functions", and other words can mean different things within different contexts. Well-known and tested functions are useful. Putting them in a "package" is just a consequence of how node/npm work. There should certainly be a much, much better implementation of code sharing, but sharing and using well-known, singular functions should be exactly what we are going for.


npm install goldmansachs


Functions Are Not Packages

Cracked me up :D


I don't think it is bad at all. For a lot of project where saving even a minute matters.


This is just more evidence that the unit of code is a function, not a module.

Eventually, given a pure enough language, every "package" could contain only a single function, and every function in a project could be published as an independently reusable unit.


Only if your modules are little more than textual inclusion and perhaps a mechanism to deal with name clashes. ML style modules are way more substantial than that, they can be used as units of abstraction.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: