← Back to context

Comment by runin2k1

11 years ago

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.

      62 replies →

    • > 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.

      8 replies →

    • > 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.

      1 reply →

    • 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...

  •     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.

      13 replies →

  • >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'.

      2 replies →

    • 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...

      1 reply →

    • 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.

      1 reply →

    • 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.

      1 reply →

    • 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.

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

  • 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.

      9 replies →

    • 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...

      6 replies →

    • 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?

      2 replies →

  • > 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.

      13 replies →

    • > 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?

      2 replies →

  • 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.

      6 replies →

  • 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.

      6 replies →

  • 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.

  • 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.

      3 replies →

  • > 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.

      1 reply →

  • 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.

  • 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.

  • 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.

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.

      1 reply →

  • 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"?

      2 replies →

  • 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.

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).

  • 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.

  • 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.

      10 replies →

    • 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?

      14 replies →

    • 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.

      2 replies →

  • 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.

  • 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.

      3 replies →

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.

      1 reply →

"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.

  • 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?

      1 reply →

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.

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.)