← Back to context

Comment by NathanKP

11 years ago

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.

      1 reply →

  • 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". :)

      5 replies →

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

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.

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