341 comments

[ 3.6 ms ] story [ 253 ms ] thread
...for a site as simple as hn

Yes!

True Because hackers need much more code lines than those.
Most bloated sites don't even have an excuse to be more complicated than this.
That might be one of the most readable pieces of code that I've ever read.
Really? Plenty of needlessly abbreviated function names IMO. At a quick glance is it immediately obvious what posf or arem are without looking into the context in which they're being used? Don't get me wrong it's a nice piece of code but super-readable it isn't.
Well, I disagree. It is abbreviated, but since the whole file is self-contained and the definitions are put at the beginning, there is enough information to infer the function's semantics without being excessively specific. I think well defined organization of abstractions is an often overlooked but surprisingly effective way to achieve readability
> there is enough information to infer the function's semantics without being excessively specific

You mean you can just read the function definition to see what it does?

He doesn't like being excessively specific with his writing I guess. Plus it makes you sound more like an engineer when you speak like this.
Really? It has horrendous naming and it isn't even using es6

function byClass (el, cl) { return el ? el.getElementsByClassName(cl) : [] }

const byClass = (el, cl) => el ? el.getElementsByClassName(cl) : []

How does es6 improve readability in this case? It barely removes `{}` and replaces very clear word `function` with `const` that in most languages is associated with constant values, not with functions
It removes the brackets and the return keyword, const is in fact a constant but this not ES6 it is just doing a function expression instead a function declaration (you create a const variable and assign an anonymous function to it instead of using the keyword 'function')

This is used for readability and to avoid hoisting that can be confusing.

Given that brackets and the return keyword (and the function keyword!) are core parts of functions (a scope and a value to return to the caller), I would argue that removing these key parts of a function makes it harder for someone not familiar with ES6 to see that it is a function.

This reminds me of Ruby's return-the-last-evaluated-thing-in-a-function semantics (which sadly Rust copied). Having to type one extra word is not such a burden that you should add cognitive overhead each time someone not intimately familiar with the language has to read your code.

If the argument is that only ES6 experts should only ever have to read or write ES6 code, then I would argue that any syntax argument is pointless because only experts can comment on a languages syntax -- and thus COBOL objectively has the best syntax and you cannot disagree unless you are a COBOL expert.

> This reminds me of Ruby's return-the-last-evaluated-thing-in-a-function semantics (which sadly Rust copied). Having to type one extra word is not such a burden that you should add cognitive overhead each time someone not intimately familiar with the language has to read your code.

I think the concept of returning by default is fundamental enough that doing so or not is mostly a matter of what you're used to. If I'd started out with Elixir or Ruby I'd probably find it strange that I have to manually return stuff from a function.

Furthermore, at this point ES6 is common enough that knowing at least some of the core additions (among them arrow functions with their implicit return) should be something any front-ender knows.

It does seem strange if you think of it as “return the last evaluated thing from the function.” But that’s not the semantic; the semantic is “everything is an expression and expressions evaluate to a value.” Removing this behavior would make these languages less consistent.

(And yes, in both Ruby and Rust not literally everything is an expression, but almost everything is.)

I agree that because of common uses of things like match in Rust it makes it consistent to also do it for functions. I don't agree that the reason why it's consistent is because a function body is an expression (though of course you have more expertise than me on this one -- and Rust does actually treat scopes much more strictly than most other languages so you could argue every scope has the smell of a function call associated with it).
It was almost certainly written long before ES6 was widely supported.
The es6 syntax is LESS readable. It's less declarative. Just quicker to type if you don't have a nice editor setup.

My issue with this method is the name more than anything. I would also guess if it's even needed - probably bad application logic is requiring that ternary statement but didn't read it yet.

Ideal method name is already given - getEmementsByClassName... Really should just have a utility to default return types.

Your code is not more readable, it is just shorter.

In fact, using the online babel transpiler, it transpiles the es6 version you provided precisely into hn's version:

https://bit.ly/2MPmjK7

To use es6 to achieve what this tiny piece of javascript could already do, you probably need to introduce some tremendous dependency like babel just to be compatible with all kinds of weird browsers out there. What does es6 give here? Not very much in my opinion.

It's 2018. Unless you're using Internet Explorer arrow functions are supported natively.
And I bet there are some people here using IE. And old browsers.
I would REALLY love to see the browser usage statistics for HN :)
Well.. I might be an outlier, I use OmniWeb and Camino as they give the best results with my 2005 Mac. So I'm still living in the Pre-=> Age.
Probably not worth setting up a babel transpiler for that small amount of JS tbh

Makes supporting older browsers a lot easier

You're still returning a live HTMLCollection or an Array.

It's still bad code. Using newer syntax to do the same thing didn't actually improve anything.

It seems some of the one-liners are simply renaming JavaScript built-ins like forEach to aeach and slice to acut. Maybe this is to make it look more like the Lisp backend? But taken on its own it does not really improve readability.
No, it is because some array-like objects like node lists actually aren't Arrays and don't have those functions in their prototype.
this is why I usually go for something like const $ = (el) => [...document.querySelectorAll(el)]
Although NodeList has forEach defined on it now at least.
I think the main reason this is somewhat readable is because there's hardly any code. Which is fine.
The supreme art of war is to subdue the enemy without fighting.

-sun tzu

It's also a piece of code that hasn't been rewritten to fit whatever buzz is the buzz today. I wish more of the world was written with code that need not be rewritten every five years.

    function onready () {
      recoll();
    }

    document.addEventListener("DOMContentLoaded", onready);
Just attach recoll() directly. There's no benefit of going via onready().
`recoll(apse)` is a verb. `onready` is an event condition. They are not interchangable. (Though it might be combined into a single line with an anonymous function argument to addEventListener.)
You don't have to make every piece of code 100% tight. onready is actually the kind of function that you very likely want to add more lines to in the future, so why not leave it in? (I'm willing to bet it had more lines at some point in the past.)
onready is actually the kind of function that you very likely want to add more lines to in the future, so why not leave it in?

I'm a fan of writing the code you need when you need it. Adding or leaving things things there 'just in case' leads to code that's harder to reason about in the future because it's full of things that may or may not be used.

(comment deleted)
Originally there was something else in that function body. That's also why we didn't collapse it when we took that bit out.
https://twitter.com/triskweline/status/798443082740023296

> Valve's Steam Store renders on the server, uses ancient jQuery 1.8, loads 12 unminified JavaScripts.

> It moved 3.5 billion dollars in 2015.

And burned 3.5 trillion developer brain cells just this january. Sensationalist metrics like this belong in the same trash bin as 'js is objectively bad and non-serious' bs.
so actual information is bad and "3.5 trillion brain cells burned" is good? Hrm....yeah, I'm going to Steam approach and focus on features that matter to the user rather than pedantic code which serves nothing but the developers ego in a vast majority of cases.
your choice, it's your cells :) I would not want to dot that to my sanity.
I haven't had my morning coffee yet.

That's a piece of factual information for you, yet it has no bearing in this discussion.

Valve's revenue might be factual, but it also does very little to further this discussion. Valve has almost no effective competition, and people will put up with a lot of shit from them because of it — which makes their JS engineering practices largely unrelated to their revenue.

Yup, that's exactly the point. Focusing on pedantic js engineering practices will not effect their revenue, so hence, the developers do not focus on doing so.
I'm not sure of what it's supposed to prove, who knows how many clients and how much money Steam lost because of slow loading/painting times or just plain broken JS. From experience I can tell that I gave up buying a game on Steam at least once because of broken jQuery.

There are real metrics from top industry leaders[1][2] to prove that loading time has a clear impact on conversion rates.

>Consider this when you need 5000 npm modules and a team of 10 to compile your startup's login form.

Unminified JavaScripts files and bloated JS are both wrong, once again: not sure of what's the point of comparing both.

[1] https://medium.com/@vikigreen/impact-of-slow-page-load-time-... [2] http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20....

What it proves is that it’s good enough to make tons of money.

You’re arguing from a hypothetical alternate reality where they could have been better off, but there’s no point in that. It’s like saying “Sure Usain bolt can run a sub 10sec 100m.... but if he’d gone into politics perhaps he’d made even more money and been twice as famous, we have no way of knowing, so how does that sub 10s 100m really prove anything about whether or not he’s fast?”

Usain bolt is fast, and that code made money. It’s just facts, accept them.

Steam forums on the other hand are an abomination stitched together from every content tracker and third party js library ever written.
This is neat (how the up/downvote onclick handler sends the info to the server).

    new Image().src = el.href;
Where href looks like this:

    vote?id=xxxxxxxx&how=up&auth=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&goto=item%3Fid%3Dzzzzzzzz#wwwwwwww
Maybe it's neat but this is not how anyone would write it today. This is the legacy code still left from the time before ajax was invented.
Probably. I wonder though if they are trying not to use any libraries and Image was just easier than xhr.
Hacker News was launched 2007.

XMLHttpRequest has been around for about 7 years then and using it was called Ajax for about 2 years already.

Not to mention the "ajax" function in the script...
There's also an XMLHttpRequest wrapper for when they care about the response.

This is used for write-only GET requests (with graceful fallback when JS is disabled since those are plain anchor elements).

It is strange. I wonder if it's an approach that lets duplicate vote attempts get automatically responded to by the webserver cache rather than passing through to be detected computationally at the application server level, which would also involve a DB call.

Conceivably this would make the architecture more robust against vote-spamming attacks, and the approach would also provide an enhanced version of this benefit if using an external caching reverse-proxy service like CloudFlare or similar (though I believe HN does not use CloudFlare).

This is complete supposition on my part.

Is this a GET request? What if my browser wants to peek ahead on some links?
This would indeed be problematic :-) The up/down vote buttons are plain anchor elements.
Image makes the HTTP request without appending the Image to the DOM? TIL
That's how most tracking scripts operate...
any more javascript than this and you're probably making something that's full of ads
Or something that has features?
While I appreciate the JS on this page (for usability reasons), I was surprised by the amount (149 lines) that is used for a page which looks like plain HTML.

Nevertheless, the code looks kinda beautiful.

Either run it through a minifier or have the whole thing unminified, these single lined functions look horrific, and are saving characters for the sake of it.

Terrible naming, e.g. 'vis' -> reading the function it means toggle visibility, so it should be called 'toggleVisibility', you shouldn't have to read what the function is doing to understand what it will do.

You ever get a bug in your code, It's not fun to look back at badly maintained code and go 'oh, shit, what does this do again?'.

> Terrible naming, e.g. 'vis' -> reading the function it means toggle visibility, so it should be called 'toggleVisibility'

But it doesn't toggle visibility. It sets visibility.

Yeah, you're right, I misread the code. It should be called 'setVisibility' then.

A problem that wouldn't exist if it was named correctly in the first place ;)

I couldn't disagree more with your first sentence - being able to see all those functions in the first page of the code above the fold is what makes this 10 times easier to understand than any React or Angular component I have ever seen. In fact if a function doesn't fit on one line, it should be refactored into simpler functions until it does. Oh for a full UI framework like this made of single line functions - One Line Framework. However I am biased having used asm in the 70s, C in the 80s, java in the (late) 90s, and javascript in the naughties and teens.
Official name of the one line per function framework is FOOL - Functions on one line.
The code is very much readable, without suffering from being concise. Love it.
Hacker News also works pretty well without JavaScript at all, which is really nice. Not something that I use often, but it comes in handy when you want to post a comment from your potato device that can't handle the modern web.
I once managed to open HN on a Nokia 3110 classic. Scrolling hanged and eventually rebooted the phone, but nevertheless the signature "HN orange" could be seen.
I usually read HN using w3m. It's entirely usable with no javascript.
I actually think this code is nice! Love the one liners. Variable names could be a little more....explicit.
To me this looks more like transpiler output ...
(comment deleted)
It would if the variable names were all a,b,c. But they're not, they are almost Polish Notation. There's a all right, but fn, cl, tag for callbacks classes and tags. Also n and m for integers in a range and x for iterable integers.

This is the work of someone with a ton of experience. You take 10 minutes to look at the code and understand it, then you are groking complex ideas and operations quickly. I'd love to work with whoever wrote that code.

Not all that impressive, considering non-consistent code style and the like.

Also not a fan of unnecessarily cryptic variable names.

In most places this code would never pass a code review and would be considered plain unacceptable. I guess I'd prefer some place where this code style is the norm.
Go into academia. You'll receive no code review, and you'll deal with pretty bad code, but you'll be free to write however you want as long as it works and can be published.
So you would like to work a place with no code review? You will be happy to hear that many many workplaces have no code review at all.
> So you would like to work a place with no code review?

How does this follow from my statement?

Sorry if I misunderstood.
It only exists because DOM APIs are crap, and jQuery is too big. So it ends up reimplementing parts of jQuery needed to run.
For the record, jQuery 3.3.1 (production, normal) is at 30kb [1]; if you don't want effects (production, slim), you can get it for less than 24kb [2]. For the value it provides, that does not sound "too big" to me.

[1]: https://code.jquery.com/jquery-3.3.1.min.js [2]: https://code.jquery.com/jquery-3.3.1.slim.min.js

In contrast, hn.js only weighs 2.1kb and contains all the business logic.
And it isn't even minified. So if there would be a problem in production it would be easily debuggable.
Barely readable but I think there's a lot of optimization going on here... I don't think that optimization is done correctly but that is why it's hard to read.
(comment deleted)
There's minimal optimization (as in tricks), but lots of optimization (as in readability and common sense).

It's also incredibly readable; I'm puzzled as to what the bar for "readability" is if this doesn't meet it.

It's just a pile of functions; no actual processes are scripted to execute.
> function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }

Why does it use `unshift` rather than `push` ?

The only reason I can think of is that the last class added will be faster to remove when iterating over the array...

    function aeach (fn, a) { return Array.prototype.forEach.call(a, fn) }
Does this win anything over using forEach directly?
A lot of things like this will work on both arrays (thta have a .forEach method) and things that behave enough like arrays such that Array.prototype.forEach works for them.

The downside of this approach (if you subscribe to the ideology of OO) is that if you make something completely unlike an array internally, like a linked list, you can’t use this function on it. Whereas, if you wrote a .forEach method for it, you could use it interchangeably with arrays anywhere the code calls .forEach.

Without trying it, I’ll guess that this is used on the arguments magic parameter. It’s notorious for being array-like but not an array.

DOM functions like getElementsByClassName return a nodelist, which as you say behaves like an array.
This function could be used to iterate over the `arguments` array like object, but I do not think it is used for that purpose here.
It's used to iterate over HTMLCollection objects here.
(comment deleted)
This works with array-like objects which aren't arrays. It's important if you want to support older browsers which don't implement NodeList.forEach(). Well, it's really just for old IE.

If you're targeting modern browsers this is no longer an issue, as they all support NodeList.forEach(). It's worth noting that many DOM APIs return live collections, so depending on what you're doing you might want to clone it first. Luckily now with ES2015 you can use Array.from(list) or just [...list].

This style is very similar to one which is used by expert C programmers, it seems that most people that write like this have had experience writing parsers and compilers too. It's not their fault that most people don't just think purely in terms of function composition :P
I'm sure the code works very well for its intended purpose, but I'm kind of wary of the idea that using obscure abbreviations like 'posf', 'arem', 'vis', 'ind' etc. makes you "expert hacker".

I don't agree that code should always be designed to be perfectly readable by outsiders. If such abbreviations works for the people who have to maintain the code, more power to them. But I disagree than this is somehow proof of superior skill.

I think there are several different types of C programmers, and that terseness and functional design are very different.
Yes, and it reminds me also of Unix shell commands.
Looks like pretty average code to me.
Love it. Looks like lisp (can almost tell pg or rtm wrote it?), functions all over. No bloat...
Thanks for sharing this nice bit of code; we always learn from examples, we should see more of this.

I feel the naming is too short, things are just a little too specifically compact and slightly cryptic ... almost like the author is trying to say something ...

I've never said this before about code ... but I think that code is 'smug'!

Like odd facial hair, or one of those valley-specific t-shirts ... the code trying to project how cool it thinks it is!

That code is 'humble-bragging' ...

Really? It just looks compact and efficient to me.
"Compact" (abbreviated) names are negligibly more efficient when machines read them, and they're wildly less efficient when humans read them. Code is read many more times than it's written, so it doesn't make sense to optimize for fewer keystrokes when typing the code.

This is especially true with modern IDEs that autocomplete everything.

> Code is read many more times than it's written, so it doesn't make sense to optimize for fewer keystrokes when typing the code.

That's actually probably not the case with JavaScript, so it may indeed make more sense to write it to be more efficient to execute.

Post-processors do that much better than a human and can also tree-shake. There's no reason for a human to do it.
Exactly, those function names hurt my brain:

remClass -> does it hurt to write removeClass ?

what is vis, ind, posf? I shouldn't have to decipher the function to figure out what its name might mean.

Hacker? no. Smug? yes

Too compact? It's the size of an APL implementation, and all it does is some ux... ;)