Abstractions, complexities and off-ramps
Your Code
The browser
The user
npm install lodash
const lodash = require('lodash')
that looks nice!
DX
UX
DX
UX
npm install lodash
const lodash = require('lodash')
THIS IS SO EASY!
npm run build
npm install lodash
const lodash = require('lodash')
No visible cost
npm run build
But most of us aren’t making Figma. Most of us are still making documents [...].
Jason Godesky
https://scribe.rip/@jason.godesky/when-javascript-fails-52eef47e90db
The browser (and the user)
The browser (and the user)
Your framework
Your frameworks' dependencies
The browser (and the user)
A library from npm
The browser (and the user)
Your code.
The browser (and the user)
Dependencies, complexities and abstractions
An architectural lasagne!
Third party dependencies
Complexity
Third party dependencies
Complexity
Third party dependencies
Complexity
UX
No one purposefully set out to cause this
I am not saying that we should never use JavaScript.
the thing that’s really bugged me for the past decade is the increasing complexity of “modern” frontend development when it isn’t driven by user needs
Jeremy Keith
https://pixelpioneers.co/blog/2023/speaker-spotlight-jeremy-keith
2
1.
2.
The impact of dependencies
Complexity & abstractions
Complexity & abstractions
https://www.theregister.com/2016/03/23/npm_left_pad_chaos/
becomes unmaintained on GitHub
has security vulnerability that no one is fixing
has bad perf case that your
website exposes
{
{
Every dependency you add to a project is one more potential single point of failure.
Jeremy Keith
https://adactio.com/journal/19021
Your code
Mission critical abstractions and dependencies
use the platform
jackfranklin.co.uk/blog/working-with-react-and-the-web-platform/
custom elements
HTML Form Validation
FormData API
Constraint Validation API
98%
96%
97%
data from caniuse.com on 28/04/2023
npm install event-emitter
Well I'd just use EventTarget
class BlogPostComments extends EventTarget {
onUpdate() {
this.dispatchEvent(new Event('updatedcomments'));
}
}
const comments = new BlogPostComments();
comments.addEventListener('updatedcomments', () => {
// we got some new comments!
});
class BlogPostComments extends EventTarget {
onUpdate() {
this.dispatchEvent(new Event('updatedcomments'));
}
}
const comments = new BlogPostComments();
comments.addEventListener('updatedcomments', () => {
// we got some new comments!
});
97%
https://caniuse.com/mdn-api_eventtarget
Can we shift our default starting point?
You can’t JavaScript your way out of an excess-JavaScript problem. These large JavaScript bundles are costly to site performance.
Zach Leat
https://www.zachleat.com/web/single-page-applications/
I have a problem to solve
what dependency will solve this for me?
npm install lodash
const lodash = require('lodash')
This is so easy!
npm run build
The problem with conveniences is that they come at a cost. How easy is it to add this one little convenience that will surely make our lives better, never think about it again and move on.
Pascal Schilp
https://dev.to/thepassle/the-cost-of-convenience-kco
I have a problem to solve
Does the browser provide a solution natively?
Does the browser provide a solution natively?
No: I need to implement or add a dependency
Yes!
Reduce the amount of
JavaScript we ship
moving away from
JavaScript by default
https://twitter.com/ksylor/status/1456341818698838016
The general idea of an “Islands” architecture is deceptively simple: render HTML pages on the server, and inject placeholders or slots around highly dynamic region
Jason Miller
https://jasonformat.com/islands-architecture/
Jake Archibald
https://twitter.com/jaffathecake/status/1448585986422808576
This means I can enhance little parts of the page without going all-in on a framework.
The Islands Architecture feels like the best of both worlds
Let's talk about abstractions and dependencies
The 4 easy methods YOU can use to MANAGE your JavaScript!
Maintaining control
Dependency awareness
Lean on browser primitives
Have an exit strategy
Maintaining control
Your code is in control
Your code is in control
The framework is in control
Your code is in control
The framework is in control
this.user = 'jack';
this.render();
Your code is in control
The framework is in control
this.user = 'jack';
this.render();
const [
user,
setUser
] = useState('');
setState('jack');
Your code is in control
Inversion of Control
this.user = 'jack';
this.render();
const [
user,
setUser
] = useState('');
setState('jack');
Libraries and a frameworks can be distinguished by looking at the Inversion of Control
Surma
https://surma.dev/things/cost-of-convenience/index.html
Use framework primitive to update data
Framework re-renders for you.
Render component
Use framework primitive to update data
Framework re-renders for you.
Render component
Update legacy jQuery component
However, as frameworks often put themselves at the core of any architecture, it can be hard to opt out
Surma
https://surma.dev/things/cost-of-convenience/index.html
A library gives you an escape hatch by default
Because you never give up control
Maintaining Control
Prefer libraries, or ensure your framework's escape hatches are going to work for you.
Dependency Awarness
1. more code in your website's bundle
3. another thing to upgrade (or not) on a semi-regular basis
2. another potential point of failure
4. more complexity and code to understand
Dependencies must justify these costs
And we must look to mitigate the risks
Jack's ULTIMATE guide to dependency consideration
Was £299, now £49 for TODAY ONLY!!!!
WHAT
is this dependency providing?
would it be like to build it ourselves?
do we do if it becomes unmaintained?
is its impact on the bundle size?
if we have to replace it?
LitElement vs lit-html
CodeMirror vs in-house
Awareness of bundle size
Me! (is it weird to quote your blog posts in talks? Maybe. Egotistical? Definitely...)
https://www.jackfranklin.co.uk/blog/check-in-your-node-dependencies/
No more npm install locally or on CI bots
100% reproducible builds by default
No blocked builds if npm is down
Dependencies of dependencies are no longer invisible
You are forced to see the code you are committing.
Dependency update code changes are unmissable
no more energy used from npm installs
30 engineers with 100s of builds per day...
node_modules/*
!node_modules/new-dep
It fucks me up, we devops / sre do our best to advocate for good tools, do our best to educate people, and there's a dude coming from google saying "oh yeah no more npm installs" and i want to fucking cry
warning: approach may make you unpopular
Dependency Awarness
Weigh up the costs vs benefits of each dependency and make those costs visible.
Lean on browser primitives
<web-components>
<web-components> and the island architecture
The browser (and the user)
Large framework
The browser (and the user)
web components
Historically, it has been difficult to use Shadow DOM in combination with Server-Side Rendering because there was no built-in way to express Shadow Roots in the server-generated HTML
Jason Miller, Mason Freed
https://developer.chrome.com/en/articles/declarative-shadow-dom/
Islands Architecture
Web Components
Declarative Shadow DOM
Single Page Application advocates argue [...]. While we can debate those points (recognizing also that they will fade into irrelevance as the web platform progresses)
Zach Leat
https://www.zachleat.com/web/single-page-applications/
Lean on browser primitives
The browser keeps getting more powerful and abstractions that build on top of these primitives are here to stay
Have an exit strategy
Be British
a pessimist
⏰
KnockoutJS
Backbone
⏰
KnockoutJS
Backbone
Angular 1 & Object.observe()
⏰
KnockoutJS
Backbone
Angular 1 & Object.observe()
React
Vue
⏰
KnockoutJS
Backbone
Angular 1 & Object.observe()
React
Vue
React hooks
Svelte
⏰
KnockoutJS
Backbone
Angular 1 & Object.observe()
React
Vue
React hooks
Svelte
web-components, Lit, Stencil, islands
⏰
KnockoutJS
Backbone
Angular 1 & Object.observe()
React
Vue
React hooks
Svelte
web-components, Lit, Stencil, islands
what is next?
??
Jack of the present
* in this slide Jack of the future is played by Jack's dad
What can I do today?
Jack of the future*
To make the future maintainers happy?
You cannot future proof everything
Maintaining control
Dependency awareness
Lean on browser primitives
Have an exit strategy
the path to a less complex web
Reduce complexity
by leaning on the browser
Reduce the amount of code
by leaning on the browser
Reduce energy usage
by leaning on the browser
Reduce user frustrations
by leaning on the browser
Increase accessibility
by leaning on the browser
A website that uses standard HTML links for navigation is easier for assistive technologies and web crawlers to use.
[...]
It makes the default outcome the accessible one.
Jason Miller
https://jasonformat.com/islands-architecture/
The browser (and the user)
Framework internals
Core framework
Lean on the browser does not mean the end for frameworks
The browser (and the user)
Framework internals
Core framework
Reduce the reliance on abstractions and if you need them, prefer libraries over frameworks
Have an off-ramp and stay flexible to change.
you find yourself having outgrown the framework [...], and you want to switch. This can be really hard, because frameworks shape your code, and each framework is different, so there is no easy migration path
Surma
https://surma.dev/things/cost-of-convenience/index.html
Critically consider each dependency and mitigate the risks.
Be aware of what
you are shipping
The browser (and the user)
Your code.
Shrink this
The browser (and the user)
to this
DX
UX
DX
UX
DX
UX
Thank you.
jackf.io/talks/abstractions
jackf.io/toot
jackf.io/tweet
jackf.io/blog
abstractions-and-complexities
By Jack Franklin
abstractions-and-complexities
- 302