[MUSIC PLAYING]
RAHUL YADEV: Good
morning, everybody.
It feels great to be here.
We're really excited to
present our journey of how
we built housing.com,
the mobile version.
So I'm Rahul.
I lead the front-end
team at Housing.
As the name suggests,
we are into home buying.
And for most of our users home
buying is a once in a lifetime
event, and it's a long
journey before they
settle for their dream home.
We have native apps to
facilitate that experience
with native performance,
better re-engagement,
and offline
experience but we also
had few challenges like poor
internet connections, mostly 2G
and 3G, and low-end devices
in terms of power computation,
memory, and storage.
These kept our users
from downloading our apps
and has hindered our business
to reach their goals.
We also had a mobile
website, which
looks something like this.
The problem with this was
it was a monolithic code
base, having desktop and
mobile tangled together.
And the components
were kind of bloated
because of the
fails in JavaScript
and the media
queries in our CSS.
And that eventually
affected our performance.
So we thought about we
have to gather the growing
need of our mobile user base.
We chose to upgrade
our mobile website
to something that can
compete with our native apps.
The reason being simple.
Web has better discovery
and a wider user
base than any other platform.
Also, the cost of bringing
a user to our mobile web
was 50 times cheaper for us
than bringing the same user
to our native apps.
So it was the deal breaker.
We started off building
our mobile website.
The first and the
foremost thing that we had
was to support all the
major browsers that
are out there that
our users use on more
than 2,000 different
devices that they have.
So this was our first team.
And then we thought,
once we have
this part done we'll upgrade
that experience to compete
with our native apps.
So we built Housing
Go, and we were
happy with the kind of
metrics that we saw.
We were able to bring down
our page load times by 30%.
We were having 10%
longer user sessions,
and the bounce rate
was cut down to 40%.
On top of all this,
the most important part
was 38% more conversions
on our mobile website.
That really helped our business
realize the goals much sooner
and effectively than earlier.
I'd like to call up
on stage Ritesh, who
will be taking us through the
journey of how we built this.
RITESH KUMAR: Good morning.
So, I'm Ritesh.
I'm a front-end
developer at Housing.com.
Let's talk about how
we actually built it.
From the start we were
focusing on four key areas.
The first one is that we
wanted to deliver assets fast.
Then we wanted to bring down the
time to first meaningful paint,
and also the first
JS-enabled interaction time.
And at the same time, we had
to improve the experience
of our returning users.
So most of the
performance metrics
actually depend on when
is your asset delivered.
So this is a waterfall
of a traditional website.
First you have whole
HTML loads and then
other assets requests goes.
So you have to wait
for a whole HTML
to load before making a request.
So when you analyze
your code you
will find that there's a
certain part of your code
that needs no computation
or no API requests.
So let's talk about
HTML streaming.
Now this is how it looks
like on client side.
First we send the
initial chunk that
only contains the code
that needs no computation.
You can see this
preconnect, preload,
and the critical inline CSS.
Now sending the preload we
actually start the request
for critical JavaScript earlier.
I will talk about them.
So now, this is the full HTML.
After the server
has made API request
and it has received
all the responses
it sends all the HTML.
Now this has body,
initial render, and tag.
And this is all the content.
Now the size of full
HTML is around 15
KB but the first response
that the server sent
was around 4.2 KB, gzipped.
So preload.
Most of the time the
developers already
know that a particular
route is going to need
a few critical resources.
You can load them in advance.
And so, by using HTML streaming
and preloading combination
we were able to start the
request for critical JS
much earlier than other assets.
So after we were done
with asset delivery
we went ahead to
improve our render time.
And by render I mean the
first meaningful paint.
Now the difference between
first pain and first meaningful
paint.
On the left side you
see that first paint.
First paint is when there's
anything, any pixel [INAUDIBLE]
on your phone.
Most of the time, that's
not relevant and the user
feels like waiting.
And when the relevant
content is there,
that's the first
meaningful paint.
Now we wanted to zero down the
difference between these two.
So we experimented with
server side rendering.
I am saying, "experimented,"
because you should always
mess up before you implement.
So this is a traditional app
shell model on first load.
Until 2.2 seconds there's
a white screen of death.
You have nothing to see.
Then there's a state
where you have something
but it's not
relevant to the user.
He still has to wait.
And around seven seconds he sees
the first meaningful content.
Now the region before 2.2
seconds and seven seconds
is what we call the loading
screen of purgatory.
So the user doesn't know
what's going to happen.
He may receive the content.
He has to wait for a
certain amount of time.
And if there's any error he
will have to wait in that state
forever.
So we wanted to improve this.
We wanted to remove
this totally.
So this is after SSI is enabled.
Now when we implement
the server side
rendering the first meaningful
paint happened at 2.3 seconds.
And that was quite
an improvement.
And all these test have
been run with WebPagetest.
It is written on the bottom.
So there's also a bonus
when we implemented
this outside rendering that
the basic meaningful content is
available for everyone.
As Rahul said, we have users
using more than 2,000 types
of devices.
And so there are a variety of
users with different browsers,
the versions may be older ones.
So the basic content is
rendered for all of them.
So that's a bonus.
So until now I have
talk in bits and pieces
about how we improve
JS-enabled interaction
but the main thing
that you need to do
for improving
JS-enabled interaction
is that you need to
shift less code, less JS.
So less of the JS, faster will
it be the interaction time.
Now when you ship
less code you will
have to lazy load
resources on demand,
and this brings us
to code splitting.
We are Webpack 2
for code splitting,
and we do both JS
and CSS sharding.
So generally, the
chunks that we make we
divide them into two categories.
The first one of them
is route-based chunks.
Now when a user lands on
a particular route, first
we make our call for the main
JS file that you will require.
And in parallel
we make a request
for the corresponding CSS file.
So when the CSS file has been
loaded we allow the navigation.
And after he's
idle we make a call
for the next probable route
that he might navigate to
so that when the user
navigates to that route,
the transition is
almost instant.
So next I come to
the second category.
That's intent-based chunks.
These are the
chunks that are only
required when the user does a
particular kind of interaction,
like scrolling or click, and
doesn't involve any route
change.
Now let's take an example.
This is our listings page.
On top right you can
see a Notify button.
Now, by analytics
data we know that this
is a kind of button that gets
clicked once in few sessions.
And the corresponding
view that it requires
has [INAUDIBLE] 32 KB we unzip.
So it makes no sense to load
that with the main JS bundle
because most of the
time it will go unused.
So we require it only
when the user has clicked
on that particular button.
So after doing all
this, currently we
are at a stage where our
first meaningful paint happens
at around 2.3 seconds and the
first JS-enabled interaction
starts at around 4.2 seconds.
Now, by JS-enabled
interaction here
I don't mean the DOM
content is loaded.
We have defined a custom metric.
Like we calculated this vendor
component actually mounts.
Since we are using
React, for us it
is actually component
[INAUDIBLE] mount.
So this is the time when the
component mount gets triggered.
So I've talked about how we
pushed critical resources using
preload.
We have improved render using
SSR and inlining critical CSS.
We are precaching assets using
Service Workers, as Rahul
will tell you, and we are lazy
loading resources on demand.
So we are very much near to
what the PRPL pattern promotes.
And so, until now
what I have discussed
has improved the experience
of first time users.
Now we also had to improve the
experience of returning users.
So I'll call back Rahul to
tell you more about that.
RAHUL YADEV: So as I
told you, finding a dream
home is a long journey.
User comes online multiple
times to make his searches,
to compare the
houses he's seeing,
the properties he's seeing.
So it was very important for us
to make a compelling returning
user experience so that
we make that journey as
smooth as possible.
We use Service Workers to
precache a few resources
on install and act as a proxy
for our subsequent [INAUDIBLE]
resources so that it can
solve them from the cache
directly when it's
requested a second time.
By doing this, we have
been able to bring down
the first meaningful
paint that happens
for a first-time user
at around 2.2 seconds,
2,200 milliseconds, and the
first JS-enabled interaction
from 4.2 seconds for a
first-time user to 1.1 seconds.
We also implemented
[INAUDIBLE] features
to give users the access to
instantly interact with our app
directly from
their home screens.
And we implemented
push notifications.
I like to mention that the kind
of conversion rates that we are
having from push notifications
are almost beating few
of the channels that we have.
So that's one thing that's
taking us closer to our apps.
The offline navigation,
this was important for us
because whenever a user visits
for the actual site visit,
the properties are generally
at the outskirts of cities
where the network is very
flanky or absolutely no network.
So this experience helped
them to actually revisit
their session or re-look
at the properties
they have already
done on the mobile.
We use credentials
management API
to keep our users almost logged
in virtually almost every time
so that their information is
synced across devices very
smoothly.
So once you are done
building the app
the main question that we had
is how do we maintain this?
We had done a lot of things
to maintain the first paint
time, first JS-enabled
interaction time.
But as the product evolved
with a lot of features
it's very difficult to keep
in check these metrics.
So we came up with
our own system
of continuous integration
with Webpack and WebPagetest,
and we made it as easy as
just putting a label on a PR
in GitHub.
So if you're done with your
code and you raise a PR,
you just need to put
a label run test,
and we'll put out all
the information that
needs to track these metrics
right on the GitHub PR,
like the chunk sizes.
It helps you know
that how this PR is
changing on modifying the chunk
sizes that we already serve.
A few route-based statistics,
like the first paint,
what's the speed index of a
few of the critical routes
that we take care of.
Also, the network and
the timeline view.
This helps the
viewer get any sense
of what resources are loading
and how they are loaded.
So I think this was
very important to us
to close the loop of
the complete development
from the development
and maintenance.
We are here to do a lot of
things to make the app faster,
and one of the things that
we're experimenting with
is moving from React to preact.
We have seen that in
our initial prototypes,
moving from React to preact
has brought down a difference
of 122 KB in our vendor window.
And that's huge.
Like that's around 700
to 800 milliseconds
of JS-enabled interaction
time gain on the client side.
We are also
experimenting with AMP
to let our users have almost
an instant experience when they
come through Google's page.
Thank you.
Thank you all.
PAUL: Hello, over here.
Hello.
Jake, are you there?
JAKE: Yeah Paul, I'm here.
PAUL: OK.
JAKE: We were asked to do a
quick handover so we've cut
the bit where we walk onstage.
That was Housing.com.
PAUL: And here's Lyft.
JAKE: Yea.
MALCOLM ONG: Well good
morning, everyone.
I'm Malcolm Ong,
product manager at Lyft.
And here with me
today is my colleague
Mohsen Azimi, lead
engineer on our project.
And we're going to
tell you a little bit
about our journey of how we
built our Progressive Web
App at Lyft.
So just a show of
hands, how many of you
have taken Lyft before?
All right, there
are a lot of you.
Awesome.
Well for those of
you unfamiliar,
Lyft is the fastest growing
on-demand transportation
service in the US.
It actually came out
of a hackathon project
from our original
product, Zimride.
And so similarly,
our ride.lyft project
also came out of a
company hackathon.
And so, certainly,
in the very beginning
when we came up
with the idea, there
was a lot of internal
skepticism as
to why we would build a web
app at all, if we did, could we
actually build a web app that
would be a viable alternative
to our native apps.
And certainly this makes sense
because historically, Lyft's
been a native
mobile first company
has invested a lot
of time, resources,
et cetera on the native apps.
And so we said, well, let's
go ahead and take a stab at it
and see what we come up with.
And I think we're pretty
happy with our work.
This is a desktop view
of the app that we built.
And our hackathon
project gathered
enough internal
excitement that we said,
you know, why don't we go ahead,
go out, try to procductize this
and see if our users would
also be just as excited.
And so some of the
reasons why we built this.
From our standpoint, a PWA
could be a great supplement
to native apps for
various reasons.
Number one, greater
reach, so essentially
allowing a lot of
our users that are
unsupported or on aged-out
devices to use Lyft.
Number two, reduce friction.
So certainly pushing users
through an app store funnel
is very, very inefficient.
And number three, faster
uploads and experimentation.
So let's talk through
each of these.
So greater reach.
On the pie chart that you see
on the right, approximately 8%
of iOS users in the market
and 3% of Android users
will soon be
unsupported as we slowly
deprecate older OS versions.
So our Progressive
Web App allows
us to support these users.
Furthermore, 100% of
Windows mobile and 100%
of Amazon Fire
devices, for example,
simply because we never had
Amazon Fire app until our PWA.
And we still don't yet have a
native app in Windows mobile
either.
So this allows us to
support these users.
And in addition to
this, obviously, it
reduces a lot of operational
costs, technical costs,
resources because it means
less code and potentially less
incidence or support tickets.
Numbers two, reduce friction.
So as we all know, sending users
through an apps store funnel
is highly inefficient,
is high drop-offs,
high cost per installs.
And so the funnel that you
see on the right there,
we can potentially
go from six steps,
right, starting from web
entry all the way down
through the app
store towards sign-up
and, finally, a first ride.
So it's been said that
every step of this
funnel you could essentially
lose 20% of your users.
And so what are
reasons for this?
I mean, maybe it's
because I don't know,
users don't like the permissions
that you are asking about.
Or maybe because
it's they don't have
enough storage on their phones.
They have all those Snapchat
videos that they've saved.
So we can basically change
this to the three step process,
if you will.
So in other words,
imagine the PWA
can replace the white
portion of that funnel.
Another interesting
thing is deep linking.
So imagine we had a developer
partner app like Google Maps,
and Google Maps has
integrated Lyft,
and they want to send users to a
seamless experience from Google
Maps to Lyft.
Right now, if you don't
have the app installed
it sends you to the app store.
Very, very not as
seamless as it could be.
So deep linking from
developer partner apps
straight into our
Progressive Web App
is a much more seamless
experience to do that.
And finally, faster deploys.
So certainly, deploying fixes,
code, experiments on web
takes hours not weeks.
There's no need for this app
approval process, if you will.
And so running experiments
faster, A/B testing.
And certainly, our app
is always up to date.
You don't have to
constantly update your app.
And in terms of
productivity and timeline,
our team actually just started
with just basically one
engineer, Mohsen.
And our alpha NBP
that he built was
built on top of an Angular
stack primarily because Lyft's
historically been on Angular.
And we were able to do that
in roughly two month's time.
And so we proved that our
PWA had a lot of promise.
We, of course, ate our own dog
food, leveraged our public API,
it's handled a lot of
server side things.
Had a little bit of support
from like design and QA.
But for the most part,
this was pretty quick
compared to developing
a native app.
And so once we could quickly
proved the potential of this
we got enough internal
support to get more engineers
on the team and worked on a new
app, a beta version of this,
from the ground up
in React, and was
able to do this in
roughly one month's time.
And so, now I'd like
to bring out Mohsen
to talk a little bit more
about the technical aspects
of the app.
MOHSEN AZIMI: Hello everyone.
I'm here to tell you a
story, a story about a ride.
Meet Valerie.
She just saw DJ Khaled
is giving undercover Lyft
rides so she wants to try
maybe he's her driver.
So she goes to lyft.com.
And what you have in lyft.com
is this big pink button
that says Request a Ride.
You don't force users
to download the app.
They can just request a
ride right there in the web.
This is really
important because if you
force them to
download the app they
have to download almost
75 megabytes of data just
to start.
So this is very PWA wins.
You might think,
OK, if she downloads
the iOS app it's going to
be a better user experience,
it's going to load faster.
But in fact, our PWA loads
faster than our US app.
This is on the LTE network
with a good device.
But with slower networks
and slower devices
we get good results,
acceptable results.
So now that she's in our
PWA she needs to sign up.
For sign up in Lyft we ask for
your phone number and a payment
method.
If you have your
web payments set up
you don't need to put
in your credit card,
just use Android Pay
with web payment API.
So she can do that.
With two taps she has an account
and she can pay for her ride.
When she requests a ride--
Oh, I forgot about this.
I talked about how the PWA
is a better user experience,
and that means in any
front, animations,
anything is same
as a native app.
OK.
So now that she
wants to take a ride,
when she taps request
ride, what we do is
we register the
Service Worker that
listens to push notifications
from our servers.
So if her driver is
coming in two minutes
she can put her
phone in the pocket
and wait for the
driver to come in.
When driver is here, we
send this push notification
that lets her know
that driver is here.
This push notification
do another thing.
It has a payload that
updates the right information
in our app.
So that means, if she taps
on this push notification
we are going to
take her to our PWA
without making any network
request because push
notification had all the data.
When she's done with
the ride, we also
send a push notification.
That push notification, as
well, includes ride information.
So even if she's offline
she can open up our PWA
and access this
screen, which has
all the information
about the ride, who
the driver was, how much it
cost, and things like that.
This is really cool but
it doesn't stop here.
Thanks to background sync
she can submit her feedback
while she's offline.
When she does this what we
do in the Service Worker
is we are making a post request,
putting it in the cache,
and then Service Worker is
going to wait for online event.
When that happens
this post request
is going to be submitted.
This is without her interaction.
It's happening in background.
I think this is
really, really cool.
So making this
Progressive Web App
was a really, really
interesting journey for us.
We are doing it for
about two months.
It's very early but we
learned so many lessons
that I want to share a
few of them with you.
As other speakers here told
you, less JavaScript is better.
As much as we love to add new
libraries and new dependencies
to our code we should always
be considerate about the amount
of JavaScript we
push to our users.
This is really, really
important for mobile users.
Your regular MacBook
Pro that you develop on
is way more faster
than the phone
that your users are using.
That's why in Lyft, we are using
real devices for development
and testing.
We forbid ourselves
from using our Macs
or these powerful
computers for development.
It's not the most
pleasant experience
but that's the user
experience you are working on.
Another thing that we
did was minification.
I'm sure all of you minify
your have JavaScript.
But we did something new,
which is thanks to Angular team
with their TypeScript to Closure
Compiler annotated JavaScript
project.
With that, we were able to
minify JavaScript even further.
A method name in minified--
regular minification process
will not change because
minifier doesn't
know where this method is being
called so you can't rename it
all over the place.
But with tighter scripts
and Closure Compiler
we were able to minify
it even further.
The other thing that
we did with JavaScript
was tree shaking,
which is now much
easier than before with
Webpack and the new minifiers.
Here's a quick look at
our technology stack.
You're not using a lot of
dependencies, as I said,
just React and a
little bit of Webpack.
And things are
working pretty great.
Our bundle size is just
some 40 kilobytes, gzipped.
We had challenges
doing this work too.
The biggest challenge
was the other platform.
But at the same time,
all these PWA APIs
are pretty, pretty
new, and sometimes we
saw [? MDN ?] documents
that are out of date.
There is not much Stack
Overflow questions to look for.
You just ask questions
and get answers.
So that is an interesting
challenge to have.
The other one is
not in our control.
So hopefully, that's going
to be fixed next year.
I have one little
lesson we learned,
and that is animation
for opacity.
If you use CSS
animation for opacity,
infinitely, it's going
to crash your browser.
Don't do that.
So now I'm going to
hand it over to Malcolm
to talk about business
impact of this PWA.
Thank you everyone.
MALCOLM ONG: All right.
Thank you Mohsen.
And so putting this
all together, what
was our early business impact?
The initial response
from our alpha
was certainly very,
very positive.
We exceeded our initial
weekly rides, number
of weekly rides goal by five x.
We were able to launch
a app, essentially,
a wrapper around our PWA,
into the Amazon Fire App Store
very, very quickly.
And if you're
interested, there's
a link up there to learn more.
And finally, we
estimate that we'll
have up to a 50% improvement
in forced upgrade churn.
So this is, again, folks that
we eventually have to force
upgrade due to having
lower or older OS's.
And so next steps, you
know this is certainly
still a very, very early beta.
We're still iterating
on it a lot.
It will be buggy but we
encourage you to try it out.
Number one for next
steps, we definitely
like to prove and optimize
our conversion funnels.
Number two, experiment a lot.
And number three, finally start
adding a lot more features
onto this so that we can
actually reach feature parity
with our native apps.
And so, I wanted to thank
everyone here for listening.
Some of our team members will
be here at the conference
later today to
answer any questions
you might have or just chat.
And if you'd like to give it a
try just go to ride.lyft.com.
And in fact, we have a coupon
code for you to use for 20%
off your ride.
All right, thanks everyone.
[APPLAUSE]
[MUSIC PLAYING]
