Making the Jump: How Desktop-Era Frameworks Can Thrive on Mobile

Tom Dale

Recorded at JSConf EU 2017


Get notified about Tom Dale

Sign up to a email when Tom Dale publishes a new video

[Music]
[Applause]
[Music]
thank you very much thank you all for
having me here today it really it really
is an honor to be here my name is Tom
Jas Conti you is a very special
conference for me I was here about four
years ago which is where I met
Zara who you saw earlier jedd introduced
us on the plane and a few years after
that I proposed and we're getting
married in August so for me in
particular days comfy you had been
particularly life-changing and hopefully
many of you can develop similarly
special relationships professionally and
so today I want to talk to you about
smartphones and in particular I want to
talk about how tools that grew up on the
desktop like ember angular and react can
make the jump to the mobile future and I
think when people think about adapting
apps to phones they often focus on the
more obvious differences so for example
things like the difference in screen
size the difference in CPU performance
the different input devices touch versus
is used is important too so for example
a user on a mobile phone is more likely
to be distracted than someone who might
be focused on a task at their desk or
they might have a more intermittent
network connection than having a
broadband Wi-Fi router in their
apartment so when phones can be used
anywhere there are very few assumptions
that we can make whether that's how
attentive the user is or whether they
have that internet connection at all I
recently started working at LinkedIn in
December and one thing about working at
a site like LinkedIn is that if
reminds me of how truly global the web
can be in many cases adapting the
smartphones really means adapting to new
users for many people their first
computer is a smartphone that means
millions maybe even billions of people
are going to be participating online
without ever owning a desktop computer
and the more global your app is the more
combinations of devices and networks
you'll have to deal with CPU power can
range from a feature phone to a low-end
smartphone to the latest iPhone up to a
beefy desktop computer and network
connectivity can range from GPRS to
gigabit fiber to not being there at all
just ride the subway in New York so the
problem with this is that without
careful design
it's easy to optimize for one
combination at the cost of another so
let's take a look at two different users
user a has a very low end smartphone it
has a CPU that easily overheats and
starts throttling itself and they've got
flash storage that is so slow that it
would be borderline useless except for
the fact that it's already full this
person's data connection is quite slow
so they use Opera Mini which uses a
proxy to heavily compress all of the
assets of their browser requests before
the phone actually gets it now user B
has a high end phone with a CPU that
might rival a laptop computer and I've
got plenty of fast storage the only
problem is that this person is traveling
without any cellular data so while they
may sometimes have access to broadband
internet it's only when they're in range
of a Wi-Fi network that they can do
anything useful for user a anything that
requires a lot of JavaScript is probably
not going to work at all even if they
stopped using opera's proxy the slow CPU
and storage means that downloading and
evaluating a bunch of JavaScript is
going to take a really long time getting
reasonable load times probably means
rendering on the server and keeping the
file size of everything as small as
possible
we're user B we want our web app to work
more like a native application in fact
we'd be willing to spend more time
upfront to load the entire app and as
much data as possible onto the phone
basically what the App Store does right
if that meant that we could still use it
when the phone was away from this Wi-Fi
connection that we're relying on and
this user probably has higher
expectations of our app as well it would
probably be worth sending a little bit
more code if it means that they get 60
frames per second scrolling and buttery
smooth animations since their hardware
can definitely handle it so best
experience on the slower device is often
radically different from the best
experience on the faster device and the
problem is that historically the more
that we've tried to take advantage of
high-end phones and vast networks the
worse that we've made the experience for
the majority of the world so what do you
all think is the solution to this
problem I'll give you a hint the
initials are PE and you guesses we guys
think it's shout it out that's right
panic and evacuate the building
very good
no the answer is supposed to be
progressive enhancement but one thing
that's implicit at least in all the
advice that I've received about
progressive enhancement is that you're
supposed to do it yourself it almost
always means rendering on the server and
denying yourself the temptation of using
too much JavaScript and browsers in the
last 10 years let's say have advanced at
a remarkable rate to me it feels like
the web has never had more momentum
adding feature after feature and yet
despite all of this incredible
innovation from indexeddb to web workers
it doesn't actually feel to me like the
experience of using web apps day to day
has improved that much in the last three
or four years so why is it that these
radical improvements in the browser
don't seem to be translating into
radically improved web applications
and I would argue that the reason for
this is that the cost of code is too
damn high taking advantage of all of
those new features in the browser
requires a lot of code native apps that
work offline with beautiful user
interfaces or hundreds of megabytes and
that's not even factoring in the SDK
that ships with the operating system on
the phone on the web just parsing and
downloading JavaScript can be enough to
turn some phone's janky and when you
bundle all of your JavaScript into a
single file every byte starts to count
and in turn this sets up misaligned
incentives where libraries have to
compete on file size rather than
robustness so how do they achieve these
in probably small file sizes often it's
by persuading you that the old thing is
unnecessarily complex the cardinal sin
in JavaScript but they have seen through
the BS and built something simple and
that leads us to the JavaScript
simplicity fetish it is this emphasis on
file size that leads the JavaScript
communities to this fetish when file
size relies on simplicity and speed
relies on file size and speed is
paramount on the web we have to pretend
that simple tools are the best tools
because what other choice do we have
the only way to write an app that will
run well on slower phones and networks
is to shift less JavaScript too often
that comes at the expense of handling
edge cases or building higher level
abstractions as a community we often
feel like we can't build those
sophisticated solutions because
eventually we start to collapse when our
own page weight that's rational we've
seen this play out several times now
more sophistication equals more code
equals flow or load times so the time
period from 2011 to 2017 can roughly be
broken up into the backbone era the
angular era and the react the era
now this trio of eras is also sometimes
hmm
now it's easy to become enamored with
the simplicity of a tool and that can
lead us to underestimate the complexity
of building modern web apps so let's
take a journey all the way back to 2011
when the most cutting-edge tool that
people were using was backbone and that
I love it it's so simple I can clone it
from github and read the entire source
code all the source code in a day but
then I realized that whenever a model
changes every view in my app as we
render right terrible performance so
what are we going to do about this well
I heard about this new library called
angular it's really amazing two-way
bindings feel like magic it's so simple
you just change it and it updates on the
screen but a year into the app they
thought my app consists of one
controller with three million lines of
code this doesn't feel very maintainable
but don't worry because we have
something called react react is amazing
because it's so simple because it's just
the v in MVC and a year or two into
building your app you realize that it's
seven megabytes and takes two years to
build so Don Norman who you may know is
the author of the design of everyday
things wrote this in an essay about
security the numerous incidents of
defeating security measures perhaps
prompts my cynical slogan the more
secure you make something the less
security gets in the way sensible
well-meaning dedicated people develop
hacks and workarounds to defeat the
security for example when password rules
are too annoying people will just write
down their password on a piece of paper
and put it on their desk so this prompts
me to offer the Tom Dale simplicity
corollary the simpler you make something
the less simple it becomes because when
simplicity gets in the way
sensible well-meaning dedicated people
develop hacks and workarounds to defeat
the simplicity so how do we break out of
this local maximum how do we write one
app that can somehow scale up and down
across all these different devices and
performance characteristics well I think
we can learn from native developers
because they've had to tackle a similar
problem so this is a bunch of different
CPU
architectures and different CPU
sets if you write some assembly code for
x86 let's say and then you want to run
it on arm you have to start over from
scratch there's totally different but
learning assembly for all of these
architectures is a really big task if
this is how system software was written
there probably wouldn't be much
cross-platform code and introducing new
CPU of new CPU architectures would be
borderline impossible we figured out a
long time ago that a compiler can take a
higher-level program and get it to run
across all of these architectures and if
a new architecture comes along you just
have to update the compiler not rewrite
every app in existence so here is an
example of using flying an LLVM to take
some C code and and targeting an
architecture that definitely didn't
exist in the 70s which is webassembly
and best of all the compiler not only
makes our code run on all these
different architectures you can also use
its knowledge of different architectures
to optimize our code differently for a
particular performance characteristics
of that platform so for example here's a
paper about optimizing GCC where they
said finally a new target independent
feature could be implemented to take
into account certain specific features
of the target for example to make
advantage of speculation feature of
Intel Itanium debating the paper here we
have implemented its support in the
instruction scheduler and this is
something that that ashley said in her
talk which I just totally agree with if
there's one thing I want you to take
away from this talk it's that modern web
toolkits are transforming from libraries
into something that's more like a
compiler except instead of compiling a
higher level programming language
incident native code they're compiling
your higher-level app into a highly
optimized version and eventually I think
this will mean building multiple
versions of an app and delivering the
most optimized versions for that device
this might be as simple as delivering
es6 builds to newer browsers and
transpile ds5 for older browsers or it
could be something like automatically
transitioning between server-side
rendering and client-side rendering
based on the network's be detected but
most importantly these tools can finally
embrace
complexity by decoupling it from file
size if we can shift the complexity from
to our bill tools rather than being
these kind of monolithic runtime
libraries we can actually eliminate this
enormous pressure to be simple so
unfortunately I don't have a lot of time
with you today
but I wanted to highlight these I wanted
to highlight three things that the teams
behind react angular and ember are
working on that demonstrate that these
frameworks are staying relevant on
mobile by essentially becoming compilers
so the first thing we'll talk about is
react then Ashley also mentioned free
pack which is an open source tool from
Facebook for optimizing JavaScript
evaluation now fortunately for me free
pack was released while I was literally
on the plane to Berlin so I had to
rewrite as part of the talk but it was
almost too good to be true given that
definitely fits the theme the free pack
website uses quite a bit of computer
science terminology to explain what it's
doing it's genuinely very exciting and
very novel but you don't need to
understand terms like symbolic execution
or heat serialization to understand why
free pack has a potential to be a
critical tool for optimizing web apps so
to understand why free pack is cool
let's take a step back and understand
how a bundle optimizer like roll-up
works the way roll-up works is by
starting with an entry point file and
analyzing which files it imports so for
each of those files it then looks at
what that file imports and so on
building up this tree once its analyzed
the entire graph it builds a new
JavaScript file that includes just the
modules that were actually used and then
once we've analyzed that we can get rid
of the stuff that we don't actually need
now all here's an example from the
roll-up repple on the roll-up web site
all of our modules on the left and the
final output is on the right everything
we've imported is inline right into that
file and modules that are never imported
or executed our excuse-me are excluded
this gives us smaller files by
eliminating files that we don't actually
use but it's not just modules roll-ups
can do some really smart things if it
sees exports that are unused in this
case note that here that we've added a
new class to animal
but our output on the right remains the
same as before
that's because roll-up is smart enough
to not include the feral animal class
even though animal j/s is imported
because no one actually asked for that
specific export this kind of thing is
possible because module syntax and
JavaScript was specifically designed to
be statically analyzable static analysis
just means figuring out things about how
a program will run without actually
having to run it roll-up isn't running
your code it's just scanning it to
figure out what gets imported and what
gets exported and modules don't actually
work inside conditionals so if roll-up
sees an import statement it knows 100%
certainty yep we're going to need this
module so that's roll-up which uses
static analysis to optimize file sizes
without running any code let's take a
look at v8 what specifically I mean
really any JavaScript virtual machine
but we'll look at v8 specifically v8
can't help you with file size by
definition it has to have downloaded the
JavaScript files already but it can help
with making your code faster so this is
a high-level view of the architecture of
v8 and there's three major components I
think there are v8 people here so please
forgive me if I get anything wrong the
parser which turns your javascript
source code into data structure then the
interpreter ignition which evaluates
your JavaScript and finally turbofan
which is an optimizing compiler and
optimized native code optimized code
takes more memory and it takes more time
to generate but it can run at truly
ridiculous speeds and as your program
executes v8 keeps track of which parts
of your code get run the most and how
they run based on this information it
will ask turbofan to create optimized
versions of the code that it thinks will
help your app run faster so this is a
kind of dynamic analysis v8
optimizations require running your
program and that's a good thing it's a
JavaScript to the end not running
a bug but because it relies on how the
program actually runs you have to use
the unoptimized code for a while until
v8 can make some decisions and that
means that things will take a little
while to get really fast and every user
pays that cost even though the end
result is more or less the same for
everyone so this is the fundamental
tension that we have between static
analysis and dynamic analysis
optimizations that use static analysis
can be run at Bill time meaning you pay
the cost once and all of your units
users benefit but static analysis can
only get you so far because you can only
perform optimizations you're 100% sure
about if the module might get used
roll-up can't remove it because if it
guess is wrong the app breaks
optimizations that use runtime analysis
can collect information about how a
program actually runs so they don't have
to guess but requiring the program to
run in order to optimize it is a bit of
a catch-22 by definition it means that
the first run of the program will be
running unoptimized and then and that's
the opposite of what we want on the web
where things typically feel instant so
that's what makes pre-packed so cool
what it does is actually run your code
similar to v8 it's an actual JavaScript
virtual machine rather than running apps
though it's designed to run at Build
time and then it reverse engineer is a
simplified version that is faster for
your users JavaScript VM to run the
first to execute for the first time so
this whole thing is a talking to itself
and I'm still digesting how it works
internally but let's just take a quick
look at how it works I know Ashley
showed an example also so imagine we're
writing a library that prints the start
date of Jas confit you to create a new
string so if we run this through
pre-pack you can see that it's replaced
all of that code with a single string
literal the final result of what we
would have got if we'd actually run the
code in JavaScript but now instead of
every user having to allocate a date
object parts a date string initialize a
date object based on that strings
generate a full iso 8601 date string and
then concatenate it we have the simple
string literal and this is a contrived
example that probably wouldn't make much
a difference in reality but each of
these small savings may start to really
add up in code bases the size of ember
angular or react and the good news is
I've heard rumors that Jason Miller
who's here will soon be releasing a new
library called pre free pack it does
everything that prefect does in only 600
bytes of code so let's take a look at
angular angular bet early on typescript
a language that personally I'm a really
big fan of
and if you're not familiar with
typescript it's just a superset of
modern JavaScript but extended with type
annotations and those types make
refactoring much easier particularly as
your project grows as well as giving you
incredibly rich autocompletes in your
text editor but to me the most exciting
and underappreciated reasons for
introducing types into javascript is the
potential for using a type information
to more aggressively minify our builds
so let's take a look at how minify are
like uglify works minifiers use their
knowledge of javascript to make changes
that don't affect behavior so in this
example we've renamed this variable
fruit to be called o the end because the
variables inside this function scope
nowhere else outside of that function
can access so whatever we call it is
kind of arbitrary let's change this
example a little bit and instead of
making fruit a string we make it an
object with a name property and even
though the variable fruits though it's
mangled as you can see the property name
name stays the same
now up Lefou JS has a property mangling
feature that you can optionally turn on
but unlike renaming variables where it
doesn't affect how the program runs
renaming properties can break things
in fact the readme io5 says note this
will probably break your code which
doesn't instill a lot of confidence so
for example if we have an element and we
change its unclick property if that gets
read if that gets renamed to something
else then this is just going to fail
silently that click handler that we
thought we were handling just doesn't
exist anymore similarly if we look at a
component this is like a react component
for example there are parts of this
object that are private just to that
component and there are other things
that are public meaning that things like
a library or the platform rely on it
really what we want to do is mangle the
private stuff but maybe make sure that
our library can keep using the public
stuff and there's good news
Google's closure compiler already
supports this kind of advancement if
ocation for javascript unfortunately it
requires annotating types of J's
comments and other weird closure
specific configurations approximately
seven people on the planet outside
Google have actually got this to work
now the angular team has released an
amazing pool called
sickled that translates the typescript
project into the format the bazaar
format that closure compiler expects so
by using this extra information that
types des provides and because angular
is written in typescript 2 closure
compiler can do some shockingly advanced
minification
as the readme says the goal of this
project is to let you write typescript
and let sickle handle massaging into
something that closure compiler
understands and I'm really excited to
see Googlers working on making closure
compilers under appreciated power more
accessible to the broader world and
integrating this into ng CLI by default
would I think make a huge practical
impact into the final size of basically
every angular app ok so lastly I just
want to talk about ember and
specifically a glimmer which is the
rendering engine and ember and also
library that we extracted to be used
standalone outside of ember a month or
two ago now we're thinking about the
performance metrics of a rendering
engine in the browser there's three
things you want to consider the first is
how long does the library take to load
so the fastest rendering library in the
world probably wouldn't be worth it if
it was like 50 megabytes right but then
once you have something within
reasonable bounds of time you want to
look at how long does it takes to render
a component for the first time when you
have to create brand-new Dom elements
and third how long does it take to
render a component updating its existing
Dom elements instead of replacing them
when the data factum that component
changes okay so who remembers this app
yeah the DB bane of my existence this is
Brian Florence's DB monde demo app which
highlighted the benefits of reax virtual
Dom dipping strategy and for a while I
set off this frenzy of performance
benchmarks and everyone was competing to
have the fastest score on it that's all
anyone ever focused on but then more
recently the communities focus has been
on initial load times as people have
grappled with hat with how we deal with
these low-end phones and slow networks
and the tricky thing is trying to find
the sweet spot that balances between
optimizing the first render and
this is an issue of bookkeeping the more
bookkeeping you do in the initial render
to make subsequent updates faster the
longer that initial render is going to
take so consider a react component using
bootstrap markup in the JSX on initial
render virtual
those are going to be created for each
element here and that makes a lot of
sense right this is basically just
instructions for how to create the real
bomb but if a single prop changes we
have to run the entire render function
again and that's quite a few allocations
of virtual Obama objects not to mention
once you have them you then have to do
is dipping steps to kind of reverse
engineer what changed between the last
render and this render so this example
is small enough that the performance
cost is probably negligible but in large
apps or inside a loop you can imagine
that it starts to add up so can we do
better in glimmer we use handlebars
templates for components and these get
compiled when you build your app the
question is what do we compile them into
in embers history the answer to this
question has changed three times first
it was strings then DOM and now summer
into it a bytecode so we tried an
approach that we thought could balance
the fast initial rendering of jsx while
improving on the update performance and
we did it by treating handlebars and the
entire rendering pipeline actually as
though it were a programming language
and a virtual machine that runs that the
compiled code so in glimmer we compile
this template into a JSON object that
looks like this now this probably
doesn't look meaningful to you but what
object represents is actually a sequence
of octobe or instructions that tell the
bloomer VM how to build a Dom structure
we represent the opcodes in this compact
data structure of arrays integers string
literals so parsing is fast and memory
consumption is much lower when we landed
glimmer in ember 2.10 many apps from
their template sizes dropped by 70 to 80
percent pretty significantly now if you
look closely at this like like that does
it really look like very much but we use
integers to represent instructions for
compactness and for certain v8
optimizations but if we were to
represent in a hypothetical glimmer
assembly language it might look
something like this just a linearized
set of instructions so when we take
those instructions and we execute them
on top of the glimmer vm it looks a
little bit something like this we have
an instruction pointer that just goes
through this sequence and so this is
going to build an Li element then we're
adder opcode and that's going to set the
attribute and then we're going to add
another static attribute which is the
active class and then that's the whole
template right so now we're just going
to flush the element which basically
tells Limor this is good to go to put
into
now this is an easy example because it's
completely static but what about
something that has some dynamic content
well what I haven't mentioned yet is
that the glimmer VM is actually made up
of two VMs
one is optimized for constructing an
appending Dom elements on initial render
and the other is optimized for updates
so so this is the program for initial
you're rendering this template now we
could run this again for updates right
but there's a lot of static content in
here and spending time executing op
codes for a static content that we know
hasn't change doesn't seem like a good
use of time in fact we just want to
focus on this dynamic part and the way
that we optimize this is through a
technique called partial evaluation and
partial evaluation simply means to
generate an optimized program by
evaluating an initial program and we use
this technique to generate an updating
program automatically so essentially as
the initial rendering program is being
run we're generating a second more
optimized version to update the Dom for
us when the data changes essentially
these operations in the initial
rendering program are responsible for
generating their own subsequent code so
the way this works is that we're going
to go through our op codes for the
initial render as before it's going to
open a new div we're going to set some
static attributes on this but now we're
going to get to the dynamic content
portions and in addition to actually
appending that content that text content
into the Dom we're also going to
generate an updating out code which is
to update that contact back content and
it's going to include a reference to
which value we're putting in it as well
as a reference to that text note that
we've just created and then we'll flush
the element like normal now when that
data behind the component updates
instead of going through that whole
process again all we have to do is
evaluate our highly optimized updating
program which is just going to take the
old data out of the Dom and update it
and you can think of it as doing
something it's a little more complex
than this but you can think about why it
ends up being quite fast because all
you're doing is comparing these two
values to see if they've changed to know
if you need to update the Dom and we
noticed that basically all the rendering
library benchmarks only tested pure
dynamic content but that's not actually
representative of most apps in the real
world where you have plenty of divs in
other semantic markup so we created the
benchmarks that had a mix of both static
and dynamic content
rendering was more or less neck-and-neck
with virtual Dom based libraries I'm not
putting the other names here because not
a competition I just want to show that
we're basically the same ballpark
however when we move to measuring
updating performance what we notice is
that it seems that having this optimized
updating bytecode makes a big difference
when you're updating these components
which makes which can make a big
difference especially on lower end
phones so today we've talked about three
popular frameworks and some of the
things they're doing to improve
performance on mobile devices it's
important to understand that modern
frameworks are becoming optimizing
compilers not just something that you
dropped into a page with the script tag
and this is just the beginning there's
an exciting trend towards more
sophisticated tools that can analyze
your app perform optimizations that
would be time-consuming if not
practically impossible to do by hand
best of all code that clearly conveys
intent to other humans has a funny way
of being able to convey intent to these
optimizing compilers - so in 2017 the
idea that builds tools are optional in
my opinion is totally outdated a
sophisticated build process is the
backbone ironically of a modern web app
this morning addy showed a bunch of
techniques for measuring and improving
the performance of your web apps
that's awesome information but not
everyone can have a de standing over
their shoulder and reminding them to
take care about performance and to spend
the time learning all these techniques
so I'm incredibly excited about a
community that could build tools to help
democratize and commoditize that
performance know-how so the best part of
approaching frameworks is compilers
isn't just that they help today's apps
become accessible to more people by
reducing the cost of code we can build
better apps that don't collapse under
the weight of their own complexity by
reducing the cost of code we can finally
dispense with an idea that I hate that
developer happiness and user happiness
are somehow inherently in tension this
is a very exciting time to be a
JavaScript developer I for one am really
looking forward to web apps that load
instantly work offline feel great to use
for everyone Thanks
[Applause]
Thank You film the pie really mixes