There's a lot of good advice here, but also some misinformation.
First of all, a script tag will block DOM construction, but in any sane modern browser will not block loading of subresources like stylesheets, because browsers speculatively parse the HTML and kick off those loads even while they're waiting for the script. So the advice to put CSS before JS is not necessarily good advice. In fact, if your script is not async it's actively _bad_ advice because while the CSS load will start even before the JS has loaded, the JS will NOT run until the CSS has loaded. So if you put your CSS link before your JS link and the JS is not async, the running of the JS will be blocked on the load of the CSS. If you revers the order, then the JS will run as soon as it's loaded, and the CSS will be loading in parallel with all of that anyway.
Second, making your script async will help with some things (like DOMContentLoaded firing earlier and perhaps getting something up in front of the user), but can hurt with other things (time to load event firing and getting the things the user will actually see up), because it can cause the browser to lay out the page once, and then lay it out _again_ when the script runs and messes with the DOM. So whether it makes sense to make a script async really depends on what the script does. If it's just loading a bunch of not-really-used library code, that's one thing, but if it modifies the page content that's a very different situation.
Third, the last bullet point about using DomContentLoaded instead of $(document).ready() makes no sense, at least for jQuery. jQuery fires $(document).ready() stuff off the DomContentLoaded event.
The key thing for making pages faster from my point of view is somewhat hidden in the article, but it's this:
> This isn't even that much JavaScript in web terms - 37kb gzipped.
Just send less script. A lot less. The less script you're sending, the less likely it is that your script is doing something dumb to make things slow.
[Disclaimer: I'm a browser engine developer, not a frontend or full-stack web developer.]
> making your script async … can hurt with other things
I can't agree. It will likely regress window.load time, but that's a bad metric. Additionally, the reflow cost post-execute will be <100ms, but the delay to the paint caused by a sync script worth 100-5000ms. In very rare occasions would it be preferable to show a white page to a user longer to avoid the DOM change costs.
> DomContentLoaded
Agree with the above. Additionally, I believe all browsers wait for DCL handlers to finish loading before painting (for the common case). But, most pages have ALOT of code attached to DCL. If the handlers are not critical for the first paint (they shouldn't be), then the easy fix would probably be
document.on('DOMContentLoaded', e => { requestAnimationFrame(function(){ /* your doc.ready stuff */ })});
That way you get a paint to the user before your piles of DCL code runs.