How true hackers write JavaScript
news.ycombinator.com
news.ycombinator.com
This file is just in consonance with this website's style: concise, terse, to-the-point and with minimal bs.
Of all of my currently in-rotation news websites, this loads and reacts the fastest, no matter where I am or how crippled my current connection is. I am sure they are factoring load times and speed over 'shiny new features'.
Keep going, YC!
If you load megabytes of JS to do animations, as opposed to using CSS, then yes you are just doing it wrong.
Things aren't either exactly right and done one way, or else terrible. You can of course have bells and whistles like animations if your usecase requires them (HN doesn't!) and still have a fast loading site with clean code, if you do it right.
The disease is overutilizing buckloads of js scaffolding which is blatant in the majority of the websites right now. Even a small blog now "has to have" the latest xzibit framework which pulls 32 foobar dependencies, all gulped into one huge blob of minified compressed clusterfuck-pack.
Though I agree about what you're saying for blogs and other content sites, where the tradeoff is clearly inappropriate in a lot of cases. With that said, things like https://www.gatsbyjs.org/ offer an interesting '3rd way' here, allowing you to keep the productive toolchain and framework stuff but still serve a blazing fast static content site.
Because this is MUCH better than sites loading megabytes worth of scripts to do nothing more than render text, have dozens of floating elements everywhere, auto playing videos that follow you, "use our app" buttons, etc...
JS is the assembly of the web, do you also criticize games written in assembly?
No it isn't, any more than C++ is the assembly of your operating system.
Assembly has the terseness that it does because of constraints that javascript doesn't have -- this javascript looks the way it does because the author wanted it to look like lisp code, not because it has to.
Depending on where you are starting that could be JavaScript, or it could be Basic, or any of a number of things. In the browser it’s JavaScript.
This only makes sense if you can see that there are machines within the machines, within the machines.
Seemed related to the second paragraph in your comment
I went to my girlfriend's computer to see if she was syncing with Dropbox or something. Nope. She quit all of her applications just to be sure. Except for a single browser tab opened to a recipe for pita bread.
Yep, turned out that a recipe for pita bread was saturating our router because what looked to be buggy error-handling for ad-related network code. I think her ad-blocker only partially maimed it, so it went apeshit.
If it's a typical platformer from 1980s, now. If it's a modern game with a modern game's complexity, it would never be finished in assembly anyway.
hm
HN is a simple website. I'm glad that they decided to keep the code simple as well. I love the fact that the code almost fits in a single screen. Zero dependencies. You can understand the entire code in a few minutes.
I think new developers greatly underestimate the negative impact of dependencies. Today, something like create-react-app downloads hundreds of megabytes of dependencies. Libraries, transpilers, compiler extensions, and various tooling. Of course all those things come with many advantages, but what most people miss is the huge cost of all that. The increased complexity introduced by dependencies. The unnecessary bloat.
Sure, the way you name your variables have an impact on readability. But it's nothing compared to adding thousands of dependencies to your project.
False dilemma. Code can be concise and readable.
> JS is the assembly of the web
No. Assembly is supposed to have a virtually one-to-one correspondence with machine code. JavaScript is far from that. It is a higher-level language.
> do you also criticize games written in assembly?
I would indeed criticize someone writing a game in assembly if an alternative was available.
If that sounds strange or obviously wrong, it may be that you're taking certain widely-held beliefs about programming as if they were truths about programming. Also, the conventions of the larger codebase which this small program is drawn from carry a lot of information, and free us to work with concision and simplicity that aren't so common in production codebases.
I know this sounds weird, but hope that it will stir curiosity in one or two of you. By far the biggest mistake I made as a young programmer was staying within what one might call the shallowculture for longer than I needed to. The outstanding properties of the shallowculture are that it 'knows' how to program and 'knows' to dismiss uncommon approaches to programming as wrong and inferior. If you notice yourself reacting this way, my grizzled advice is to pause and start questioning. Look into it deeply, and you'll find that this seeming 'knowledge' is spurious. That will open up for you more satisfying ways of making software. For example, you'll find ways of avoiding the complexity bloat that is routinely lamented in Hacker News threads as a plague on our industry.
It doesn't mean you're wrong, of course. I am still learning to assume that smart people are smart people who, by and large, have smart reasons for things that on their face seem silly to me.
I think this is one of those times for me. Not knowing the bigger picture, I want to give you the benefit of the doubt. But as one piece of stylistic feedback, since bikeshedding is easy: Congress repealed the Taxation of Readable Variable Names Act around the time that autocompletion was invented. I am dumb and find those far easier to read at a first glance.
Edit: I guess this thread is one of those times that software reveals its true nature, which is that it has almost nothing at all to do with computers.
So, like, maybe two people besides yourself.
And that's merely the best-case scenario; systems that go for that rarely make it to 100%. What usually happens is they find abstractions that give 90% of the desired flexibility, but break down at the problem's edges and require special-casing and workarounds to sort of hobble their way to the finish line. The complexity of that special-casing means you end up giving back a lot of the maintainability you hoped to win with such a design. It's like grabbing a handful of sand: a lot slips through your fingers.
Since we're not going for full decoupling and flexibility, what should we do? Here we differ from what most developers would consider best practice. We don't adopt half-measures, we try to avoid them. Since we're not trying to fully abstract away the structure of the markup, we just let that structure be naked in the code. For example, since 'hide' needs to remove 3 nodes, it just removes 3 nodes. We do it this way not because we don't know the risks of dependencies, but because we know the risks of abstractions: most cost more than they're worth. Programmers have been so steeped in 'best practices' that they tend to incur those costs without being sufficiently aware of them. Alice does it for her feature, Bob does it for his, neither's top priority is the system as a whole, and the costs build up like mercury in the blood. To be fair, most programmers have little choice, given the project they're on and the culture of the profession. You can see how deeply engrained this culture is by the bulk of the responses in this thread. In my experience, one can't fight it. One can only look for a project with different thinking (which btw is why sctb and I are here).
Our profession has a large blind spot about the downside of adding code. Obvious abstractions that have a best-practicey feel (e.g. assigning names to the nodes that 'hide' has to remove, or putting the nodes into a collection so 'hide' need only remove one thing instead of three) are like that old adage "no one ever got fired for buying IBM". You make a suboptimal choice because it feels like you're supposed to and it feels safer. That dooms software. All those little choices add complexity, complexity compounds rapidly, and soon you are paralyzed with bloat. To avoid this you need heightened, even paranoid awareness of the downside, which unfortunately runs opposite to most software culture. I'm actually not very good at it; others are much better [2]. pg never adds a line of code unless he's forced to. Me, I add 10 lines of code, then feel embarrassed and and remove 7 or 8 of them. But at least I feel embarrassed.
So that little number 3 in 'hide' is not there because we're naive and made a rookie error, waiting to be uncovered by the internet—it's because we considered the code that various options would cost, considered what they'd buy us, and decided that in the context of this system the simpler approach was best. That's what I meant about the code being written for maintainability. If you do that in every case and you're lucky and the moon is in Sagittarius, maybe you evade the rigor mortis of complexity and live to hack another day.
[1] In case that sounds like dissing accessibility, I'm not, but it would take time to explain why and I don't think those reasons are in the critical path of this discussion.
[2] If anyone wants more grizzled advice, mine is to search out a programmer who is better at this than you are and make it a career goal to work with them.
As for adding code, note that my suggestion would remove code - removing a single node in code is obviously simpler than removing a fixed number of nodes in a for loop. But you are correct it introduces an abstraction. I appreciate the cost you assign to abstraction, but I personally think this should be weighed against the cost of more complex and fragile code as here.
Again, I'm not criticizing the code itself, just the assertion that the code is super maintainable. The proof of the pudding is in the eating. If it is too costly to accommodate users using screen readers (how few and unimportant they may be) because of the complexity and risk of changing the HTML, then you have a maintainability issue.
In my experience the way to get extensibility is not to try for it, which leads to bloat, but rather to have the smallest system you can. A smaller system is easier to make a major change to than a larger system, and you don't need to anticipate the change in advance.
I would say simplicity is a really important part of maintainability, but that is not the same as smallness. For example, the for-loop mentioned is quite short and compact, no doubt about that. But to me it it not simple, because it is hard to figure out what it does and what happens when you change it. It is tightly coupled to the particulars of the HTML (even including the whitespace) so you have to dive into the HTML to see what nodes is removed and figure out yourself what is the implicit relationship between these three nodes. So is difficult to change without accidentally breaking something.
Making code simpler often leads to it also being shorter, which is good. But making code shorter without making it simpler just leads to unmaintainable cryptic code.
But I acknowledge maintainability also depends a lot on the people who are supposed to do the maintenance. I personal have limited memory, and I would forget what exactly those three nodes represented within hours of writing the code. I use abstraction and simplicity not because of some lofty ideal, but because I simply do not have the mental capacity to keep this amount of accidental complexity in my head. Your mileage may vary.
But it's especially the inconsistent use of camelcase in function names than makes me think it's pretty amateurish.
You can have the exact same code wihtout abbreviations, and I can assure you that it will still be 2-3 pages long, with very short functions.
In this system, 'rks' communicates something that 'ranks' does not: that this is a local variable denoting a collection, and that it is a collection of the things that 'rk' denotes one of. So actually it adds quite a bit to readability.
If I were going to edit that code to make it more readable, the one change I'd consider is using 'r' (or maybe 'n', because these are integers) instead of 'rk'. One can argue either way whether the k adds information or just noise.
Despite its size, this .js file has been modified throughout many many years probably by various authors.
And what is with this code returning false for everything? Everyone knows you're supposed to return JSX wrapper in another DIV in order to talk to browsers!
Lastly, how could they ever expect this solution to scale? I bet this is only slightly worse than okay for a hack-a-thon but hell would surely freeze over the minute a single user was forced to use it!
It's a maintenance nightmare. It's far better to start with some webpack + react boilerplate. Each one of the dependencies means that someone else is maintaining the code, so you can wash your hands of that responsibility and sleep well knowing internet elves are maintaining your web app!
To be honest the code is not very readable, but it is very simple and self contained. It would be very easy to dive into to fix a bug because there are no external dependencies or frameworks you have to understand.
The list of one-liners at the top seem like a very minimal 'jQuery'-like layer. Perfectly fine except for the l33t identifier names.
But there is also some really fragile coupling to the HTML, e.g.:
for (var i=0; i < 3; i++) { remEl($(id).nextSibling) }
I wouldn't dare to modify the HTML when the JavaScript looks like this! What are the three elements which are being removed? Why exactly three? Probably the author and maintainer of the code knows, but this is the kind of coupling where nobody except the original author would dare to change anything. Perhaps this why HN still uses tables for presentation - nobody dares to change it?There's also simply no reason to change it, it works.
It works, and it's arguably simpler than the div-soup with extensive styling, which is the "kosher" way of doing this.
Right now there's already nested tables-within-tables, just to work around how table columns work in order to handle indentation (and each row's indentation is handled separately with a stretched 1x1 image with a width attribute that's calculated server-side).
As for the redesign, the total amount of code (HTML/CSS/JS) you’d need to touch is still small. Do web designers still exist that only do HTML/CSS but no JS?
I hope so, given that most websites (as opposed to web apps) in existence should not need JS for core functionality.
That is really beside the point. HTML and JS should not be coupled to the extent that adding or removing some whitespace from the HTML will cause the JS to fail. This kind of things turns a trivial improvement into a nightmare, so you end up not daring to change anything.
But it is simple, and it does get the job done for the most part.
Either way, it's a minor annoyance on an otherwise fantastic site!
The 3 elements being removed are the tr containing the story options, a literal whitespace node, and a tr used as a spacer.
I agree it is a coupling, and while accurate to describe as fragile or even really fragile, I personally would not have used those words.
I would feel more comfortable daring to make changes in this html and JavaScript than any other front-end code I've worked with in the last 15 years of my career. If for no other reason, the sheer small size of it and lack of external dependencies that you mentioned.
If there is such information, there are two possibilities: either we could easily modify the code to express it; or we could not. Each would be interesting, but for different reasons.
for (var i=0; i < 3; i++) { remEl($(id).nextSibling) } // pop following options row, whitespace text node, .spacer
This way, if (for instance) someone were to try to modify the layout to use margins rather than spacers, or if someone were to strip whitespace from the rendered HTML, a quick search (or manual skim of the code when something breaks) for "spacer" or "whitespace" would help someone avoid needing to step through what's being removed in Dev Tools. Minimally invasive, would greatly help someone doing code review, and doesn't even increase line count. Documenting the helper methods at the top would help newcomers to the codebase as well.More generally, going a step further, I've been inspired in my commenting style by Jeremy Ashkenas's literate code for the Backbone library. See, for instance, http://backbonejs.org/docs/backbone.html#section-111 . The code itself is as self-explanatory as any other code I've ever seen, but the comments serve to accelerate that browsing process. Backbone, while somewhat dated nowadays, is a great level of abstraction in that the entire library can be grokked in an afternoon - but without comments, I think it would take longer. Reading time does not monotonically increase with number of characters :)
And as a third point, if there's ever something confusing enough, or you're using some infrequently-used feature of the CSS/DOM spec, such that the author or reviewer needed to look up how it worked in documentation or a forum somewhere, I encourage my team to simply add that documentation or forum link into the comments. Extremely helpful for maintainability, particularly when onboarding junior team members, allows code reviews of that line to be asynchronous whereas they would otherwise require synchronous explanation, and it's ideally just as quick to keystroke "select-url/copy/close-documentation-tab/tab-into-codebase/start-comment/paste/enter" as it is to "close-documentation-tab/tab-into-codebase."
This is Hacker News - we're the antithesis of enforced Javadoc-style comments :) But if you consider someone needing to read code without having the live input of the author, minimal comments for especially terse or creative code can quickly start to make sense, certainly in the territory of the rightmost columns of https://xkcd.com/1205/
It's terse and effective, I suppose, but it was really interesting measuring pixels to judge comment nesting.
The JS itself is fine. It's the markup that's a mess.
- All functions
- The functions are simple and decomposed into smaller functions
- No usages of "this" (except one necessary one in an event handler)
- No ham fisted attempts at doing OOP with JS
Only complaints really are naming and code style is overly compact which would potentially make it harder to understand, but in this context I think that is ok. There is nothing overly complex going on here. The code seems reasonably easy to understand.
If there are criticism I'd like to hear them.
new Image().src = el.href;
Where href looks like this: vote?id=xxxxxxxx&how=up&auth=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&goto=item%3Fid%3Dzzzzzzzz#wwwwwwwwXMLHttpRequest has been around for about 7 years then and using it was called Ajax for about 2 years already.
This is used for write-only GET requests (with graceful fallback when JS is disabled since those are plain anchor elements).
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.
Edit: I'd be more worried about repeated calls - but presumably the service being called is idempotent.
https://stackoverflow.com/questions/705782/why-shouldnt-data...
Also not a fan of unnecessarily cryptic variable names.
You mean you can just read the function definition to see what it does?
function byClass (el, cl) { return el ? el.getElementsByClassName(cl) : [] }
const byClass = (el, cl) => el ? el.getElementsByClassName(cl) : []
This is used for readability and to avoid hoisting that can be confusing.
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.
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.
(And yes, in both Ruby and Rust not literally everything is an expression, but almost everything is.)
> A function consists of a block, along with a name and a set of parameters.
https://doc.rust-lang.org/reference/expressions/block-expr.h...
> Blocks are always value expressions and evaluate the last expression in value expression context.
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.
In fact, using the online babel transpiler, it transpiles the es6 version you provided precisely into hn's version:
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.
Makes supporting older browsers a lot easier
It's still bad code. Using newer syntax to do the same thing didn't actually improve anything.
-sun tzu
How does this follow from my statement?
I've written uglier code and gotten paid for it. ¯\_(ツ)_/¯
There are a few arguments in this thread about poor maintainability of this code, when coupled to the HTML it affects (which always must be considered, otherwise this code is pointless), and I tend to agree with them.
Personally, I wouldn't mind some less terse names and some more semantic groupings of HTML nodes.
Thankfully, it is simple. I'm pretty sure doing that would be the task of an afternoon, though I would definitely want to accomplish that before trying to change anything else on the page.
This is Hacker News... there's a good chance it will go more or less unchanged for a while.
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.
function hidestory (ev, el, id) {
for (var i=0; i < 3; i++) {
That 3 there is the problem with “simple” JS. It’s tightly coupled to the HTML but they live in completely different places.On a side note this 3 should be at least assigned to a variable with a meaningful name.
The time you spent writing this comment was longer than what it took writing this code. Considering what kind of site this is, that code may work indefinitely. Or, if necessary, someone from the future will need to change that 3 to a 4 at some point. In terms of total cost, it's almost certainly the cheapest solution.
Thing is simple, unreadable, and it works. Turns out, code can both work and be shit at the same time. We've all done it.
Looks a lot like "read-only" web code I've encountered on projects, where waves of people decided it's easier to work around what's already there than amend it (and that's also how you end up with 20,000 line CSS files).
> 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.
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.
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....
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.
The original JS was inlined and contained two functions: hide and vote.
var on = !afind($(id), collapsed());
(on ? addClass : remClass)($(id), 'coll');
where function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
where function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
where function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }The best programming advice I ever got was to write code for other people to read (and understand at a glance), not for the machine to execute.
This code is the precise opposite of that.
It is a mistake to assume a single standard here. Does a German speaker find a Sanskrit text readable, or a trombone player find a guitar piece playable?
I agree it's not a simple glance to figure out what it does, however it is not unreadable.
But how about what it does? Has anyone ever tried to collapse or expand a 100+ comment thread here? It can lag for 1-2s. So all the talk of "it's simple code that serves its purpose" is not really true.
That said, please don't solve my complaint by adding a bunch of SPA stuff to HN. I love how fast the base content loads here and how there's nothing dynamic.
function onready () {
recoll();
}
document.addEventListener("DOMContentLoaded", onready);
Just attach recoll() directly. There's no benefit of going via onready().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.
Some examples:
The functions hasClass, addClass, and remClass could be replaced by Element.classList [0].
Another one is remEl, which can be replaced with a direct call to ChildNode.remove() [1].
I was going to give more examples, but I don't feel like rewriting the whole script.
[0] https://developer.mozilla.org/en-US/docs/Web/API/Element/cla...
[1] https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/r...
function aeach (fn, a) { return Array.prototype.forEach.call(a, fn) }
Does this win anything over using forEach directly?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.
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].
On the contrary, it's not what I'd do at work for one of our bigger web apps.
No need to criticize, it does what it's supposed to.
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.
It does what it's designed to do, and fills a specific need for one website. It's not there as a teaching aid, nor is it meant to be shared for other people to use elsewhere.
Not everything has to be gold-standard code full of perfect variable names, extensive comments and good whitespacing. If you have a day job that isn't primarily writing code, and/or you are likely to be the only person ever working on that code, who cares if it's not up to the standard that people around here seem to expect of every project?
As long as the code works, anything else is just gravy. If you were to peel back the layers on all the major websites out there, I'm sure you'd find less than stellar code everywhere.
Future you that has to decipher the code. Why make it hard on yourself? Software is a living thing eventually a decision will need to be made about it and without understanding what it does it's easier to make suboptimal assumptions.
To the defense of the title, in my experience, whenever label "hacker" was used on programmer or code, it meant "difficult to read, full of hard to maintain shortcuts and tricks" kind of code. Really always, I don't ever remember it to be used in any other sense.
function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }
Could turn into something like this: const addClass = (el, cl) => el.classList.add(cl)
Not only is is shorter, but it's more readable too!My point was that using a "more modern standard" would break hn for people using legacy browsers.
And won't work on IE. Not just because of el.classList, but also because of the arrow notation. So by doing this change, you either have to ditch one of still used browsers, or introduce a stupidly complex transpilation chain into the mix.
> Any effort in trying to bring it up to modern webdev standards would likely make the code expand significantly
That's just not true, and nowhere did that person say: "Because of legacy IE support requirements", they only mentioned "modern webdev standards".
Are you suggesting arrow functions, and classList aren't modern standards then?
It costs almost nothing more to write "event" instead of "ev" or "removeElement" instead of "remEl", but it makes the code much more readable. You might gain 1s for writting remEl instead of removeElement but you will loose more seconds in the future trying to remember what remEl stands for, or having to check what abbreviation you chose for that function.
It's just something programmers learn with experience (even when working alone) : abbreviations are a false good idea, and people are just pointing that out.
No, it doesn't. Unless you're an absolute beginner, it takes you a couple of seconds to realize that in this codebase, "ev" (or "evt" or even "e") means "event". The same goes for "remEl". If that's consistent, then it's not a big deal at all.
Having less characters makes code more readable (or rather "scannable") as well, it's just another tradeoff.
Only to a point, otherwise minified js would be more legible than plain source. It takes "a couple of seconds" to scan the codebase and find out what el or ev refer to, but it would take zero seconds if they were just called "event" or "element."[0] There's no gain in readability from shaving off one or two characters in that case. It just "feels" more efficient because it resembles the common style of lower level code.
[0]Assuming those aren't reserved words in javascript, I don't know.
I think it's generally frowned upon, though. For example, I believe "event" is defined as a global in browsers (on window, perhaps?), so you run the risk of shadowed variables and accidentally referring to the wrong thing.
Okay so perhaps a couple of seconds doesn't matter. How many seconds does?
However renaming "important" things to make it quicker to read or type is, in my experience, a mistake if your code base is more than just a handful of files. Descriptive names make it much easier to understand the code which is more important in my experience. As the number of code files gets beyond a few files any benefits you get are rapidly lost because now you need to keep all that knowledge in your head and start having to jump around between 5, 10, 20 or more files to work out what everything is let alone what it is doing. This is why naming stuff in code is so important (and sometimes so hard).
Golang has a good philosophy on this front: https://github.com/golang/go/wiki/CodeReviewComments#variabl... tl-dr: the further from its declaration that a name is used, the more descriptive it should be.
This sort of attitude about brevity & speed appears to be a common problem with some developers I come across - they are absolutely obsessed with "productivity" when it comes to writing code. They've just got to have their 97 plugins/extensions to their editor and they simply must have their finely-honed emacs keybindings. If you are focusing on just churning out code as fast as humanly possible (i.e. "productivity"), then your job as a programmer can probably be safely replaced with a couple of shell-scripts.
I've never been in a situation where the limiting factor in programming has been how fast I can read it or write it - the limiting factor has always been understanding the problem at hand, and designing a solution that solves the problem and is maintainable.
Based on a Fortran convention, IIRC. Within a web setting, "el" and "ev" are extremely conventional.
Interestingly, this results in more or less the opposite of entropy coding
Abbreviating "element" to "el" or using "attr" instead of "attribute" (etc.) can significantly reduce noise in web client code. Everybody either knows what it means, or they shouldn't be editing the code in the first place.
It might, in a specific circumstance, while in another circumstance it might make the code significantly less readable. That is an indication that number of characters is probably not a good metric for adjusting readability.
However, I've also seen large classes with members like "stN" for "set N". WTF is N, and why can't we just type set, that one char doesn't save anybody any time at all. Or nested loops with i & j reversed, just to make it especially painful.
I'll try to incorporate this in my next job interview :)
> As long as the code works, anything else is just gravy.
How did you end up with this opinion? That's an unpopular opinion even when considering the qualifiers you've listed in the paragraph above.
Nonsense. Maintainability almost always matters.
As the adage goes: code shouldn't merely work, it should clearly work.
> If you were to peel back the layers on all the major websites out there, I'm sure you'd find less than stellar code everywhere.
Indeed, but Sturgeon's Law shouldn't make us feel better.
The HN code is meant to be understood as a self-contained piece, and that's contextually very different from most code out there.
The last three large corporations I worked for never cared about maintainability because the application or portal would only be in use for maybe a year or 18 months before a complete rewrite or total redesign.
All of the recent projects I've been on take the same approach. Get it stood up, make it look pretty and release it. Because agile development makes business owners believe it only takes a few days to build Rome now, maintainability is never a consideration on any of the projects I've been on. Even senior devs are saying, "It's pretty hacky to get this to work, but it works" is a common troupe on our teams. I agree maintainability should be important, but on the agile projects I've been on, nobody cares about it.
To some degree I think executives do it on purpose so they can create more work for themselves in the future. They can pick it apart, ask for a bigger budget, hire more devs and use some shiny new technology to build it over again. Rinse, repeat.
The first significant web app that I wrote had a similar approach. I was a new developer, but I had a good idea and they hired a manager and a couple of other developers to flesh it out. No need to hire a senior dev, because it was just a POC. We weren't even part of IT, so anything we built would of course only be in use for a few months before IT would allocated "real" developers to rewrite it.
Four years after I left that job, I got a call one day that the site was down and they couldn't fix it. After ten minutes on the phone with a former colleague reading me the logs out loud while I read through an old copy of the code that I (thankfully) still had on an archived drive, it turns out that the LDAP server's address had changed. We updated that and it came back.
Out of curiosity, I had them run `uptime` on that box. It had been up for six years - the two years I was there, and the four since I'd left. Not only had the application continued to function and be used... the box it was running on hadn't been updated restarted since it was built. It hadn't gotten updates since I'd left either.
The point of this story is simple: there is no such thing as code where maintainability doesn't matter, because there is no such thing as "temporary" code. Code can get retired or refactored away, sure, but there is absolutely no way to tell ahead of time if any given code snippet falls into that category.
Why on earth is this such a popular idea? Do customers somehow prefer to see all the buttons in new places and old urls broken every other year?
Now? I think newer technologies come out faster and capture people's attention. They want to use the new shiny thing. I think the "business people" want to have a big budget project to get recognition within large orgs. I think developers want to use the latest and coolest stuff. I think people feel like we already live in a disposable culture, why would our sites and apps be any different? You combine all of these and suddenly the pressure and inertia not to move or rebuild or redesign regularly is too much to overcome.
I still remember going to a ReactJS class and the guy running it kept saying, "Don't get me wrong, BackboneJS is still a hell of a library and is still relevant and awesome to build stuff with BUT React does a few things better."
This is where we are. Huge financial investments, time and energy to get a 3-5% bump in efficiency? Doesn't make sense to me.
From the top down (strategic), and from the bottom up (tactical).
Depending on your perspective, you will likely fixate on different things.
If you're starting from the bottom, you'll probably be more interested in the coding choices made by the developer.
If you're coming from the top, you'll probably look at the goals of the site and whether the code met them, and then consider other details like optimization and cleanliness to be secondary ('gravy').
When I was in biz-school in the 90s, we used to spend a lot of time talking about effectiveness (the end result does what it needs to do) and efficiency (the end result does what it needs to do and is optimized for -insert criteria here-).
Ideally, you get to be both effective and efficient. If, however, you can only be one, it's better to be effective.
“It works” is one of the lowest imaginable quality bars for software to meet, one notch up from “It compiles.” Through my career I’ve worked with way, way too much code where the developer stopped at step one (it works).
It will be tagged javascript and jquery. =(
Any suggestions for me as a shitty semicolonist?
I prefer Standard JS, but either way it's easier to not need to think about the exact details IMO :)
I just found it weird that they were seemingly arbitrarily used here on occassion.
Consider this code and the range of comments on it while doing (or undergoing) your next non-trivial code review.
(For one thing, it reminds me that a great value of adopting comprehensive code standards is that it resolves these kinds arguments, letting teams get on with the important stuff.)
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...
Yes!
new Image().src = el.href;
will make image request to server with any params from href so you can process the voting stuff. Nice one. - minimal functionality
- rarely if ever changes
- is maintained by a very small group (Hacker News)
...then something like the example linked is perfect. Most web app developers don't live in this world. The more common situation is: - large and/or transient teams
- large quantity of inter-dependent features
- constant changes
...which means that the thing you need to optimize for isn't pretty/fast code. It's clarity and resiliency in the code. Can a junior and senior dev both work on the same code base? Can someone new to the project be effective with minimal ramp up time? Can you work on a feature without accidentally stepping on another persons current task?Modern web apps are less about being performant and minimal, and more about dealing with the complexities of large software teams
If that's true, then the app will likely have what I call a "programmer's interface" (which is especially common in most enterprise web apps I encounter). Those apps might be solid from a code perspective, but don't tend to be very user friendly.
I tend to think that modern web apps are more about dealing with the complexities of balancing functionality with ease of use to the end user while still dealing with the complexities of large product teams, which include but are not limited to designers, UX experts and developers.
I can't be the only person who finds browser default behavior more intuitive and usable in general.
Why does "UX" so often mean mouse-heavy and always favor the beginner over the power-user.
I'd say apps skew towards mouse-heavy because controls are on-screen and therefore discoverable. It takes a good deal of product-market fit before you can count on your users knowing/caring enough about your app to remember keyboard shortcuts.
Most apps are grown by adding users, so optimizing for the new user is more important to the business until a certain level of maturity and market saturation is reached.
Applications with a dedicated professional user base can go deeper into power-user territory sooner since they can charge more per user, and new users expect there to be a learning curve.
In a small, simple, established site, those needs don't change often, so the code doesn't change often, and you can focus more on performance than maintainability, as well as spend your efforts on non-coding tasks.
In a startup when you are finding your market and seeking product/market fit, and you've got investors demanding speedy delivery of a product to customers, the attempted solution is often a large team with fast and numerous code changes. And then you do have to optimize for the devs, because even though the final goal is customer satisfaction, devs need to perform efficiently to hit that goal.
The needs of the codebase still tie back to the needs of the business. One answer does not fit all.
Really? Most Javascript developers live in the world of trillion-dollar startups serving billions of people? That's why they need Kubernetes, React, and 2000 nodejs modules to show a couple of confirmation dialogs on the SOHO website they are working on.
Nevertheless, the code looks kinda beautiful.
My opinion of the code is this.
It's pretty tight, which I can respect.
I haven't run it, but it PROBABLY works.
But it's hard to read.
Things are named poorly.
Many edge cases are missed (not checking args properly).
These functions are not pure and therefore, harder to test.
Maybe true 1337 H@X0Rz don't need to write tests, but some of us have mortgages to pay...
I didn't re-write all of this mess, but check out the few util functions that I re-wrote.
As a developer, which version would you rather work with?
If you said the original version that's cool with me.
But don't try to come work on my team.
// isNull :: a -> Boolean
const isNull = x => x == null
// isNullOrEmpty :: String -> Boolean
const isNullOrEmpty = string => string == null || !string.length
// $ :: String -> HTMLDocument -> HTMLElement
const $ = (id, _document = document) => {
if (isNullOrEmpty(id)) return null
if (isNull(_document)) return null
return _document.getElementById(id) || null
}
// findClassInElement :: HTMLElement -> String -> [HTMLElement]
const findClassInElement = (el, className) => {
if (isNull(el)) return []
if (isNullOrEmpty(tagName)) return []
return el.getElementsByClassName(className) || []
}
// findTagInElement :: HTMLElement -> String -> [HTMLElement]
const findTagInElement = (el, tagName) => {
if (isNull(el)) return []
if (isNullOrEmpty(tagName)) return []
return el.getElementsByTagName(tagName) || []
}
// findClass :: String -> HTMLDocument -> [HTMLElement]
const findClass = (className, _document = document) => {
if (isNullOrEmpty(className)) return []
if (isNull(_document)) return []
return findClassInElement(_document, className) || []
}
// elementHasClass :: HTMLElement -> String -> Boolean
const elementHasClass = (el, className) => {
if (isNull(el)) return false
if (isNullOrEmpty(className)) return false
return el.className.includes(className)
}
// addClass :: HTMLElement -> String -> Boolean
const addClass = (el, className) => {
if (isNull(el) || isNull(el.className)) return false
if (isNullOrEmpty(className)) return false
if (el.className.includes(className)) return true
el.className = `${el.className} ${className}`
return true
}I don't see the value, for this use case, of all those checks. We know what the input values are, essentially what's in the DOM that we 100% control.
I feel ES6 syntax much less readable than ES5 (probably because I have worked more in ES5 than ES6), it introduces a bunch of new symbols.
How can this
const isNull = x => x == null
Be more readable than function isNull (x) { return x == null}
Another example, const addClass = (el, className) => {
}
I always have to remember what this construct means and translate / unroll it in my mind to a regular function.Also, why would an addClass return a boolean? Doesn't make much sense.
// $ :: String -> HTMLDocument -> HTMLElement
const $ = (id, _document = document) => {
if (isNullOrEmpty(id)) return null
if (isNull(_document)) return null
return _document.getElementById(id) || null
}
How can above be more readable than function $(id) { return document.getElementById(id); }
I bet I can give the original code for Python/C/Java developers and they would understand / change it easily.That said, I consider myself a backend / devops person who sometimes needs to do work in the frontend, the biggest project I did was a medium size app with around 40 routes that I used react, es5 and bootstrap.
Big fan of Go and it's very readable / limited syntax.
Beyond the code size savings, I find the newer standard methods are generally easier to understand when scanning code.
let $ = (selector, scope=document) => {
return scope.querySelector(selector);
};
let $$ = (selector, scope=document) => {
return Array.from(scope.querySelectorAll(selector));
}; const node = (tag = 'div', attributes = {}, inner) => {
const e = document.createElement(tag);
for(const [key, value] of Object.entries(atrributes))
e.setAttribute(key, value);
if(inner) e.innerHTML = inner;
return e;
};
The premium version contains an additional: e.child = (a,b,c) => { const f = node(a,b,c); e.appendChild(f); return f; };BEFORE
function vis(el, on) { if (el) { on ? remClass(el, 'nosee') : addClass(el, 'nosee') } }
AFTER
function vis(el, on) { if (el) { window[on ? 'remClass' : 'addClass'](el, 'nosee') } }
...sadly we have to add window when using the square brackets so it's not much shorter. Oh well.
function vis(el, on) { if (el) { (on ? remClass : addClass)(el, 'nosee') } }
?
this[expr ? 'method1' : 'method2'](args)
So I have to add "this" as the methods are not global. Now if I overlooked something again I'm definitely getting rusty :) function $(id) { return document.getElementById(id); }
Nice! Now I don't need jQuery.https://gist.github.com/paulirish/12fb951a8b893a454b32
It let's you do
$('p').on('click', el => /* ... */)
which is handy for smaller scripts :)I think maintainability was not a first class concern in the writing of this script (consider the terse naming of variables), but it doesn't really matter; it's short, and you can understand it all because there's not all that much to understand. It does the job, and works everywhere.
var is pretty much just for legacy codes. Yes, it has unique behaviors, but better stick to the new keywords, can prevent some headaches.
And yeah, this playful attitude to "just do it" tends to goes away when people become adults.
So in my book, it is better to grow down!
I think it's important to understand both contexts (and practice them!).
[1]: https://code.jquery.com/jquery-3.3.1.min.js [2]: https://code.jquery.com/jquery-3.3.1.slim.min.js
It's also incredibly readable; I'm puzzled as to what the bar for "readability" is if this doesn't meet 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?'.
But it doesn't toggle visibility. It sets visibility.
A problem that wouldn't exist if it was named correctly in the first place ;)
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' ...
This is especially true with modern IDEs that autocomplete everything.
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.
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
I'd say I'm going to copy them next time but there is no license header...
const $ = (sel, elem = document) => [ ...elem.querySelectorAll(sel) ]
is nicer as you can use CSS selectors.