Hacker News new | past | comments | ask | show | jobs | submit login
Robust Client-Side JavaScript (2017) (molily.de)
76 points by tosh on June 24, 2020 | hide | past | favorite | 28 comments



This is a great reminder of a lot of tools, and the closing thoughts are excellent:

"Still, JavaScript is the most brittle of all front-end web technologies. An important skill of a front-end developer is to know when not to solve a problem with client-side JavaScript. It is always more robust to solve a problem further down in the stack."

Also, maybe the title here should include (2017)?

> Published on December 21, 2017.


> "Still, JavaScript is the most brittle of all front-end web technologies. An important skill of a front-end developer is to know when not to solve a problem with client-side JavaScript. It is always more robust to solve a problem further down in the stack."

Underrated comment.

After doing full stack development for a couple of years now, my anecdotal experience has taught me that front end JS is (usually) the more labor intensive and nuanced of the two stacks (client and server), and that the majority of defects are UI (that is, JS) related.

That's not to say JS sucks or is a sub optimal solution for its use potentials use case. For the use case of the browser, its the only sensible choice.

When in doubt, move logic to the back end - less JS is always better.


> Underrated comment

I think it's an overrated comment.

Sometimes you just want a simple API that all of your clients talk to: your iOS client, your Android client, your Windows client, your TV client, and your browser client (Javascript) is just another client that consumes the same API, not a special-cased frankenclient that's half-rendered on the server.

Or maybe all you have is a browser client and you still don't want it to be this frankenclient thing. And instead you want the separation of concerns instead of doing some UI things on the server and some UI things in the browser.

A rule of thumb like "do as much as you can on the server" just sounds like cargo cult mysticism in place of putting on an engineer / product-designer hat and weighing the pros and cons.


I think the fundamental problem I find when architecting a new website is that not enough thought has been put into making HTML and CSS sites scale with complexity.

It feels like we put all of our eggs in the JS basket - we know how to factor out well-scoped components, encapsulate styles at the component level, and build reactive, state-driven interfaces, all from JS.

There are certainly patterns that allow for componentization, encapsulation, and state management in pure HTML and CSS. None of them that I have used (pure WebComponents, SSR templating a la Pug or Rails/Laravel/etc, the "good ol' days" of "simple" single-file HTML/CSS) have felt like they scale beyond a basic document with link-based navigation.

As soon as you introduce forms, real-time updating, or layered interface state (modals, drop-downs, etc) you start spiraling into progressively more and more kludgy orchestration scripts until you end up wishing you'd just built the damn thing in React to start with.

If there was a well-defined set of HTML and CSS patterns that you could start your website with and _always_ feel like you could add additional functionality without compromising the existing structure, I'd migrate to that in a second.

As it is, the best way I can see for accomplishing a web project where I don't feel boxed in as soon as the requirements change is to just eat the few days of setup complexity up front and use a well-supported JS-based component framework.

There are all sorts of terrible downsides to that approach, as have been extolled ad nauseum here and elsewhere, but I legitimately don't see another way to do it. It's easy to say "for a simple static site, just use plain HTML/CSS!", which is fine if there's a clear scope that won't expand.

The second you introduce uncertainty and possible future complexity, I don't see a better alternative to a current JS-favored stack.

That's hugely compounded by all of the zillion QOL things that you need to worry about as you account for complexity - time to first paint, responsiveness across screen sizes, proper eventing, cross browser support, memory leaks, jank-free animation, page weight, internationalization, etc etc etc. One layer of the stack has an answer to most of these concerns, but it's very difficult to orchestrate all of them without ending up with a lot of JS that needs to be managed.


Nice to also read this sentiment, I just go all in on client-side code (react-redux, redux-forms, css-in-js, next.js) and once it's set up and working you can really iterate quickly and it all scales.

I find myself annoyed by how trendy it is to crap on all of the progress that has been made on the client-side stack... basically it seems like a lot of the lessons we have learned over the years are being forgotten. "Embrace the C in CSS!" Like, no! That C is what lead to the most painful CSS refactor and cost 6 months of my life. "Seperation of concerns!" It sounds good to say HTML and CSS should be separate, but we found how powerful it is to co-locate a component's styling with the component. People underestimate how much styling is just inline styles. "Wait, what do I name this class that is just applying margin-top?" If you have a global bin of css utilities and theme variables, everything else is just inline on the component.


How do forms require Javascript in your view?

My experience is that most of the Javascript used for forms is for fancy custom controls (where a standard one would be fine), weird editing flows (like click to edit), or other functionality which isn't actually needed.

That's not to say that JS with forms is evil. HTML validation is better than nothing, but complex data requirements may require custom validation logic in JS as a convenience to the user.


JavaScript is a requirement on most forms due to either designer or client expectation. They want styled, custom client side validation. They want a visual loading state while the form is being fetch'd to the server/service. They want a nice success message to display inline once the submission is complete. Rarely is it custom controls or weird editing flows in my experience - it's much simpler than that.

And we do it because they're paying the bills - there are more important hills to die on.


Validation, datepickers, multi-select, other complex controls, dynamic forms (think "add row"), accessibility. You can definitely get away with simple forms in pure HTML, but again it puts a hard limit on the complexity you can support.


Examples where some JS has its place for forms:

1. The browser already gives you <input type="number"> or <input type="date">, but you may also need <input type="credit-card-number"> or <input type="expiration-date">. For credit card numbers (or bank account numbers) specifically, you want the input field to break it up into groups of 4 digits similar to how it's printed on the card, to make it easier for the user to double-check their input.

2. Suppose you're in the checkout workflow of a webshop. While choosing a shipping method and destination, I want to see the final price update to reflect my selection immediately. I don't want to flip back and forth between selecting a shipping method and seeing the actual cost on the final confirmation screen.


> Still, JavaScript is the most brittle of all front-end web technologies.

I don't think "brittle" is the right word. I get what it's going for, but thinking back to the jquery days where the javascript was driven by events fired from the DOM instead of a framework, it was actually really robust - when something went wrong, instead of the whole page crashing, you'd end up with just one feature not working while everything else was fine.


I think the author is referring to things on the client that are outside the developers control, like network issues, script blockers, origin restrictions, firewalls/badly behaved proxy servers, outdated browsers etc.


For error logging (https://molily.de/robust-javascript/#error-logging in the article), can anyone recommend a logging service that has a small JavaScript footprint?

I use Sentry on one site but it has a 56K library you have to load before any of your JavaScript loads, so bad for page size + bad for page blocking. I would have thought they'd at least let you use a shim to capture errors quickly while you defer loading the full library (like how Google Analytics does it) but I can't see an obvious way to do that.

Sentry has really nice features but 56K (17K compressed) is a big chunk when I'm trying to keep a page to about 100K transferred.


There are many ways the browser can fail. I'm sure their SDK is large because of all the random hacks they need to use to capture everything from every browser. Couple this with the need to create first-class API, which adds some code bloat, and I can see how this ends up at 17K compressed.


Why can't they do the shim approach while the library loads though?

Google Analytics for example lets you push everything you want to record into a plain global array called "ga", and when the library loads, everything in the array in processed by the library. This lets you start capturing events without waiting for the library to load.


it's a bit different though, you can't "push" errors somewhere, I guess so many error handlers and interceptors are set that it's not worth to split.


> you can't "push" errors somewhere

Why? Can't you push the Error object or stack trace into an array to await processing?


What if the thing that is pushing the error object has an error?


I think it's more to get as much context as possible. One of our older products just sets window.onerror [0] to a small function that sends the information back to the server, which then forwards it to Sentry. The information in Sentry is nowhere near as detailed as if it came from Sentry's library, but it's been enough for us and doesn't seem to miss anything.

[0] https://developer.mozilla.org/en-US/docs/Web/API/GlobalEvent...


I guess it depends what you mean by error logging specifically.

I added a pretty barebones “report js errors to the backend” script in about 1.1k uncompressed/unminified.

It’s possible it doesn’t catch all errors I guess but I can’t imagine what 56K of js just to catch and reports is doing.


Having read that section you linked now, ours is similar to his “not enough” example.

I’d say probably 99% of the error reports we see from it are a third party js script not loading (Sometimes a network issue, sometimes a plugin blocks them) we’ve pared third party loads down to just the payment gateways, but they’re still the vast majority of reports we see).

However it’s also not js heavy, so that type of error logging solution works well for us.


I use sentry but call the API directly. So my overhead is basically try-catch with a fetch sending the data in the catch.

I don't get sourcemap mapping, but I can send along enough metadata to find the issue in most cases,


> Do not take anything for granted. Do not count on anything. Question your beliefs.

One thing that makes it much harder to question your mental model: Not having one.

Confusion is bad.

I think some developers shy away from learning to build robust or accessible sites because getting something to work at all makes them feel like they are fighting against Venetian blinds[1]. By "some developers", I mean me. One key to overcoming this is the self-confidence that when someone says you are "overthinking things", "trying to understand the universe", or "just experiencing impostor syndrome" you can (silently, in a message you never send) tell them to pound sand.

If sitting down with a book[2] or other guide to frontend development helps you see a picture of how things work, be willing to give yourself that.

If writing automated tests helps you to see how things work, be willing to give yourself that.

[1] There are people who interpret it as an insult when frontend devs post https://www.youtube.com/watch?v=-pzckbNyqfc, but it is a real expression of individual experience.

[2] http://book.mixu.net/css/


Too bad, I was hoping for a discussion of state reconciliation strategies between front and back end. Anyone know where to find the conversation on the state of the art?


you wanted that from something titled "Robust Client-Side JavaScript"?

i somewhat work in state reconciliation. what do you want to know? i dont think its progressed a whole lot beyond the "your frontend is a part of your distributed system so CAP theorem applies"


A large part of the domain is synchronisation and determining what even needs to be reconciled.

Even at that level, pretty much everyone’s been inventing their own thing since time immemorial.

I am aware of only one attempt to standardise anything in this general area (synchronisation in web tech): JMAP, now published as RFC 8620. JMAP is an object synchronisation protocol based on web tech. Its prime motivating example has been the email domain, to provide a replacement for IMAP, and that data model is published as RFC 8621; but JMAP is perfectly generic and applicable to other data models (which is why the two RFCs are separate documents).

JMAP doesn’t say anything about actual reconciliation; if it’s possible you may need it, deciding how to handle it is left up to you.

I reckon JMAP is the state of the art, though the ecosystem is decidedly immature. But before listening to me, know that I’m biased by having been a Fastmail employee, working on the frontend. (And having drunk the kool-aid, I intend to use JMAP as the primary API on various other personal projects that I have in mind, even if I also expose other less-complicated and less-capable APIs for others that want them.)

Depending on what you wanted to count, you could also include the CouchDB Replication Protocol. It’s decidedly robust, with a different focus to JMAP and overall more opinions, which could be good or bad depending on your perspective and situation.

(And returning to what you were originally asking, CouchDB has its own state reconciliation in case of diverging edits. Kind of, anyway. It’s essentially “pick one, deterministically, and leave the revisions around so that software can do something more nuanced afterwards if it really wants to”.)


I think the best we have is still using GraphQL on the Server, with a client side library like Apollo or Artemis (Clojurescript)


React + Apollo + TypeScript syntax took me a bit of time to get accustomed to after primarily working with JavaScript and REST APIs, but once I fully grokked it I decided I will never go back.


An excellent book that will be understood by beginners and teaches all the important things, written in a clear and simple language without any shenanigans. Great work!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: