The DRY culture taken to the logical extreme ends up exactly here.
Perhaps someone influential can create an acceptable definition for when it is OK to "repeat yourself". The node.js package dependency culture is toxic.
I see it as a combination of that and no incentive to review the code you require. We live in a “I need it to just work because everyone wants the dream of tech seemlessly working” age. This will probably become more and more common as the market opens up and the easier jobs (work?) become more and more blue collar. JS lends itself well to entry level devs that just want to write code and not understand needs or how an application works and why code is needed...and the exacerbation continues.
It's very easy to flippantly go "why would you ever use that???", but I'm honestly wondering what is the actual justification for the use of such a package. I see it does some initial checking (which will take more lines in your own code to deal with), but I can't figure out why someone would actually use that.
is-odd's behavior is also extremely odd. It depends on "is-number" (why would this need a dependency to begin with?), which checks in the most bizarre way imaginable if the input number or string(??) is finite. is-number, and by extension is-odd, also doesn't support BigInts.
These projects seem to have been created and promoted just to boost the author's "npm score" / package count. These micro-modules must cause more problems than they solve.
To be fair, it is actually far from trivial to determine whether a value is a numeric value in JavaScript, and most all-in-one utility libraries (jQuery, Lodash, etc.) java something of similar functionality. If there is one function that needs its own NPM entry, this probably is.
This one year old reddit discussion [1] seems to indicate that the package was used in another, pretty popular and more complex, package by the same author. The dependency doesn't look current anymore but it might be downloads of previous versions.
As for justification and usage I couldn't tell you, apparently it was only used once in that whole codebase anyway (also according to that thread).
Even worse: it is only used one time in the entire code-base, and that one time was to check whether or not a value returned by the string length function was odd.
What.... why.... the only concievable point of a package like this is that at least it does the kind of type-checking needed to compensate for JavaScripts weak typing system, but why do you need to check the type of String.length?! Do you really think it's going to start spitting anything other than a positive integer? Fucking hell....
My problem with these kinds of libraries is it’s not at all obvious how it behaves in edge cases, and they’re so simple I’d rather just implement it myself. Maybe I want it to throw an error, maybe I just want it to return false. Even the documentation doesn’t tell you what happens for non-integer values, you have to look at the source, at which point you may as well copy it into your own source tree.
This kinda of thing (see: the "left-justify" fiasco) seems unique to NPM. Why is that? Python packages are very easy to import as well, but you don't see this kind of madness in most python programs.
I’ve been using Python for 20 years and have never used either of those libraries. People _do_ use Python for more than accessing web services and processing HTML, you know.
Besides, the stdlib, and Python packaging in general, is unquestionably light years ahead of NPM.
Haha "twine". [0] Seems like a real mainstream tool there. Of course I know from previous python debacles that while npm just uploads the directory structure that already works in development, with python one has several obscure and changeable package preparation steps to perform first.
You're using some colorful language ITT. "unquestionably light years ahead", "steaming pile of shit", etc. This sort of idiom is good for convincing children. Whom are you trying to convince?
personally i always pip install attrs numpy pendulum, but my criticism is more or less the same.
The stdlib is of variable quality. I'm happy it's there, but there are absolutely crufty old dragons in there these days, and it updates very slowly compared to pypi.
I found Python's standard library to be generally excellent. Not perfect, by any means, and certainly not "all-encompassing", but that's an impossible standard. But it covers the areas you'd want a standard library to cover, and more besides.
I'm not that bothered with nodejs (many are careless with packages imported in a production version, but that's quite easily managed) but in all fairness, in node you will always include request, and some xml/html parser.
Node works fine when multiple versions of the same package are imported. Python doesn't, and very few other interpreters do either. Multiple version imports become increasingly likely with increased dependencies, so python has a natural brake on dependency numbers. That's why you don't see popular python packages that themselves have lots of dependencies. Even the most popular Node packages can go wild with dependencies, and their dependencies can do the same in turn.
Node has this phenomenon that some people consider harmful because it is more capable in this particular sense.
I wouldn't use it but I can see why this might happen.
The correct solution would be process.platform === "win32" but if you aren't familiar with Windows then you might not realise this includes 64 bit Windows or Windows Server.
If process.isWindows() existed in the standard library nobody would be concerned about it.
I'd argue that having the function process.isWindows() would be a bad design. You'd then need to add process.isMacOS(), process.isLinux(), etc. You can do a switch statement on process.platform.
Ok, but lets say you're doing something that requires an isWindows() function. You search NPM, you find this package. At this point, do you not even look at the code? Like, even a glance would make you aware that all it's doing is comparing with "win32", which you can trivially do yourself. So why use the package at all, then? All it adds is overhead.
No, clearly the people who use this package has not taken even a second to review the code, because it if they had, they'd know that it was trivial. Security-wise, that's deeply troubling.
Even the guys on our Node team joke that the node modules folder is abhorrently large. More massive than a black hole is the most common meme.
I think there's two things going on here. One, Javascript has taken Don't Repeat Yourself to an absolutely insane extreme. Python claims "batteries included" and Java boasts "there's a library for that", but it's at a higher level than stuff like is-windows.
Most libraries are large and include a lot of functionality, and something like is-windows would be found inside a larger dependency like system-utils. Similarly, a Java dev would import Guava, not immutable-list and and multiset and multilist.
Second, I think many Javascript devs see themselves more as plumbers than developers. The goal seems to be to stitch a bunch of dependencies together to achieve the desired outcome, rather than developing the desired outcome.
I've seen plenty of people rip FileUtils.writeStringToFile out into a standalone utility class, because they didn't need all of commons-io as a dependency. In order to do that, you need to know how writeStringToFile works, and be able to fix whatever breaks when you separate it from its ecosystem.
Devs that pull in is-windows don't have that kind of ... curiosity, I guess is the right word. There's no desire to see how is-windows works, or to see if they can improve it. It does what they need, so they include it as a dependency and move on, end of story. The overhead, and more importantly the security issues, involved just aren't a factor.
I do wonder if the short lifespan of many UI projects is a factor, too. If you're just going to rewrite the UI in six months, there's no reason to make it efficient or maintainable.
I hate NPM with a passion but even I concede you could argue that this kind of extreme "plumbing" work is an experiment in world-wide collaboration. I mean technically you probably could make a fix in one package and everyone who uses the package benefits from it even without knowing they use the package. While I want to know how stuff works for my personal satisfaction, that's not what business cares about.
One time webpack did a breaking change (version 4) right as my team was switching from make + grunt to webpack. It was a total shitshow. Some packages were upgrading, some weren't, some did it wrong and didn't actually work on webpack4. We could have stayed on 3 but then we'd have to figure out which version wasn't broken...
> Even the guys on our Node team joke that the node modules folder is abhorrently large. More massive than a black hole is the most common meme.
This "argument" gets brought up every time there's an article about NPM. The STD library for node is relatively small so a lot of that work is outsourced to third-party libraries. The node binary is 17mb for mac os x, the node modules for a project end up 100-800mb maybe? So let's say binary + library size reaches 1GB. Now go and download visual studio to work on a C# project and wait for 15GB to be downloaded. I'm not arguing that relying on a third-party ecosystem is great as it does have legitimate security issues, but filesystem size is not the problem here.
Why are you comparing the size of application dependencies to the size of an IDE that includes several compiler toolchains and tons of other build-only stuff?
As a bit of an accidental programmer (HTML, CSS, TeX, along with enough Node and PHP to dislike them both) can someone provide a concise distinction on the difference between the idea that something is "in the standard library" vs "part of the language?"
For example, in CSS, I don't need to import a module with the syntax for Grid, it's just...there, no extra typing required.
"Part of the language" means that the language's syntax directly allows you to do a thing, while "in the standard library" means that the functionality is included with all (or at least most) versions of the language, but the language syntax doesn't provide the feature directly.
For example, take dictionaries (or maps). It lets you use a key (like "bob") to look up a value (like "555-555-1234" or "bob@spam.email").
Dictionaries are built into the language in Python, so to do the above, all you'd have to type is:
phones = { "bob": "555-555-1234" }
In Java, maps are part of the standard library. They're there, but you have to import them:
import java.util.*
Map<String, String> phones = new HashMap<>();
phones.put("bob", "555-555-1234");
The main difference is that features in the standard library can be written in the language itself. Java's Map classes are written in Java, while Python's dictionary utilities are written in C.
Other than that, the difference is largely semantics.
I think it's a question of maintenance. You can check if process.platform is "win32" or "win64", but what happens when they release a 65-bit architecture and all your code breaks?
This is generally my line of thinking - theyre going to be responsible for keeping the package up to date and tested, I dont have to. But this is a good illustration of the extremes of that I think - if they put out a new architecture and it needed to be updated, that would be a major semver bump right? So my code isnt going to be automatically updated and chances are I wouldn't want it to be. And the risk here is huge at the moment - itd be super easy for this package to be compromised and switched out to do something nefarious.
I think there are still going to be things more substantial where the upkeep and testing might be higher where the balance would start to tip back to outweigh the risks maybe, but I think personally I haven't been giving the risks enough consideration and I think thats where NPM needs to focus more of its attention.
I will often use things like this, not because I don't know how it's done, but because I have a hope that whoever made it will maintain it and keep it working properly under future changing conditions. Today, it's easy to see if the platform is windows. Later, that may change, and if this code is maintained it will save me headaches. If lots of other people are also using it, there's a very good chance that it will be updated quickly and that weird corner cases I'm not aware of will be already taken care of.
Yea, I agree completely. While I tend to reinvent plenty of wheels, I don't find the idea of importing a package designed for one small task, bad. (edit: missing word)
_Especially_ if it's used by a ton of repos. If, as your example says, something changes where `is-windows` needs to be updated it's likely to either be updated or break so many codebases that someone will update it.
There's a bit of chaotic safety in relying on a web of dependency trust like this. On one hand more people invested in the behavior of a simple package gives you more confidence. On the other hand, it means more developers are depending on more packages, introducing possibly more brittle behavior.
The latter (brittle behavior/deps) has been my experience fwiw. While I don't dislike the idea of `is-windows`, I do dislike introducing more points of random failure. In general if I don't have the idea that my own implementation of something like `is-windows` is likely to need maintaining then I'm happy to do it myself and remove a dependency.
Coming from Rust mostly, but I wonder if the safety of the language aids this problem too. For example, I imagine `is-windows` level of dependencies is far less problematic in Rust than NodeJS.
This line is tested against and super optimized. By requiring this, you are future proof. The maintainer is active and writes tests. It's also a bit esoteric and hard to undestand.
`return process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE));`
Here's is what's probably going on in the head of the people who require it:
"What does this do exactly? Why do I need to test against 'win32'? Should I also test against 'win64' or 'x64'? What if it changes later on? Do I need to keep track of Windows versions and then change all my apps? What if I write it without testing for 'cygwin' and someone else in the project do? Will the app only work partially on some hardware? What if some new OS comes out?"
Don't get me wrong, it's not perfect and is a security hole. But that's the reason it's done.
You really think that line is "super-optimized"? It generates and executes a regex to check if a string has one of two different prefixes. I don't know a lot about JavaScript internals, but I would imagine that is both forces memory to be allocated (and garbage generated) as well as being significantly slower than a basic prefix check. If doing this is considered "super-optimized", I don't even know what this industry is any more.
Oh, you're right! I missed the dollar sign! That makes it even worse! Unless V8 converts the regex to that expression (which it could maybe do, but I doubt), there's no way the regex version could be anything like as fast. Your version has the added benefit of also being far more readable and totally without any kind of memory allocation.
Here, test it. If you test it multiple time you get slightly
different results. Both seem to be similar. You would have to ask the author why they went with the regex solution to know why they chose that one. Knowing Javascript, there's probably a pitfall we are not thinking about.
Super optimized as in the sense that this line was distilled to be as small and lightweight as you can get it while making sure it works with actual tests.
Just search for "Javascript check if variable is window" to find all kinds of solutions that seem to work at first glance but would probably end up breaking under some specific situations. Some people tests against the .toString of the variable, others look to see if the variable has under it what 'window' usually contains while some create a new instance of 'window' and compare it to their variable, etc.
The solution that "is-window" brings seems to be as perfect as you are going to get it. In an ecosystem where things change quickly and you are not sure where your code is running it's easy to become dependent to such crutches. You do not want the mental burden of remembering all the hacks.
I am not defending this particular package and I'm sure we could come with a better solution of testing it. If you do, make a pull request. I am merely explaining why those package happen.
I think it's funny how so many people have an allergic reaction to using a package manager to add fragments of small but non-obvious code to their codebase, but have no issue with frequently using Google to track down one-liners on Stack Overflow, which they then copy-and-paste into their codebase.
Why is you acting like a slow, biological package manager for small bits of code better than using a real package manager for the same purpose?
1. The code you pull in will not get updated unless you do it explicitly.
2. The code you pull in will not get exploited when someone masquerades as the package owner. (How many widely publicized cases of this have there been by now, 5? 10?)
3. Presumably you read the code enough while copy-pasting to be sure it isn't mining Bitcoin or something.
The line of code in that moment is read and "vetted". Same can be done for the dependency yes, but what happens if malicious code is injected later and you update your packages?
Or like the leftpad disaster where the package was deleted?
There is many reasons why the copied one liner is better and many why the package manager is better.
Here's an idea. Why doesn't NPM or some big guy maintain a node "standard library" which pulls top 1000 most used small npm packages into a single one?
> When I write code, I want to write things semantically— I want to call an isWindows() function, not idiomatically compare a string process.platform (node only) to a string that could change in the future.
> If I have "semantic" functions like isWindows, I want it in a library, not one-off in my project. And if I have a library management system, it's great if the libraries are highly granular— IE, a standard library where each function is one library sounds great.
> …It's just that, instead of maintaining a standard library set containing many small granular sublibraries, the javascript community decided to make every single one-function library an attack/vuln injection surface OH WELL
I’m glad people publish packages like this, but feel like there needs to be a better way to consume them.
What if you could do `npm install-inline ./src/utils is-windows` and npm downloaded the module and inserted it inline in your utils directory, along with a comment referencing the original package, version number, and license information (if required). There could be an update-inline command as well.
This seems like a ok compromise between “install 376 micro libraries” and “copy code snippets off StackOverflow”.
83 comments
[ 2.0 ms ] story [ 167 ms ] threadPerhaps someone influential can create an acceptable definition for when it is OK to "repeat yourself". The node.js package dependency culture is toxic.
I have my doubts about that. I suspect it is more a question of what other packages they bring in... that for some reason use is-windows.
I think that is a difference and points to a different set of folks who for some reason, use this thing.
is-odd 831,460 weekly downloads
These projects seem to have been created and promoted just to boost the author's "npm score" / package count. These micro-modules must cause more problems than they solve.
As for justification and usage I couldn't tell you, apparently it was only used once in that whole codebase anyway (also according to that thread).
[1] https://www.reddit.com/r/programming/comments/886zji/why_has...
What.... why.... the only concievable point of a package like this is that at least it does the kind of type-checking needed to compensate for JavaScripts weak typing system, but why do you need to check the type of String.length?! Do you really think it's going to start spitting anything other than a positive integer? Fucking hell....
1) write actually useful package, get 10k weekly downloads
2) split out 100 utility functions and use them as dependencies
3) magic - your packages now have ~1M weekly downloads. You are now a JS superstar.
Besides, the stdlib, and Python packaging in general, is unquestionably light years ahead of NPM.
And yes the setup is more extensive than NPM, which is exactly why the PyPi ecosystem isn’t the steaming pile of shit that is NPM.
You're using some colorful language ITT. "unquestionably light years ahead", "steaming pile of shit", etc. This sort of idiom is good for convincing children. Whom are you trying to convince?
[0] https://www.python.org/search/?q=twine&submit=
It depends on what you want to do.
The stdlib is of variable quality. I'm happy it's there, but there are absolutely crufty old dragons in there these days, and it updates very slowly compared to pypi.
Node has this phenomenon that some people consider harmful because it is more capable in this particular sense.
Also it's a bit easier to create and publish on npm.
The correct solution would be process.platform === "win32" but if you aren't familiar with Windows then you might not realise this includes 64 bit Windows or Windows Server.
If process.isWindows() existed in the standard library nobody would be concerned about it.
No, clearly the people who use this package has not taken even a second to review the code, because it if they had, they'd know that it was trivial. Security-wise, that's deeply troubling.
I think there's two things going on here. One, Javascript has taken Don't Repeat Yourself to an absolutely insane extreme. Python claims "batteries included" and Java boasts "there's a library for that", but it's at a higher level than stuff like is-windows.
Most libraries are large and include a lot of functionality, and something like is-windows would be found inside a larger dependency like system-utils. Similarly, a Java dev would import Guava, not immutable-list and and multiset and multilist.
Second, I think many Javascript devs see themselves more as plumbers than developers. The goal seems to be to stitch a bunch of dependencies together to achieve the desired outcome, rather than developing the desired outcome.
I've seen plenty of people rip FileUtils.writeStringToFile out into a standalone utility class, because they didn't need all of commons-io as a dependency. In order to do that, you need to know how writeStringToFile works, and be able to fix whatever breaks when you separate it from its ecosystem.
Devs that pull in is-windows don't have that kind of ... curiosity, I guess is the right word. There's no desire to see how is-windows works, or to see if they can improve it. It does what they need, so they include it as a dependency and move on, end of story. The overhead, and more importantly the security issues, involved just aren't a factor.
I do wonder if the short lifespan of many UI projects is a factor, too. If you're just going to rewrite the UI in six months, there's no reason to make it efficient or maintainable.
Are devs really pulling in is-windows, or are they importing some other thing (that might be more legitimate... that actually uses is-windows)?
If it is the former, man I'm at a loss for words there.
If it is the latter... that would seem to put the onus on the folks writing those other packages.
One time webpack did a breaking change (version 4) right as my team was switching from make + grunt to webpack. It was a total shitshow. Some packages were upgrading, some weren't, some did it wrong and didn't actually work on webpack4. We could have stayed on 3 but then we'd have to figure out which version wasn't broken...
This "argument" gets brought up every time there's an article about NPM. The STD library for node is relatively small so a lot of that work is outsourced to third-party libraries. The node binary is 17mb for mac os x, the node modules for a project end up 100-800mb maybe? So let's say binary + library size reaches 1GB. Now go and download visual studio to work on a C# project and wait for 15GB to be downloaded. I'm not arguing that relying on a third-party ecosystem is great as it does have legitimate security issues, but filesystem size is not the problem here.
A better example would be the .net SDK which is a few hundred MB, but is far more featured.
The npm ecosystem is more comparable to nuget packages, but they are generally at a far more sane scope.
How is that even remotely comparable. Perhaps compare .NET Core SDK vs NodeJS next time.
For example, in CSS, I don't need to import a module with the syntax for Grid, it's just...there, no extra typing required.
For example, take dictionaries (or maps). It lets you use a key (like "bob") to look up a value (like "555-555-1234" or "bob@spam.email").
Dictionaries are built into the language in Python, so to do the above, all you'd have to type is:
In Java, maps are part of the standard library. They're there, but you have to import them: The main difference is that features in the standard library can be written in the language itself. Java's Map classes are written in Java, while Python's dictionary utilities are written in C.Other than that, the difference is largely semantics.
STD is an apt metaphor for something as promiscuous as node...
https://github.com/jonschlinkert/is-windows/blame/master/ind...
If you go read the code, you are doing more than most JS developers do when they pull in a massive sequoia of dependencies.
I think there are still going to be things more substantial where the upkeep and testing might be higher where the balance would start to tip back to outweigh the risks maybe, but I think personally I haven't been giving the risks enough consideration and I think thats where NPM needs to focus more of its attention.
_Especially_ if it's used by a ton of repos. If, as your example says, something changes where `is-windows` needs to be updated it's likely to either be updated or break so many codebases that someone will update it.
There's a bit of chaotic safety in relying on a web of dependency trust like this. On one hand more people invested in the behavior of a simple package gives you more confidence. On the other hand, it means more developers are depending on more packages, introducing possibly more brittle behavior.
The latter (brittle behavior/deps) has been my experience fwiw. While I don't dislike the idea of `is-windows`, I do dislike introducing more points of random failure. In general if I don't have the idea that my own implementation of something like `is-windows` is likely to need maintaining then I'm happy to do it myself and remove a dependency.
Coming from Rust mostly, but I wonder if the safety of the language aids this problem too. For example, I imagine `is-windows` level of dependencies is far less problematic in Rust than NodeJS.
`return process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE));`
Here's is what's probably going on in the head of the people who require it:
"What does this do exactly? Why do I need to test against 'win32'? Should I also test against 'win64' or 'x64'? What if it changes later on? Do I need to keep track of Windows versions and then change all my apps? What if I write it without testing for 'cygwin' and someone else in the project do? Will the app only work partially on some hardware? What if some new OS comes out?"
Don't get me wrong, it's not perfect and is a security hole. But that's the reason it's done.
* need to figure out if platform is windows
* stack overflow search (via google) unfruitful
* npm search - popular lib for this!
* it's got a lot of stars!
* yarn add is-windows
Extraordinary package, this.
http://jsben.ch/uFK8t
Just search for "Javascript check if variable is window" to find all kinds of solutions that seem to work at first glance but would probably end up breaking under some specific situations. Some people tests against the .toString of the variable, others look to see if the variable has under it what 'window' usually contains while some create a new instance of 'window' and compare it to their variable, etc.
The solution that "is-window" brings seems to be as perfect as you are going to get it. In an ecosystem where things change quickly and you are not sure where your code is running it's easy to become dependent to such crutches. You do not want the mental burden of remembering all the hacks.
I am not defending this particular package and I'm sure we could come with a better solution of testing it. If you do, make a pull request. I am merely explaining why those package happen.
Why is you acting like a slow, biological package manager for small bits of code better than using a real package manager for the same purpose?
2. The code you pull in will not get exploited when someone masquerades as the package owner. (How many widely publicized cases of this have there been by now, 5? 10?)
3. Presumably you read the code enough while copy-pasting to be sure it isn't mining Bitcoin or something.
There is many reasons why the copied one liner is better and many why the package manager is better.
How about fundamentally inane things like giving a cutesy obfuscated name to swallowing exceptions?
https://github.com/electerious/nice-try/blob/master/src/inde...
https://www.npmjs.com/package/nice-try
Used by 1.1 million other packages apparently.
> When I write code, I want to write things semantically— I want to call an isWindows() function, not idiomatically compare a string process.platform (node only) to a string that could change in the future.
> If I have "semantic" functions like isWindows, I want it in a library, not one-off in my project. And if I have a library management system, it's great if the libraries are highly granular— IE, a standard library where each function is one library sounds great.
> …It's just that, instead of maintaining a standard library set containing many small granular sublibraries, the javascript community decided to make every single one-function library an attack/vuln injection surface OH WELL
[1] https://twitter.com/mcclure111/status/1140007877547106305
Suggestions: series of wrappers for Array renamed as possible applications of Array for those who don’t know what an Array is.
https://www.davidhaney.io/npm-left-pad-have-we-forgotten-how...
What if you could do `npm install-inline ./src/utils is-windows` and npm downloaded the module and inserted it inline in your utils directory, along with a comment referencing the original package, version number, and license information (if required). There could be an update-inline command as well.
This seems like a ok compromise between “install 376 micro libraries” and “copy code snippets off StackOverflow”.