How to Build Resilient JavaScript UIs
smashingmagazine.com
smashingmagazine.com
if (
typeof stripe === "object" &&
typeof stripe.handleCardPayment === "function"
) {
stripe.handleCardPayment(/\* ... \*/);
} else {
logger.capture(
"[Payment] Card charge — Unable to fulfill card payment
because stripe.handleCardPayment was unavailable"
);
}"
This is silly. With modern JS tooling you should never be worried if a function exists; that should be caught at compile time.Although I'm a huge fan of TypeScript and use it and a bundle in all my UIs, you cannot guarantee that everything was typed correctly or that things haven't escaped being typed.
Sometimes I wonder if the right thing is to actually guard every object from being undefined like this sample. It does seem crazy.
This is basically a problem for every language with metaprogramming/eval/LD_PRELOAD: you can't necessarily truly count on things being defined like you think they are. That also starts getting pretty paranoid but again the topic here is resilience so should you ignore that things can/actually do change under the hood when you don't expect?
This is an excellent example of necessary error-checking stuff; user-agent feature detection (which includes the browser’s baseline functionality which is basically a completely uncontrolled third-party library, the worst situation by far) is another good example. But it’s extremely easy to take it too far. Any library stuff that gets bundled into your code, you should prefer compile-time to runtime type-checking. But anything outside, yeah, then you have to decide.
If it's your own code that does no IO then yeah it's more ok to trust TypeScript.
Same with user input code. You can trust your implementation to the extent of static type checking and analysis. You can't trust anything a user entered into your system. Those are examples of the externals that I stated you can't trust.
At the end of the day, Javascript is still a dynamic language where you might be loading dependencies from outside your build chain. It's up to you to decide how defensive/safe you want to be I guess. I don't think there's a wrong answer.
I dare you to connect to a real time api and rely on your type checking, it ain’t gonna happen.
Tooling is a crutch. We should be teaching more of the above, not less.
`if (stripe && stripe.handleCardPayment)`
(I know the behaviour isn't identical, but checking the actual type is pointless in this case)
(This is assuming a browser environment, where `window` is roughly the global object; see also `globalThis`, especially for further reading on how unfortunately complicated the whole situation is.)
[1] Second example here: https://www.smashingmagazine.com/2021/08/build-resilient-jav...
Imports and Webpack. I cannot imagine externally loading a payment SDK like that in this day and age.
They can make whatever updates they want to the API and publish a new NPM version. Package management exists for a reason. Including scripts on a page like this for web development is simply anachronistic and wrong in 2021. Maybe for just throwing something into a blog site, whatever fine. But it's just not an acceptable practice for application development anymore.
While I think that's fine for normal software Stripe lives in the world of financial regulation and compliance with laws around the world. They might not be able to even allow their users to use an older SDK (regulation changed, new regulatory steps, etc) in a graceful manner.
Sticking to live loaded sdk and the technical debt that comes with to not break clients and end users might just be the best option for something as risky as payments online.
I don’t want to be that guy, but there’s no way you built anything real.
‘hey guys, I totally set the type of the api response to String, so it’s always going to return a string, right right?’.
Wait till all your compile time type checks don’t mean jack shit on prod when multiple endpoints just do something unexpected. Now you have a UI that didn’t gracefully handle any of it, and have fun extolling the virtues of type checking to the business — ‘but it says right here, it’s a String!’.
And what if it’s not? The problem with induction.