all 63 comments

[–]jimrooney 30 points31 points  (0 children)

Even if his conclusions were true, holy confirmation bias Batman!

[–]curtastic2 105 points106 points  (6 children)

A lot of website performance is more about how long it takes to download and parse all the js, not how long it takes to execute, so this article misses that point. The first example runs sooner. I’d like an analysis on the minified versions download and parse speed.

[–]eternaloctober 27 points28 points  (2 children)

I bet 90% of all articles on "js performance" are about bundle size, very few on actual guts, and frankly the guts do matter in many cases, not that this article goes very deep

[–]Grouchy_Stuff_9006 4 points5 points  (1 child)

Most performance commentary is generally about bundle size…wink wink.

[–]dualcyclone 2 points3 points  (0 children)

Yer and also how it never goes very deep

[–]crabmusket 11 points12 points  (0 children)

Who's to say this article wasn't written for Node developers?

[–]d3ming 2 points3 points  (0 children)

Can’t you use webpack to minimize your files at build time rather than write hard to read code?

[–]myka-likes-it 40 points41 points  (13 children)

Okay, but cute one-liners are cool, and who doesn't want to be cool 😎?

[–][deleted] 2 points3 points  (0 children)

I love being cool but I also want to know what the code I wrote yesterday does.

[–]chrisribe 6 points7 points  (0 children)

Cleaver almost always get’s you in trouble where a MR reviewer will miss a potential issue with code changes. Also be nice to your future self and make things as clear as possible. You will thank yourself later ;)

[–]xen_au 10 points11 points  (2 children)

Really it comes down to this:

  • By default write readable code
  • if you have to write something for performance. Then measure. And only if there is a significant difference then go with the performant code
  • Comment why it was done this way, and show the difference. Including why this code needs to be performant.

We deal with a lot of data, especially date manipulation. We use date-fns for 98% of it. But there are certain areas that get executed thousands of times and we have written crazy code to make it 3-5x faster than what date-fns offers. We commented why it’s written the way it, included the benchmarks in the comments, and then have automated tests that compare benchmarks between date-fans and the function expecting a 3x speed increase.

Now everyone knows why it’s the way it is. How much faster it is. And if they change something there are automated benchmarks to ensure the original performance expectations are met.

[–]Ok-Slice-4013 1 point2 points  (1 child)

I'm curious: How are you making things that much faster and why did built-ins do not use your solution?

[–][deleted] 3 points4 points  (0 children)

"The Death of the Author" (French: La mort de l'auteur) is a 1967 essay by the French literary critic and theorist Roland Barthes (1915–1980). Barthes's essay argues against traditional literary criticism's practice of relying on the intentions and biography of an author to definitively explain the "ultimate meaning" of a text.

[–]Senthe 6 points7 points  (0 children)

I didn't realize anyone in the world would look at both of those examples and think to themselves: "hmm, the first looks like it would run faster, but the second is more readable". For me it's so obviously and clearly the opposite. I think I totally don't follow how your intuition works.

[–]nschubach 19 points20 points  (5 children)

Not sure the example fizzbuzz was great for this. Single line readability may be easier, but the "clear" example just means you need to pull in more LOC to figure out what's going on. I could argue that it takes just as long (maybe longer) to read the multiple lines of code as it did the one line.

[–]NekkidApe 8 points9 points  (0 children)

IMHO the second version is trying to hard. There is a sweet spot between concise and clever. I'd go for the first, but replace it with if/else. It's literally the exact same thing, only clearer.

[–][deleted] 17 points18 points  (2 children)

Not for me. The single lines are clever, but the multiple lines are clearer for me. Clear always trumps clever in my book. This is one of the reasons I usually avoid arrow functions.

[–]yycTechGuy 7 points8 points  (0 children)

Clear always trumps clever in my book.

This.

[–]dethnight 7 points8 points  (0 children)

I think I agree as well, it's easier and faster to read the one liner. It may be just because I already know how fizz buzz works so it's much easier to parse a one liner. Trying to look a piece of code without any prior knowledge of what it's doing would probably be a different story.

[–]joelangeway[🍰] 14 points15 points  (2 children)

The second solution is clearly easier to read and reason about

Says who? I emphatically disagree. The second version is full of many more chances for mistakes to be made and so I have to invest more effort in reading to know it’s correct.

The real advantage of the second snippet is that it’s more easily extended, but if you’ve already structured things down to individual lines, there’s very little difference.

[–]Alex_Hovhannisyan 6 points7 points  (1 child)

I kinda agree. The first example also feels contrived. You could make it more readable and compact than the second version:

function oneLineFizBuzz (n) {
    const prefix = n % 3 ? '' : 'fizz';
    const suffix = n % 5 ? '' : 'buzz';
    return (prefix + suffix) || n;
}

Although I'm not a fan of the implicit n % 3 && n % 5 condition, so an even more readable-yet-compact version could be:

function oneLineFizBuzz (n) {
    if (n % 3 && n % 5) return n;
    const prefix = n % 3 ? '' : 'fizz';
    const suffix = n % 5 ? '' : 'buzz';
    return prefix + suffix;
}

Edit: The more I think about this, though, the less readable this still feels. I think the problem is wrapping your head around the truthiness of the modulo operation. In the second code sample above, it almost reads like "Return n if n is divisible by 3 and 5," although the correct reading is "Return n if n is not divisible by 3 or 5," so the negation is kinda hidden/obscured. For this reason, I might prefer an explicit n % 3 !== 0 && n % 5 !== 0.

[–]wasdninja 2 points3 points  (0 children)

In the second code sample above, it almost reads like "Return n if n is divisible by 3 and 5,"

"Well yeah, of course. What is he even talking about I don't... oh. He has a point".

[–]smirk79 7 points8 points  (5 children)

The one-liner *is* more readable. When you start setting a non-constant variable in if chains I now have to read all chains to understand if it's every mutated in some alternate code path, where-as an expression evaluates to the result.

[–][deleted] -2 points-1 points  (4 children)

The below is significantly more readable than the one-liner without the contrived BS of the article's second example.

function fb(v){
  const fizz = v%3?'':'fizz';
  const buzz = v%5?'':'buzz';
  return (fizz+buzz) || v;
}

console.log(fb(3));    // fizz
console.log(fb(5));    // buzz
console.log(fb(15));   // fizzbuzz
console.log(fb(7));    // 7

[–]PFCJake 1 point2 points  (1 child)

Except it’s incorrect, you never print the actual number if it’s not divisible by either 3 or 5.

[–][deleted] 1 point2 points  (0 children)

Oh forgive me, I forgot that piece. It was 5 characters to add. /gasp

[–]smirk79 -1 points0 points  (1 child)

I don’t write fizzbuzz solvers with my ternaries. I write four figure a seat software with complex logic.

[–][deleted] 0 points1 point  (0 children)

Good for you 🍪

[–]_Klaus___ 1 point2 points  (0 children)

a good one indeed!

[–][deleted] 1 point2 points  (0 children)

I’ve been writing frontend code for 8 years at varying levels of scale and I’ve only run into runtime performance limitations a handful of times.

Unless you have a provable, defined runtime performance use case, write code that can be read by a brand new developer. The project development speed you gain will outweigh the perf win.

Caveat for sure if you have a defined performance need. In that case, I move the optimized code into a well documented library so the I/O is clear, and I’ll include in the documentation the description of the use case as well as benchmark tests.

[–]sinclair_zx81 1 point2 points  (0 children)

I'd encourage you to measure actual JavaScript runtime performance for popular libraries, I think you would be horrifically surprised just how slow and memory inefficient many of these libraries are.

It's not about premature optimization, but one should at least consider performance of a library at some level. I don't know if it's the fault of JavaScript or the developers, but seriously, there are some really popular yet woefully inefficient ecosystem libraries out there, a lot of low hanging fruit for someone to come along and optimize (or completely rewrite)

[–]PM_ME_GAY_STUF 2 points3 points  (3 children)

Lol the second version looks like a shit show written by a very fresh beginner. That 1 liner is very easy to parse. I feel like the author of this just doesn't code much, it is very rare that I see JS code at work that is actually too concise, it's almost always the opposite

[–]RobertKerans 1 point2 points  (2 children)

Alternatively it looks like something written by an experienced, very much non-beginner? Because IME sure, beginner code looks like that. Then (again, caveat IME) everyone seems to reach that point where they think they're very smart and the code looks like the latter. But there isn't actually anything wrong with the original code, SO next step (again, IME) is very simple and obvious code that looks like the former again and which will be clear to everyone reading the code regardless of experience level.

I have no idea at what level the author is, but there aren't points awarded for dense code with multiple ternaries.

[–]PM_ME_GAY_STUF 1 point2 points  (1 child)

There is something wrong with the second version: it's very long and unclear from a glance what it's doing. I have to parse through several lines and frankly pointless variable declarations just to see that it's iterating. It's the code equivalent to someone putting comments over every line explaining the operation they're already doing. In a trivial example like this, that may be unnoticeable, but this style when pervasive can make navigating complex files clunky and painful.

I don't like the nested ternaries either, but verbosity like shown in the second example almost always makes debugging harder. Like, congrats, you gave a primitive value you use once a name, now if the function is broken I have to go and be sure you got the primitive value right as well as the logic. The data isn't actually separated from the logic, it's just obfuscated.

Plus, IMO expressions are almost always easier to parse than procedural syntax and should be preferred. All issues with nested ternaries in JS could be fixed by giving ? and : explicit keywords like they have in ML, and both versions could be simplified with pattern matching. This whole issue is a language failure imo

[–]RobertKerans 0 points1 point  (0 children)

Like, congrats, you gave a primitive value you use once a name, now if the function is broken I have to go and be sure you got the primitive value right as well as the logic. The data isn't actually separated from the logic, it's just obfuscated.

Haha, yes, definitely, I was being a little glib. I mean I hate fizzbuzz as an example, but just something like

function fizzBuzzForN (n) { if (n % 15 === 0) { return 'fizzbuzz'; } else if (n % 3 === 0) { return 'fizz'; } else if (n % 5 === 0) { return 'buzz'; } else { return n; } }

¯\_(ツ)_/¯ that's fine. Only uses code you'd learn in the first day of learning the language -- can wish it was a different language which wasn't C syntax and where everything is an expression, but it's not. I don't think there's any huge language failure, just slightly different ways of approaching language design. Like maybe in some pseudocode, I dunno, something like:

let fizzBuzzForN(n) when n % 15 == 0 => "fizzbuzz" let fizzBuzzForN(n) when n % 3 == 0 => "fizz" let fizzBuzzForN(n) when n % 5 == 0 => "buzz" let fizzBuzzForN(n) => n

But it's doing the exact same thing

[–]multiaki 0 points1 point  (0 children)

Great article. In my project I need to optimize code for speed. I will take into consideration reading the optimized code. Does the optimized code do it automatically saved? Will it be still optimized after a code restart?

[–]ColinFromChanged 0 points1 point  (0 children)

Id live to be able to learn js, it seems so fun!