Alex Nadalin - CTO @ namshi.com
FRONTEND CON 2017 (WARSAW - poland)
WARNING
Controversy
ahead
WARNING
Insult
@_odino_
Zalando of the middle east
http://tech.namshi.io/blog/2017/05/02/rebu
ilding-our-mobile-website/
Shidhin CR
https://developers.google.com/e
xperts/people/shidhin-cr
Mohamed amin
https://medium.com/@Mohamed
Amin88
Gabriel izebhigie
http://tech.namshi.io/team/#Ga
briel Izebhigie
Take #1
Started with a SPA
...which pissed bots off
...which pissed clients off
...which pissed clients off
...which pissed clients off
Take #2
Isomorphic js
Both on the client & the server
universal js
Works only in movies
https://2015.jsday.it/talk/back-to-the-future-iso
morphic-javascript-applications/
Clients are still angry
Sneak peak of a custom-made
universal js framework
Sneak peak of a custom-made
universal js framework
Sneak peak of a custom-made
universal js framework
Fonzie does not approve.
The do-over
Simplify our codebase
Better client-side performance
roi
Best website?
It’s an app.
It’s an app.
Smooth transitions
It’s an app.
Smooth transitions
Great (perceived) performance
Can a website
Match that?
Spa closely bridges the gap
Spa closely bridges the gap
Additional layer of complexity
Generally no.
Spa closely bridges the gap
Additional layer of complexity
Do we need it?
service is king
The b* stack
The b* stack
The b* stack
The b* stack
The b* stack
server
client
Webpack 2
Webpack 2
Tree-shaking saved us
15/20% of the gzipped
bundle size
Say “no” to jquery(as much as possible)
Abstraction === cost
Abstraction === cost
let obj = {
name: 'alex',
age: 28,
hair: 'enough',
status: 'married',
job: 'who really knows',
}
_.pick(obj, ['name', 'age'])
{
name: obj.name,
age: obj.age,
}
Abstraction === cost
let obj = {
name: 'alex',
age: 28,
hair: 'enough',
status: 'married',
job: 'who really knows',
}
_.pick(obj, ['name', 'age'])
{
name: obj.name,
age: obj.age,
}
Abstraction === cost
let obj = {
name: 'alex',
age: 28,
hair: 'enough',
status: 'married',
job: 'who really knows',
}
_.pick(obj, ['name', 'age'])
{
name: obj.name,
age: obj.age,
}
Abstraction === cost
let obj = {
name: 'alex',
age: 28,
hair: 'enough',
status: 'married',
job: 'who really knows',
}
_.pick(obj, ['name', 'age'])
{
name: obj.name,
age: obj.age,
}
700k ops/s 75m ops/s
Abstraction === cost
let obj = {
name: 'alex',
work: {
name: 'Namshi'
}
}
_.get(obj, 'work.name', null)
Abstraction === cost
let obj = {
name: 'alex',
work: {
name: 'Namshi'
}
}
_.get(obj, 'work.name', null)
1.3m ops/s
Abstraction === cost
let obj = {
name: 'alex',
work: {
name: 'Namshi'
}
}
let work = null;
if (obj && obj.work && obj.work.name) {
work = obj.work.name
}
Abstraction === cost
let obj = {
name: 'alex',
work: {
name: 'Namshi'
}
}
let work = null;
if (obj && obj.work && obj.work.name) {
work = obj.work.name
}
70m ops/s
!
Results may vary
let attributes = [
‘price’,
‘name’,
‘description’,
‘url’
]
…
…
…
function sanitize(products) {
return products.map(p => {
return _.pick(p, attributes)
})
}
let attributes = [
‘price’,
‘name’,
‘description’,
‘url’
]
…
…
…
function sanitize(products) {
return products.map(p => {
return _.pick(p, attributes)
})
}
let attributes = [
‘price’,
‘name’,
‘description’,
‘url’
]
…
…
…
function sanitize(products) {
return products.map(p => {
return _.pick(p, attributes)
})
}
let attributes = [
‘price’,
‘name’,
‘description’,
‘url’
]
…
…
…
function sanitize(products) {
return products.map(p => {
return _.pick(p, attributes)
})
} 5ms / req
let attributes = [
‘price’,
‘name’,
‘description’,
‘url’
]
…
…
…
function sanitize(products) {
return products.map(p => {
return _.pick(p, attributes)
})
} 10% / req
const _ = require(‘lodash’)
const _ = require(‘lodash’)
const pick = require(‘lodash/pick’)
articles
.filter(a => a.active)
.map(a => {
a.title = titleCase(a.title)
return a
})
articles
.filter(a => a.active)
.map(a => {
a.title = titleCase(a.title)
return a
})
articles
.filter(a => a.active)
.map(a => {
a.title = titleCase(a.title)
return a
})
articles
.filter(a => a.active)
.map(a => {
a.title = titleCase(a.title)
return a
})
articles
.filter(a => a.active)
.map(a => {
a.title = titleCase(a.title)
return a
})
700k ops/s
articles
.reduce((acc, a) => {
if (a.active) {
a.title = titlecase(a.title)
acc.push(a)
}
return acc
})
articles
.reduce((acc, a) => {
if (a.active) {
a.title = titlecase(a.title)
acc.push(a)
}
return acc
})
4M ops/s
sprites?
No thanks,
I do http/2
300kb 500kb 200kb
The old www
The old www
300kb 500kb
The old www
300kb 500kb
800kb / tot
2 conns
The old www
sprites
sprites
1mb / tot
1 conn
sprites
http/2
http/2
800kb / tot
1 conn
http/2
react?
“No sauce please”
“No sauce please”
react-lite 25kb
preact 3kb
css animations
css animations
Page transition
preconnect
<html class="en">
<head>
<link preconnect="https://a.namshicdn.com" crossorigin />
preconnect
<html class="en">
<head>
<link preconnect="https://a.namshicdn.com" crossorigin />
preconnect
<html class="en">
<head>
<link preconnect="https://a.namshicdn.com" crossorigin />
Initiate
connections
ASAP
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
preload
<link
rel="preload"
href="https://mycdn.com/fonts.css"
as="style"
onload="
this.rel='stylesheet';
this.className='font-loaded'
"
/>
Async
Non-blocking
CSS
preload
prerender
window.addEventListener('load', function(){
var preRenderLink = doc.createElement('link');
preRenderLink.rel='prerender';
preRenderLink.href= '{{ nextPage }}';
document.head.appendChild(preRenderLink);
});
prerender
window.addEventListener('load', function(){
var preRenderLink = doc.createElement('link');
preRenderLink.rel='prerender';
preRenderLink.href= '{{ nextPage }}';
document.head.appendChild(preRenderLink);
});
prerender
window.addEventListener('load', function(){
var preRenderLink = doc.createElement('link');
preRenderLink.rel='prerender';
preRenderLink.href= '{{ nextPage }}';
document.head.appendChild(preRenderLink);
});
prerender
window.addEventListener('load', function(){
var preRenderLink = doc.createElement('link');
preRenderLink.rel='prerender';
preRenderLink.href= '{{ nextPage }}';
document.head.appendChild(preRenderLink);
});
prerender
window.addEventListener('load', function(){
var preRenderLink = doc.createElement('link');
preRenderLink.rel='prerender';
preRenderLink.href= '{{ nextPage }}';
document.head.appendChild(preRenderLink);
});
prerender
SPLIT ASSETS
SPLIT ASSETS
SPLIT ASSETS
index.html
detail.html
Less bandwidth, good cache rate
Less bandwidth, good cache rate
7 req
~100kb
Less bandwidth, good cache rate
9 req
~150kb
RESULTS ?
-60%
AVG document content loaded time
1.93 vs 4.84
-14%
Bounce rate
+40%
AVG session duration
+30%
Conversion rate
Looking
Forward
to...
Not available in the browser
https://github.com/grpc/grpc/issues/8682
Under development
https://firebase.google.com/docs/cloud-messaging/
Under development
https://githubengineering.com/githubs-post-csp-journey/
NERD ADVICE
NERD ADVICE
Act upon what
moves the needle
https://en.wikipedia.org/wiki/Par
eto_principle
“Hot” is
overrated
NERD ADVICE
“Hot” is
overrated
https://jakearchibald.com/2016/ca
ching-best-practices/
“As you can see, you can hack around poor caching in
your service worker, but you're way better off fixing
the root of the problem. Getting your caching right
makes things easier in service worker land, but also
benefits browsers that don't support service worker
(Safari, IE/Edge), and lets you get the most out of
your CDN.”
https://jakearchibald.com/2016/caching-best-practices/
NERD ADVICE
“Solve problems
on the right
layer”
https://www.ampproject.org/lear
n/amp-design-principles/
NERD ADVICE
“Solve problems
on the right
layer”
https://www.ampproject.org/lear
n/amp-design-principles/
NERD ADVICE
Servers can still
be pretty darn
fast.
NERD ADVICE
NERD ADVICE
Servers can still
be pretty fast
NERD ADVICE
Servers can still
be pretty fast
NERD ADVICE
Servers can still
be pretty fast
NERD ADVICE
50% within <20ms
95% within <120ms
Take this any day
Alessandro Nadalin
Alessandro Nadalin
@_odino_
Alessandro Nadalin
@_odino_
Namshi
Alessandro Nadalin
@_odino_
Namshi
CTO
Alessandro Nadalin
@_odino_
Namshi
CTO
odino.org
Thanks!
Alessandro Nadalin
@_odino_
Namshi
CTO
odino.org
we are hiring!
tech.namshi.com/join-us
github.com/namshi
twitter.com/TechNamshi
tech.namshi.io

Spa, isomorphic and back to the server our journey with js @ frontend con poland 2017