Have we all forgotten how to program?

This is the editorial from the latest JavaScript newsletter. You can subscribe here.

In the wake of left-pad-gate, which saw a developer named Azer Koçulu unpublish 250 of his modules from npm, we saw a number of sensationalist headlines proclaiming that 11 lines of JavaScript were responsible for breaking the internet.

What they were referring to, is that one of the modules that Azer yanked (left-pad) was only 11 lines in length, yet was depended upon by a lot of projects (including big names such as Babel and Facebook). Removing this module meant that npm install started failing for any project using it as a dependency.

Azer’s actions raised a number of questions from “Was it really a trademark infringement?” to “Should it be possible to un-publish a module that another project depends on?” — many of which have since been answered as part of the official post-mortem.

Yet this episode also raised a trickier question and one which has since divided opinion. Namely, is it ok to declare a dependency on such a small module, or should we be coding this kind basic functionality ourselves?

Those in favor of writing such functionality from scratch, argue that increasing the number of third-party dependencies in a project, by definition, increases the number of points of potential failure. They claim that functions are not modules, that participants in the npm ecosystem have created a fetish for micro-packages and question if we have all forgotten how to program.

However, those arguing in defense of hyper modular JavaScript claim that size is irrelevant and it’s all about composition. As Sindre Sorhus states:

You make small focused modules for reusability and to make it possible to build larger more advanced things that are easier to reason about.

They question why you would even want to rewrite something like left-pad, citing the extremely small barrier to entry when publishing a package to npm as one of the reasons that npm is outperforming all other package managers in terms of growth.

Personally, I can see both sides of the coin. Sure, small, reusable modules facilitate composition and if you standardize on a dependency (even one as small as left-pad), you can iron out a lot of edge cases and potential bugs. Yet on the other hand, adding third-party dependencies to your code base ad infinitum, will increase your overhead and the scope for things to go wrong.

For me, context is important (for example is it a quick prototype or a production app?). Actively thinking about the modules or libraries you need to include in a project, is a great first step towards building something maintainable.

But what do you think? Let me know below.

7 Likes

I’ve done that (a long time ago), but for some reason, I can’t ever recall receiving that particular newsletter. Might be a good idea to subscribe again maybe… :persevere:

The next one is going out in a couple of hours, so might be a good time to do so.

Well I’ve done it (again) - let’s see what happens in a few hours :wink:

These are some of the most interesting thoughts/suggest to come out of this discussion in my opinion.

https://medium.com/@Rich_Harris/small-modules-it-s-not-quite-that-simple-3ca532d65de4#.ljk42g2i1

I’m of the opinion that we should not continue praising tiny modules as the way to author and consume packages. The packages that have really been important in the ecosystem are the non-trivial ones that include a wealth of functions with a coherent api. See jquery, moment, underscore, lodash, backbone, d3, express etc…

A frustration I have with the js ecosystem is that to get a lot of projects running you need to install 10 separate pieces and wire them together manually. The ruby ecosystem doesn’t have this problem, most popular gems contain enough utility to be able to be installed and used with as little config as possible - sane defaults allow you to use a lot without any config at all. This comes down to convention over configuration for the most part. It’s why it requires a lot more thought and searching for dependencies in js than other languages, too much thought.

This also affects popular libraries like React which ship without a lot of features that you’ll most likely require. An example is not including a function like classNames which I would assume is needed by the majority of those that use React in the browser. Jeremy Askenas’ packages hit a good level of complexity in my opinion, things that are used by the majority of those consuming the library are included but still aim to be as minimal as possible.

The number of packages graphs for different languages are silly when you look at the size of a lot of npm modules. Amount of code in the package managers is a much more interesting metric.

I like that js is a simple language with a small API but I can see the benefit of a larger core js library which has standard functions for things that a lot of people add a dependency for. For now, I’ll be actively trying to keep the number of dependencies down.

4 Likes

Yep, I agree with that. I had seen the post by Thomas Fuchs making the case for a better standard library. He makes some very good points. I appreciate that it can be a bit frustrating to have to depend on such small modules as left_pad in JavaScript, when coming from a language like Ruby (with a much better standard library), where you have all of that built in.

I also read Brendan Eich’s reaction to the call for a better standard library. It seems that the reason that this isn’t currently an option is that the language isn’t maintained by a single person or pair of people who design well (like the founders of Unix did, or the creators of Perl, Python, and Ruby do). Instead, “You have a bunch of people from different companies. They’d do a terrible job if they were in charge of building a big standard library.” — which I guess is a fair point.

There may be a certain element of naiveté about this next comment, but here goes anyway… If something like left-pad is so small (12 lines was it), and seemingly unlikely to need to change, why would you make it a dependency, rather than just rolling it into the larger piece? Is this just down to the way certain pieces of OSS are inter-licensed?

It feels rather like a carefully crafted spiderweb, you know, the kind that gathers dew and glows in the morning light, only to have it torn to shreds under the hooves of a passing elk…

2 Likes

No, just the node js people.

It’s further reaching than that. The JS ecosystem as a whole(npm) has glorified small modules and these philosophies are making their way to the front-end too.

Correction… Just the JavaScript people but there are less security concerns running JavaScript client side.

Not sure I follow. Do you mean why wouldn’t you make something this simple part of the standard library?

Nice metaphor : )

2 Likes

I was thinking more of a particular product making use of it as a dependency - let’s for argument sake say React is using something like left_pad (could be anything in reality) as a part of its build, wouldn’t you just pull that into the product core and take away the dependency? Given the size of it, and the limited scope of its functionality, it’s not likely to change much I’d have thought, so would seem a low risk route to take. For the impact left_pad had though, maybe it is significant enough consider for the standard library - in truth I’ve no idea what it does, apart from something lots of people relied on.

Pads a string with an specified amount of characters (which defaults to space)

leftpad('foo', 5)
// => "  foo"

“foo” is 3 chrs in length. 5-3 = 2, so it adds two spaces to the start of the string.

leftpad('foo', 5, '-')
// => "--foo"

etc …

Ah gotchya. Yeah, there’s no real reason (I can see) not to do that and I think it’d make sense.

The reason that didn’t happen (as Mark mentions) is due to the fact that the small module philosophy has been glorified and is the current standard way of doing things.

I’m just poking around the GitHub repository at the moment to understand it a little more…

I feel another spoof O’Reilly cover coming on… :wink:

1 Like

There’s not much to it is there?

module.exports = leftpad;

function leftpad (str, len, ch) {
  str = String(str);

  var i = -1;

  if (!ch && ch !== 0) ch = ' ';

  len = len - str.length;

  while (++i < len) {
    str = ch + str;
  }

  return str;
}

If you don’t mind the code being less readable, you can use the String.repeat() method to do it with even less:

function leftpad(str, len, ch) {
	return (ch || " ").repeat(len - (str && str.length)) + String(str);
}

// works with leftpad(), leftpad("text"), leftpad("text", 5), and leftpad("text", 5. "_")

Thi

2 Likes

I’ve not set up a test for this, so am not immediately sure what I’d get, but what does it do if you supply a pad value of less than the string length. Presumably nothing, with no error?

For example

leftpad('foo', 2);

Dunno about Paul’s code, but the original code will do nothing.

1 Like

Ahh, good thought.

That results in no extra padding being added, so the one-liner would have to be made even more complex with a Math.max method.

function leftpad(str, len, ch) {
    return (ch || " ").repeat(Math.max(len - (str && str.length), 0)) + String(str);
}

Complex is not good. Even though we can do it with the above, or with a more spread version of it:

function leftpad(str, len, ch) {
    return (ch || " ").repeat(
        Math.max(len - (str && str.length), 0)
    ) + String(str);
}

Complex is not our friend.

The original is better because even though both achieve the same end result, it’s easier to understand how the original works.

2 Likes

Another one-liner using the ternary operator (if that counts)…

function leftpad(str, len, ch) {
  return len - str.length > 0 ? leftpad((ch || ' ') + str, len, ch) : str;
}

[cough]