droidcon San Francisco 2019
Tweet |
droidcon News
Powering Worldwide Learning with Kotlin Multiplatform
By
Ankush Gupta
droidcon San Francisco 2019
Learn how Quizlet uses Kotlin Multiplatform to build iOS, Android, and web learning experiences for over 50 million users per month.
By pinpointing the best areas of our codebase to share (and skipping over many others), Quizlet was able to use Kotlin Multiplatform to share the “secret sauce” of our product. We did this without forcing our Android, iOS, and Web clients to follow a rigid, predetermined architecture.
You'll hear Quizlet's journey getting Kotlin Multiplatform into production, how it compares to other approaches of sharing code, and (most importantly) lessons about shared code that we learned along the way.
Transcript
English
00:00
[Music]
00:11
all righty friends I'm gonna start a
00:14
minute or two early because I've got a
00:16
ton of slides and I don't want to have
00:19
to start rushing at the very end so yeah
00:22
before I guess before I begin my name is
00:24
dawn Kosh I'm a software engineer at
00:26
Quizlet here in San Francisco I've been
00:29
involved with the Quizlet Android app
00:30
for quite a while I wrote its first
00:32
lines of code as an intern a little over
00:34
six years ago I'm gonna start by sharing
00:37
a teeny tiny bit about you know what
00:40
Quizlet is what our history is to kind
00:41
of set the stage for how and when and
00:44
why we decided that shared code was
00:46
something that we wanted to invest in
00:49
I'm also gonna give a kind of super
00:52
brief overview of a few different
00:54
approaches that other folks have taken
00:55
to shared code so that you know can kind
00:58
of have a more big picture idea of the
01:01
ecosystem as a whole and then I'm gonna
01:03
get into the juicy part I'll to start
01:04
talking about quizlets specific journey
01:07
with shared code and lessons we learned
01:08
along the way and why we ended up
01:11
deciding and how we ended up evaluating
01:14
Kotlin multi-platform as our current
01:16
tool for sharing code so for folks who
01:21
don't know us about don't know about us
01:23
already quizlets mission is to help our
01:26
users practice and master whatever it is
01:28
that they're trying to learn we provide
01:31
tools for our users to create and share
01:34
knowledge rigid with each other and we
01:37
also build experiences to help our users
01:40
learn the information that other folks
01:42
may have shared with them or that they
01:44
may have shared with other folks like a
01:48
bunch of startups Quizlet began as a
01:50
product for a single platform and that
01:52
platform is the web our core product
01:56
offering was essentially user generated
01:58
digital flash cards so that folks could
02:01
originally it was designed just to
02:03
memorize vocabulary three o'clock and
02:06
things are simple back then in order to
02:08
assess if a user had you know mastered
02:11
some vocabulary we would pick a random
02:12
element
02:13
from this big list of words that they're
02:15
trying to study ask them to type in an
02:17
answer and if what they typed in matched
02:19
what the database said the right answer
02:20
was they got it right it was just a
02:23
basic string equality check things are
02:25
super super simple back then and you
02:28
hope things change over time for the
02:30
better as the years progressed Quizlet
02:33
spread from you know from learner to
02:35
learner from educator to educator and we
02:38
started growing we had tens of millions
02:41
of users before we knew it
02:43
millions of studies millions of sets of
02:46
study material were being studied on
02:48
Quizlet spanning thousands of different
02:51
subjects across several different
02:53
modalities that users want to be able to
02:55
interact with their content in and
02:58
things you know all the different
03:00
permutations you can imagine here
03:02
different types of users different types
03:03
of study material things start getting
03:06
really complicated when you have that
03:08
amount of a variety and in order to
03:11
handle this we basically needed to be
03:13
smart we started doing some stuff to
03:17
basically create specialized
03:19
domain-specific logic to make this
03:21
process as easy for our users as we
03:24
could some examples of things that we
03:27
built were things like standardized
03:28
analytics events so that we can track
03:30
learning outcomes because if you can't
03:32
track if your users are learning things
03:34
more effectively you can't evaluate if
03:36
your new features they're helping them
03:37
learn things more effectively we built
03:40
things like context-dependent grading
03:42
rules where you know if we use our
03:43
studying math or a user studying German
03:45
or if a user studying chemistry things
03:47
like basic string equality aren't really
03:50
the best way to evaluate if a user knows
03:52
the answer or not and we went all the
03:55
way we've gone all the way down to you
03:57
know using the latest kind of brain and
03:60
cognitive science research out there to
04:01
model you know how users answer history
04:04
might predict what the most useful thing
04:06
for them to be studying is so that they
04:09
can better learn and retain information
04:11
over time and you know each of these
04:14
things is super important and super
04:16
closely linked to quizlets mission of
04:18
making education better and more
04:19
accessible and each of these things also
04:22
requires a pretty deep domain specific
04:25
understanding of quizlets specific
04:27
product in Quizlet specific market it's
04:29
you know it's a little bit it's a lot
04:31
more advanced than what it would take to
04:33
just build a simple application that
04:35
reads and writes to and from a database
04:37
like we originally had so he basically
04:40
went from a web client that talks to and
04:42
from an API something pretty simple to
04:45
something a lot more complicated where
04:47
we had this you know what started off as
04:49
just a simple front-end to talk to the
04:50
web became pretty feature-packed
04:54
and all these things that are in there
04:55
are very important but it's also very
04:58
complicated and this is what I like to
05:02
call our secret sauce the secret sauce
05:05
is kind of what takes a nap and kind of
05:07
elevates it from a view into a database
05:10
into a product that can deliver you know
05:13
delightful experiences to our users it's
05:16
directly responsible for you know
05:17
empowering users and helping them learn
05:19
things at Quizlet and it's also a big
05:21
reason why we're able to you know build
05:23
a product that is pretty solid and can
05:25
out-compete many other competitors out
05:27
there and you know it's easy to see that
05:31
continuing to invest in a secret sauce
05:33
lets you build a much better product but
05:36
Quizlet was starting to reach a phase
05:37
where it was easier said than done and
05:39
that's because we needed to build our
05:42
mobile apps so rewriting the secret
05:46
sauce across multiple platforms is
05:49
tricky enough if all your platforms are
05:52
starting from the same place and you're
05:54
writing it once on all three platforms
05:55
but the reality is our mobile teams were
05:58
building out standard you know API
05:60
interaction standard UIs we didn't have
06:02
the resources to build those and at the
06:05
same time reimplemented sauce that was
06:07
already kind of established on the web
06:08
so if we wanted to continue to make
06:11
Quizlet you know more magical for our
06:13
users and build cool new things we had
06:15
to kind of address this problem head-on
06:17
and you know one option that we had was
06:19
hey let's declare a feature parity
06:21
bankruptcy you know maybe we wouldn't we
06:23
just wouldn't support these features on
06:25
mobile clients we would make the mobile
06:26
clients just talk to the API and that's
06:30
it like if you wanted to do some of the
06:31
more advanced stuff you had to use the
06:33
website the you know the more literal
06:35
interpretation is that we probably would
06:37
have to stop doing this as a client-side
06:39
feature we would do some
06:40
like gating all the secret sauce behind
06:42
the API and that's that's the only way
06:45
for the mobile clients to to use the
06:47
sauce is just by talking to the API and
06:49
for many businesses this might make a
06:52
ton of sense you know if you're a bank
06:54
you probably don't want all this stuff
06:56
being on the client anyway but at
06:59
Quizlet we have a ton of younger users
07:01
we have learners in developing countries
07:03
who are using mobile devices as their
07:05
primary way of you know using apps or
07:09
you know consuming services online and
07:12
they tend to have less reliable
07:14
connections to the Internet furthermore
07:17
I think our secret sauce was in an
07:19
interesting place if we if it was
07:21
something like a content recommendation
07:23
where you only have to call a server a
07:25
few times per session this is something
07:27
you know maybe it's something it could
07:28
be run in the background something that
07:30
you could cache the result over time and
07:32
show something stale if you needed to
07:34
and maybe even fallback gracefully and
07:36
say hey sorry like you can you know it's
07:37
like we don't have any content to
07:38
recommend to you right now that might be
07:40
completely fine in many apps as well but
07:43
for Quizlet things like grading a user's
07:45
content that they'd that they answered
07:47
and suggesting the next thing for them
07:49
to study as they're studying through
07:50
their study material is something that
07:52
you know you don't we didn't want to
07:54
introduce friction to if it took even
07:56
like a single second it would pretty
07:57
drastically harm the studying experience
07:60
for our users and so for that reason we
08:03
basically couldn't do this kind of an
08:05
approach where the client just talks to
08:07
the server a hundred percent of the time
08:08
so the other option was you know hey
08:12
let's take a breather let's stop writing
08:14
more secret sauce so that the mobile
08:15
teams can you know finish catching up to
08:18
where web is and re-implemented three
08:20
times and you know that might work and
08:23
there's ways that that folks have
08:24
explored where maybe you invest in a
08:26
common test suite to test that that
08:29
logic is consistent across platforms but
08:32
still you know if the secret sauce is
08:33
what got us to have such a successful
08:35
web app we understandably didn't want to
08:37
just slow down so that we could catch up
08:39
on mobile and that's what kind of led us
08:42
to this inevitable conclusion that we
08:44
had to invest in in our secret sauce and
08:47
for some businesses this might just mean
08:49
let's just hire a bunch of developers so
08:50
that we can rewrite it three times and
08:52
keep going from there
08:53
we did not have the money at the time to
08:55
do that so the approach that caught our
08:58
eye was investing in our ability to
09:00
write our you know super specific domain
09:02
specific code once and share it across
09:05
platforms so let's take a take a look at
09:09
a few different popular ways in the
09:10
industry right now that folks do this
09:12
one approach is kind of this to share
09:15
all the things approach and the promise
09:17
is you you hypothetically can write your
09:19
app once and run it on multiple
09:21
platforms and when I say app I don't
09:22
necessarily mean your entire app it you
09:25
know in quizlets context and in the
09:26
context of many companies out there it's
09:28
more like probably a feature slice like
09:31
you pick a feature and you try to
09:33
implement that entire feature using
09:34
whatever code sharing platform that you
09:36
want and some examples of this are you
09:40
know react native at Airbnb Pinterest
09:42
and next door a more you know popular
09:44
example nowadays is something like
09:45
flutter and unfortunately many of the
09:49
large companies that we're pioneering in
09:51
react native ended up having to
09:54
discontinue their use of it for a
09:55
variety of factors some of the realities
09:58
were that there's a huge reliance on
10:01
bridging infrastructure and a lot of the
10:03
companies that we're trying to use react
10:05
native ended up having to spend a lot of
10:07
time writing and maintaining this
10:08
bridging infrastructure themselves it
10:11
it's difficult to seamlessly integrate
10:13
with existing native apps which is where
10:15
Quizlet was kind of coming from and
10:16
which is where a lot of these bigger
10:17
companies were coming from as well where
10:19
if you have a team that is split between
10:22
writing native code and writing react
10:25
native code it can be a little tricky to
10:27
manage just the organizational aspects
10:30
of it before you even start to deal with
10:32
the technical aspects of how you crossed
10:34
that boundary performance issues are
10:37
pretty commonly a you know commonly
10:40
cited reason why react native isn't
10:41
great mainly due to differences in the
10:43
threading models UI can end up feeling
10:46
non-native because you're using
10:48
abstractions across platforms and things
10:51
like the less mature frameworks and
10:52
libraries mean that it's a little
10:55
trickier to deeply integrate with the OS
10:58
that you're working on and again that
10:60
means that you might end up spending
11:01
time on the bridging infrastructure
11:03
instead of writing the code that you're
11:04
trying to write here's a quote
11:07
from an engineer at next door basically
11:09
the quota is every company that I know
11:12
of that has tried this hybrid approach
11:13
has walked it back months later the
11:15
companies that seem successful to react
11:17
native seem to be the ones that have
11:19
their entire app and react native and
11:20
started out the app that way we're still
11:22
in the process of winding down react
11:24
native here at next door and so this
11:26
kind of does a pretty good job of
11:27
consolidating things I'm trying to say
11:29
it's not that react native is bad it's
11:32
that when you end up trying to build a
11:34
hybrid app you end up also having to
11:36
deal with the downfalls of kind of
11:40
approach where you have to bridge this
11:41
gap at some point or another so another
11:46
another pattern out there is sharing non
11:49
UI code you know if sharing not if
11:52
sharing UI code is pretty bad how about
11:53
we just don't share UI code and the idea
11:56
is you know you can write native UI and
11:59
then you can write non UI code only once
12:01
and again maybe you'd write a native UI
12:03
for a specific feature and then the non
12:05
UI code for that feature only once and
12:07
some examples of this are c+ were worth
12:10
c++ at Dropbox slack and safety culture
12:13
and so Dropbox there was actually a talk
12:15
earlier today about a blog post about
12:18
how they had you know they'd use shared
12:20
C++ to power features like syncing
12:22
camera rolls and that ended up
12:24
introducing a downstream dependency on
12:26
things like file i/o networking storage
12:29
and they ended up not having a great
12:31
time with that and so some of the
12:35
realities here niche tooling with things
12:38
like Dropbox where they had to maintain
12:40
their own library to generate j'ni
12:42
bindings they can lead to hiring and
12:45
retention issues like if it's not fun to
12:47
work on your code people will work where
12:50
it's more fun
12:50
it requires things like strong
12:52
concurrency support if you end up having
12:54
to rely on you know storage and
12:57
networking and concurrency and there's
12:60
potential performance and typing issues
13:01
specifically at the boundaries I think
13:03
C++ is relatively performant when you're
13:05
within C++ it's pretty darn performant
13:07
but then when you end up at the edge
13:09
where you need to talk to Java it's not
13:12
it that's an interesting place where
13:13
performance complications can arise it's
13:16
also much more difficult to share code
13:17
with front-end web like was a
13:19
requirement for us
13:20
and some things just need to be
13:22
different across platforms it's it can
13:23
be a little tricky when you're writing
13:24
shared C++ code to say like hey if I'm
13:27
on iOS talk to this part of the the iOS
13:30
system whereas if I'm on Android do this
13:31
differently and a quote from the Dropbox
13:34
blog post was although writing shared
13:36
code once sounds like a great bargain
13:38
its associated overhead outweighed the
13:40
benefits which turned out to be smaller
13:42
than expected anyway in the end we no
13:44
longer share mobile code by a c++ and
13:46
instead write write code in platform
13:48
native languages slack also recently had
13:51
a blog post announcing that they had
13:53
stopped developing their internal C++
13:55
library a while ago and they instead
13:57
rewrite their implementation across
13:59
clients and had invested in some
14:01
engineering organizational changes to
14:03
make sure that those things were
14:04
consistent the quote here is while we no
14:07
longer while we are no longer building a
14:09
shared library slack still needs to
14:11
maintain consistency and reduce
14:12
duplication of effort while developing
14:14
separate implementations of client
14:16
infrastructure so now what common
14:21
threads with some of the previous things
14:22
were their reliance on cross-platform
14:24
abstractions for for UI for persistence
14:27
for networking for serialization for
14:29
concurrency and relying on
14:31
cross-platform abstractions for that
14:33
kind of stuff from what from a we've
14:35
seen tends to either oversimplify to the
14:38
point where you have to bypass the
14:39
framework to do what you're trying to do
14:41
or overcomplicates to the point where
14:43
it's no longer fun to actually write the
14:44
code because you have to maintain all
14:46
sorts of weird edge case logic so where
14:49
does that leave us you know why
14:51
certainly have been nice to you know
14:54
write our entire code base once and
14:56
share it everywhere all of the engineers
14:59
at Quizlet and and hopefully all the
15:01
engineers in this room kind of know what
15:03
resources they have at their disposal to
15:05
build UIs on Android and across other
15:08
platforms to build and interact with
15:10
api's to interact with local databases
15:13
what's a lot more complicated at Quizlet
15:15
were the things like the ins and outs of
15:19
German language grammar you know keeping
15:22
up with cognitive science research so
15:24
that we can help our users ask answer
15:26
the right questions instead of wasting
15:28
time on things that they might already
15:29
know so what we decided to do was share
15:33
our secret sauce
15:34
instead of building shared abstractions
15:37
around the database or around UI unlike
15:39
a bunch of other companies we basically
15:42
had this product requirement that our
15:45
secret sauce had to be on device so we
15:48
had a little bit more pressing concerns
15:49
and iterating super quickly on shared UI
15:51
across platforms our secret sauce also
15:53
doesn't rely on things like the
15:55
fine-grained details of like network
15:57
conditions or disk i/o like Dropbox ran
15:60
into that made her a particularly great
16:03
candidate for shared code so how exactly
16:05
did we do at a Quizlet we use javascript
16:09
our first attempt at sharing code it was
16:12
to reuse that we had already written for
16:13
the web you know the Quizlet secret
16:15
sauce was originally written in
16:16
javascript to target our web client so
16:19
why don't we just spit out a Java Script
16:21
artifact and shove it into our mobile
16:22
apps through a JavaScript engine and we
16:25
we were able to do that we were able to
16:27
spit out you know standalone JavaScript
16:29
artifacts for each aspect of our you
16:32
know secret sauce that could be consumed
16:34
in the mobile apps we would bundle that
16:36
file into the app and run it through an
16:38
on device j/s engine we would use disk
16:41
IO to read this big file into memory we
16:43
would take that string and pass it into
16:46
whatever flavor JavaScript engine we
16:47
were using at the time on the mobile
16:49
clients and we would be able to interact
16:51
with it to some degree or another and
16:53
with pretty sizable performance hits but
16:56
it worked it was far from perfect unlike
16:59
you know real native code interacting
17:01
with strings and weird non-standard
17:03
objects is not a fun way to write code
17:07
it was pretty error-prone where you
17:09
didn't we didn't have any compile time
17:11
safety of you know what exactly are we
17:12
passing in we would be constructing long
17:15
strings to call into functions that you
17:18
know hypothetically we knew were
17:19
supposed to exist in this minified
17:21
javascript file but it still worked you
17:25
know none of this shared code was being
17:27
run in a first-class mobile runtime and
17:29
so debugging stuff when things went
17:32
wrong was not very fun figuring out why
17:35
crashes were happening was not very fun
17:37
but it worked
17:38
on Android we you had to use a library
17:41
called j2 v8 because it was the only one
17:43
that was performant enough to to run
17:45
this giant complicated rule engine or
17:48
to use these giant complicated state
17:49
machines and it that that runtime uses
17:53
j'ni to talk to you know in an instance
17:55
of the v8 JavaScript engine compiled
17:58
down to C++ it calls our apk size to
18:00
almost double but it still worked and
18:03
this isn't even mentioning you know the
18:05
general disdain for JavaScript among all
18:08
of our mobile engineers at the time so
18:12
you know fronting what web developers
18:14
may have felt comfortable writing this
18:16
secret sauce in JavaScript but mobile
18:18
developers didn't really feel
18:19
comfortable consuming it let alone
18:22
trying to write any of it but it worked
18:24
and that was a huge win for Quizlet just
18:28
getting to the point where we were able
18:30
to unblock our mobile clients from you
18:32
know being able to write and use these
18:34
features that are pre central and core
18:36
to the Quizlet product was a huge huge
18:38
huge win we were able to do this because
18:41
we focused on state machines and rule
18:43
engines we were able to do this without
18:45
committing you know our apps to being in
18:47
some complicated framework because this
18:49
was just a black box that we could pass
18:51
things into and get the new state out of
18:53
we could use you know whatever you know
18:55
architecture we wanted to in our apps
18:57
the iOS app didn't have to use the same
18:59
architecture as an Android app which
19:01
didn't have to use the same architecture
19:03
as a web app and we learned a bunch of
19:05
key lessons from this experience
19:06
the first is clean interfaces are great
19:11
writing code with clean interface
19:12
boundaries made it super super awesome
19:15
when we had to extract them out later to
19:18
be used as shared code if we had you
19:20
know intermingled our web UI code with
19:23
the secret sauce code it would have been
19:25
a nightmare to get that same code to run
19:27
again on android or iOS devices so by
19:30
you know isolating it isolating our
19:31
vital business logic into those state
19:33
machines and pulling them away from the
19:36
regular application code we were able to
19:37
build and use clean interfaces in our
19:39
shared code we also learned that it's
19:42
very important to aggressively validate
19:44
inputs and outputs from shared code it's
19:46
really easy for misunderstandings about
19:48
the meanings of parameters to arise when
19:51
you're building complicated modules that
19:53
are being consumed by developers who
19:55
might not have had any input in actually
19:57
writing the module itself and
19:59
aggressively evaluating for unexpected
20:01
but especially in a loosely typed
20:04
language like JavaScript helps minimize
20:06
those issues that might arise due to
20:08
miscommunication or due to under
20:10
documentation I hopefully don't have to
20:14
explain to everyone in this room why
20:15
tests are good but tests are extra extra
20:18
good when you're writing shared code
20:20
with shared code it can be a pain to
20:24
debug in a host client like I mentioned
20:27
earlier where if you have a JavaScript
20:28
file that you're trying to run you you
20:30
don't want to have to debug that code so
20:33
a properly tested shared code is code
20:35
that you don't have to spend as much
20:36
time debugging on a host client properly
20:40
tested shared code is code that you also
20:42
don't have to go back and make changes
20:43
to it takes a little bit of effort to
20:46
you know find the change that you need
20:48
to make due to a bug go back into let's
20:51
say JavaScript to make that change
20:53
repackage all that into a nice little
20:55
artifact and should we ship it again
20:56
into the mobile app just to test if it
20:58
worked if you have tests that do that
20:60
for you and that cache those kinds of
21:01
issues earlier you save a lot of cycles
21:03
compared to you know the same you know
21:06
the same approach might work just fine
21:07
when you're writing regular native code
21:10
where you just test by manually seeing
21:12
if it worked but that doesn't really fly
21:14
when you're writing shared code it also
21:16
gives you confidence into code that you
21:18
know maybe we own as a company but as an
21:21
Android engineer maybe I wouldn't feel
21:23
confident messing around with JavaScript
21:24
but just knowing that it's tested
21:26
thoroughly means that I can feel more
21:28
confident in using that code that
21:30
technically we own as a company but I
21:32
don't know how to debug myself and as I
21:35
mentioned earlier state machines and
21:36
rule engines are super super good
21:38
candidates for sharing this allows you
21:41
to decouple kind of how your apps logic
21:43
works from how your app architecture
21:48
works and you can share the complicated
21:50
business logic of your company across
21:53
your apps without necessarily needing to
21:56
you know make sure using mvvm everywhere
21:58
or without using you know whatever
22:00
architecture you want to use on one
22:02
platform doesn't leak into every other
22:04
platform that you're using as a result
22:06
of sharing the code so I've spent around
22:10
20 minutes talking about things
22:12
completely unrelated to colleton
22:13
multi-platform and the title of the talk
22:15
has
22:15
in multi-platform in it so it's probably
22:18
time for me to start talking about it
22:20
hopefully most of you are familiar with
22:22
Kotlin in general in 2017 Google
22:26
announced that it was backing Kotlin
22:29
through the cotton foundation and it
22:30
became you know officially supported on
22:33
Android which is really great and at
22:35
cotton conf
22:36
that year JetBrains announced the very
22:39
like the concept of a cotton multi
22:41
platform project the idea was to take
22:43
the model of Cotton's jbm Interop that
22:46
made it so successful within the android
22:49
world and within folks the world of
22:51
folks who are writing back-end servers
22:52
and to basically replicate that in other
22:55
environments in call and multi platform
23:00
you generally have this set of code
23:04
called your common source set you
23:06
basically write code that's completely
23:08
agnostic of platforms you know you can't
23:11
use things like you know Java util dot
23:13
date in your in your common code because
23:16
your common code doesn't know about Java
23:17
at all you can you can use you know
23:20
standard Kotlin concepts like you can
23:22
make a list you can use in so you can
23:24
use lungs but the intra player is
23:27
nowhere touching your cotton common code
23:30
then through Gradle you can configure
23:32
multiple different compilation targets
23:36
for that common code where you might be
23:37
able to specify a little bit more
23:39
platform specific logic that you need to
23:42
apply there is collin JVM which lets you
23:45
spit out jars and lets you spit out a
23:46
arse for Android lets you spit out
23:48
entire Android applications if you want
23:50
to there is Kotlin j/s which lets you
23:53
spit out artefacts for not just in the
23:56
web browser but also for let's say no
23:58
js' environments which are considered
23:60
two different targets for the sake of
24:01
Kotlin j/s there's callin native which
24:04
lets you spit out basically anything
24:05
under the Sun you could spit out iOS
24:07
frameworks you could spit out binaries
24:09
for Mac Linux and Windows you could spit
24:12
out code for the Android NDK you can
24:14
write I like a watch OS app for iOS if
24:17
you really wanted to you can spit out
24:19
anything you want with Kotlin native but
24:21
the important thing here is that they're
24:23
each considered separate compilation
24:24
targets you can't use code for the JVM
24:28
in your Kotlin Jay
24:29
artifact but the idea here was that you
24:35
can write your code in Colin and
24:37
actually spit out first-class supported
24:42
framework types are artifacts for each
24:45
platform which sounded much better than
24:47
the approach that we were using for
24:48
shared code where you had to run it in
24:50
an external runtime like with JavaScript
24:52
or with something like J&I like you can
24:55
currently do today with things like go
24:56
and rust and and drop boxes genie
24:58
library for C++ and so yeah the idea
25:02
that we could have real JavaScript
25:04
artifacts real jars or AAA RS for
25:06
Android real objective-c frameworks for
25:08
iOS is super enticing and being able to
25:11
get all of that with the backing of
25:12
JetBrains and Google which have proven
25:14
track records and in a lot of this stuff
25:16
with all the type safety guarantees of
25:19
Kotlin in a language that was fun to
25:22
write in seemed like a no-brainer to at
25:24
least explore and validate and so this
25:28
is this is more or less how we
25:29
envisioned our Kotlin multi-platform
25:31
kind of framework fitting into our
25:34
shared code set up fitting into how we
25:36
think about our clients today it kind of
25:39
puts Android and iOS on equal footing
25:42
with our web client which was something
25:44
that wasn't really the case before when
25:46
the shared code was part of the web
25:48
client and basically we would write our
25:51
secret sauce in Colin common applied the
25:54
target platform and maybe do some
25:56
platform specific things that need to
25:58
happen to spit out a platform artifact
26:01
which we just consume on our client we'd
26:03
spit out a different platform artifact
26:05
for each platform and each client would
26:06
consume it as if it were a real library
26:08
for that platform and then you'd use
26:10
standard Interop so that you could then
26:12
you know the same Interop that we use
26:13
for JBM in Kotlin you'd use Collins Jas
26:16
Interop or Collins Swift or objective-c
26:18
Interop to consume in your client but
26:20
what does that actually look like you
26:22
know what does that interrupt look like
26:23
for non jvm languages so as I mentioned
26:27
in common code you have no concept of
26:30
what your platform is your completely
26:32
platform agnostic and so when it comes
26:34
time for you to do something where you
26:36
actually need to know what your platform
26:37
is you can declare your your your
26:41
functions or your class
26:42
is you can mark like a function or class
26:44
name is expected so you see that you
26:47
know special expect keyword right before
26:49
the function platform name and in the
26:53
next method get greeting we can just use
26:55
platform name as if it existed and you
26:59
can yeah you can continue to declare
27:00
whatever you want as expected in your
27:02
common code and the rest of your common
27:04
code will compile as normal assuming
27:06
that that already existed then when
27:10
you're actually targeting a platform the
27:12
calling compiler will yell at you when
27:14
you try to build without declaring an
27:16
actual implementation for every single
27:18
thing that was expected in your common
27:20
code so you can do things like just say
27:24
hey this platform name is a string
27:26
Android if you wanted to well it's a
27:28
little more fun is that depending on
27:31
your platform you actually get to use
27:33
the same platform api's that exists on
27:36
that platform so on iOS while you can't
27:38
use Java util date you could use the
27:42
entire UI device API to get things like
27:44
am I an iPad am I an iPhone am I an
27:46
Apple watch you can get the current
27:48
system version you can do anything that
27:51
you want using actual objective-c
27:53
frameworks that are built into iOS and
27:55
the same applies with the web where
27:57
Colin j/s lets you get access to all the
28:00
web specific API s so while the downside
28:03
is that you can't use things like you
28:05
know your Java classes that you might be
28:07
used to in your common code the benefit
28:10
is that you get to use all of those
28:11
things on all the other platforms when
28:13
you need to use them through this expect
28:15
an actual mechanism so what was really
28:22
the right way for us to test alcohol in
28:24
multi-platform at Quizlet and how would
28:26
we actually evaluate if this was a
28:28
successful tool for us and so the way we
28:30
decided to evaluate collin
28:32
multi-platform was kind of along those
28:33
same strengths and weakness faults that
28:35
we saw with javascript things like you
28:37
know it needed to work because debugging
28:39
is never really fun when you're working
28:42
with shared code it needed to be
28:43
performant which was a big issue on
28:45
Android and iOS India to be stable and
28:48
it needed to be something that
28:50
developers felt competent working in so
28:54
first you had to write something
28:56
to evaluate you know we had to choose an
28:58
arbitrary part of our secret sauce to to
29:00
port and evaluate and we decided to
29:02
start with our grading logic there's a
29:04
bunch of complicated regular expressions
29:06
in there but it's a decision tree
29:07
beneath the hood it's you know a lot of
29:09
control flow logic and has a pre and it
29:12
had a pretty concise interface which was
29:14
I think another also an important part
29:16
for us to kind of choose a narrowly
29:18
scoped part of our secret sauce luckily
29:22
it was relatively straightforward to
29:23
take our you know are already cleanly
29:26
isolated JavaScript code base and
29:27
reimplemented in Kotlin and in fact
29:30
there was just a lot of copying and
29:31
pasting and clever Find and Replace to
29:34
make sure that our code was valid Kotlin
29:36
once we copied and pasted it over and
29:40
we're actually pretty quickly able to
29:42
get Kotlin to spit out a javascript file
29:45
and a jar that were hypothetically both
29:48
supposed to be our grading library but
29:51
you know as most of you know compilation
29:53
just because you get something doesn't
29:54
mean it works and you know the fact that
29:57
we had a so-called Kotlin j/s grader
29:59
doesn't really mean much if it's not
30:01
working properly and thanks to our test
30:06
suite that we had already written for
30:07
our shared code earlier we were able to
30:09
basically take this JavaScript grader
30:11
which had the same interface as our
30:14
handwritten JavaScript grader and run
30:16
the same integration tests for our old
30:20
shared code with this new Kotlin JS
30:22
artifact and you know we run the
30:24
end-to-end tests and it works do you
30:27
trust that it's all perfect no you still
30:30
keep going so we decided to port our we
30:33
decided to port our unit tests that
30:35
we're testing the internals of this
30:36
grading logic over to Kotlin as well
30:40
Kotlin JetBrains provides us library
30:42
called collin tests you know it's
30:44
officially supported by JetBrains and it
30:46
create it consists of a bunch of
30:47
annotations and utilities to write
30:51
multi-platform unit tests it's a J unit
30:53
style API it's pretty standard it
30:56
doesn't give you a lot of the fancy
30:58
stuff you know like behavioral tests but
31:00
it lets you write real unit tests that
31:02
let you test things out and one of the
31:05
most important things is that it
31:06
supports a bunch of different test
31:08
runners so it's J unit style tests
31:10
sir shion's but they're multi-platform
31:12
tests so you can run it in a wide
31:14
variety of different test runners across
31:17
a wide variety of platforms
31:19
so using Kotlin tests we were able to
31:22
rewrite our old suite of not just
31:24
integration tests but also actual unit
31:26
tests for each individual component of
31:28
our grader to also make sure that that
31:31
grader was correct but we didn't really
31:34
stop there we were super happy do you
31:35
have we had end-to-end tests working we
31:36
had unit tests working but we weren't
31:38
just gonna ship this into production
31:39
because it's a super super super core
31:41
part of the Quizlet you know Quizlet
31:43
experience you didn't want people having
31:44
being marked incorrect when they should
31:46
have been being marked correct maybe
31:48
there were you know gaps in our testing
31:49
API in our testing in our test coverage
31:52
you know because there's a wide variety
31:54
of languages maybe we had a mistake in
31:56
something in particular so it's really
31:58
scary to think that you know some
31:59
underlying hijinks that might have been
32:02
been you know accounted for with comment
32:04
and that might have been accounted for
32:06
internally would hypothetically cause
32:09
issues you know when run on a different
32:10
web browser for example and so for this
32:13
reason we kind of went all in on
32:14
validating validating correctness with
32:17
our new grader we basically ran this
32:20
grader against tens of millions of
32:22
real-world user submissions we ran our
32:25
handwritten j/s you know our old shared
32:27
code we ran the column j s artifact and
32:29
I ran the jar the Kotlin JVM version all
32:32
against tens of millions of real-world
32:35
user submissions that that captured
32:38
basically the wide variety of
32:39
permutations and combinations of things
32:41
that people could be studying on Quizlet
32:43
and we had zero discrepancies you know
32:45
it was kind of scary to just have zero
32:48
things that are different because you
32:49
generally expect something to be broken
32:51
and then when you fix it you know it's
32:52
fixed but it really worked and things
32:55
that we learned along the way with just
32:57
integrating Kotlin j/s with our existing
33:01
build process going from part of the web
33:05
codebase to an external dependency was a
33:08
little tricky for some of our web
33:10
developers to get used to as mobile
33:12
developers who are consuming these
33:13
JavaScript artifacts we were already
33:15
used to this this was just our reality
33:16
that we had to pull this thing in and
33:17
trust that it worked but a lot of web
33:20
developers were familiar with you know
33:22
let's change a line
33:23
wait for our hot reload environment to
33:25
just have it updated and see if it works
33:27
and that was something that you know web
33:30
developers needed to get used to that
33:31
you had to build this test it outside of
33:34
the web browser and then assume that it
33:36
works
33:37
or you have to go back and fix it again
33:39
so clearly defining contracts in advance
33:41
and testing it thoroughly just became a
33:43
little bit more important because it
33:45
applied to everyone and not just folks
33:47
who are consuming this artifact blindly
33:49
we had to build a bunch of custom Gradle
33:51
tasks to create and deploy and PM
33:54
packages so that we could consume them
33:56
from the web and particularly annoying
33:59
for us was that Kotlin j/s is currently
34:03
still spitting out es5 compatible
34:05
javascript code which it works in the
34:08
web browser but there's a lot of web
34:10
tooling that you unlock when you're
34:13
using newer versions of JavaScript
34:14
including tools like web packs tree
34:17
shaking which is basically the
34:18
equivalent of I would say like you know
34:20
ProGuard or RA in Android where they
34:22
shake the trees and whatever it's not
34:24
connected to anything that falls out you
34:25
just delete from your app luckily Kotlin
34:29
j/s provides this tool called DCE or
34:31
dead code Eliminator and it's a it's a
34:34
great task that you run to strip out
34:37
things from your code and you know it
34:40
was it was a modest web bundle sized
34:42
increase of 30 kilobytes which you know
34:44
is it's not nothing but it was
34:47
relatively it was very acceptable for
34:49
the web team and one of the other
34:53
annoying things for the web folks is
34:54
that there is currently no automatic
34:56
generation of something like typescript
34:59
type definitions so that's you know
35:01
along the public API we have to look at
35:04
what these methods are and declare like
35:06
this is the type that you're allowed to
35:08
pass in in order for our web web client
35:12
to have type safety when interacting
35:14
with it performance numbers compared to
35:17
pure j/s are Colin J s code runs around
35:20
three times slower in browser but all of
35:23
these units are milliseconds it's still
35:26
very fast this was you know this was
35:29
also a straight port we didn't do any
35:31
sort of work to optimize Kotlin j/s
35:33
performance beyond just let's just use
35:35
dead code Eliminator to get rid of stuff
35:37
that doesn't belong in here there's
35:39
still you know plenty of room for
35:40
improvement here with how we do things
35:42
but just you know having you know in the
35:44
worst case of 1.5 millisecond increase
35:47
is completely acceptable for us in our
35:49
use cases on Android were there any
35:52
quirks no you just it's Colin you just
35:54
build the jar and pull it in it was
35:56
infinitely better than manually updating
35:59
a JavaScript file in your build it was
36:00
infinitely better than you know hoping
36:02
that the type the method definitions
36:04
didn't change from underneath you and
36:05
you don't realize until you go into
36:06
production compile time errors you know
36:10
yeah
36:11
those weren't really a thing anymore
36:12
because if it wasn't if it was an error
36:14
it was important because of the method
36:15
signature changed and on Android
36:18
performance was a lot a lot a lot faster
36:21
we use the same benchmark that we use to
36:24
choose jvd to v8 in the first place when
36:27
we compared it against a bunch of other
36:28
JavaScript runtimes and we compared it
36:31
against just regular Kotlin JVM code
36:34
that did the exact same thing and they
36:37
did it a lot faster as you could imagine
36:39
there's no beefy runtime to initialize
36:42
which saves us a second and a half
36:44
there's no need to like serialize and
36:47
deserialize data and like marshalling
36:49
time between our like plain old Java
36:51
objects or Kotlin objects and what j2 v8
36:54
needed that's not an issue the grading
36:58
logic is also a lot faster it's just all
37:00
around to win on Android and we were
37:03
able to get rid of j2 v8 which is a huge
37:05
deal
37:05
we were able to get our apk size almost
37:07
halved as a result of switching into
37:09
Kotlin multi-platform I talked a lot
37:13
about how Kotlin jeaious compares to
37:15
like our handwritten javascript code i
37:17
haven't really talked at all about
37:19
Kotlin native at Quizlet and how
37:21
multi-platform kind of fits in with that
37:23
strategy for us just yet and at the time
37:26
that Quizlet was originally
37:28
investigating multi-platform native was
37:30
very very very much in flux
37:31
the Kotlin native Gradle plugin was
37:35
completely separate from the Col and
37:36
multi-platform Gradle plugin when we
37:38
were first starting out and we basically
37:40
just skipped it the first time around
37:42
are we were intentionally building a
37:44
JavaScript artifact with Kotlin jeaious
37:46
that was binary compatible with our old
37:48
Jas artifact we figured let's try
37:50
for the web where we anticipate the most
37:52
pushback and let's just drop that
37:56
javascript artifact into the iOS app and
37:58
see if it also works and it did the
38:00
JavaScript performance was very very
38:02
very negligibly different so yeah
38:05
that that approach worked for us for
38:06
quite a while but you know all that kind
38:09
of stuff has changed we we are using
38:11
Kotlin native right now in iOS in
38:13
production as well some of the quirks
38:14
that we ran into when we were trying to
38:17
do this was that the biggest one is that
38:20
Collin native builds objective-c
38:21
frameworks it does not build Swift
38:23
frameworks so you have all of the
38:26
limitations of Objective C and Swift
38:28
Interop when you're trying to use a
38:30
common native framework this you know
38:34
it's it's not just all the limitations
38:36
of objective-c interrupts its if you can
38:37
represent a concept in Kotlin and you
38:39
can represent the exact same concept in
38:41
Swift but you can't represent it in
38:43
objective-c you you lose granularity
38:45
when you're going into objective-c and
38:47
then you have to maybe manually specify
38:49
that in Swift when you're consuming it
38:51
which you know it's still better than
38:54
JavaScript but it's not ideal the other
38:58
thing is that fat frameworks are not
39:03
automatic with iOS code you know you're
39:06
you our developers are used to being
39:08
able to run you know a framework in the
39:09
simulator on their computer which is a
39:11
different CPU architecture than on
39:13
phones and until recently you had to
39:15
support multiple CPU architectures for
39:17
phones as well and so there's this
39:19
concept of a fat framework where you
39:21
build the same framework for different
39:22
architectures and you split merge them
39:24
together it's not automatic it used to
39:26
have to be a very manual process and
39:28
they and JetBrains has at least added a
39:30
Gradle task that does it for you but you
39:32
still have to manually call it
39:34
exceptions are very different in Kotlin
39:37
then in Swift colin has no real concept
39:41
of checked exceptions and swift only has
39:44
checked errors you can annotate Kotlin
39:46
functions with his at throws annotation
39:48
but if you forget to do it you forget to
39:50
do it and your app crashes if and when
39:53
you run into an error on iOS concurrency
39:58
is not fun yet
39:59
there are some cool there's there been
40:02
some cool progress with
40:04
dreaded co-routines in native luckily
40:06
Quizlet doesn't need to use any of that
40:08
because we intentionally chose to share
40:10
business logic in the instead of things
40:12
like the data layer or you know
40:14
networking or persistence but if that is
40:17
something that you're interested in
40:18
there are some limitations that you need
40:20
to be aware of and this is rapid this is
40:22
an area where things are rapidly rapidly
40:23
changing ya and framework
40:26
interdependencies are a little bit
40:27
tricky where until recently you couldn't
40:30
have more than one Kotlin multi platform
40:32
or Kotlin native framework in an app now
40:34
you can but those there's still this
40:36
requirement that they're in independent
40:39
of one another whereas you know when you
40:42
know when we're building this code
40:43
you're building in Gradle you might have
40:44
your greater might depend on some shared
40:46
code and a different thing so right now
40:48
you have to build one mega framework and
40:50
consume it from iOS which is still fine
40:53
because the iOS client is using all of
40:54
those things but it's just a little bit
40:56
more annoying to deal with and you know
40:59
builds our Mac or Mac OS only which I
41:01
guess is expected for iOS frameworks but
41:05
yeah despite all those quirks we were
41:06
able to get our native iOS test suite
41:11
testing the grader which was originally
41:14
testing the Jas grader and is now
41:16
testing the kahlan native grader we had
41:17
a 25 times performance boost which is
41:20
pretty ridiculous and it you know it it
41:24
works relatively well for us so
41:26
connecting all those dots together
41:28
Cartland multi-platform is not without
41:30
its drawbacks hopefully you know those
41:31
those slides with all those bullet
41:33
points kind of Illustrated that but for
41:35
quizlets use case it was the right fit
41:37
for us by porting the grater and using
41:41
you know established pain points that we
41:42
had with JavaScript we were able to do a
41:44
very very detailed breakdown of is
41:46
cotton multi-platform the right fit for
41:48
Quizlet and we found that it was a much
41:51
more robust solution compared to sharing
41:52
javascript across our clients over the
41:55
past year we migrated the rest of our
41:58
shared code to cotton multi-platform and
41:60
we've involved developers from the
42:02
backend front-end web iOS and Android
42:04
we've been working with existing
42:07
libraries that were existing you know
42:08
shared frameworks that we were writing
42:09
in JavaScript and we've written brand
42:11
new stuff in cotton multi platform and
42:13
it's been proving to be very very useful
42:15
for us you know some older module
42:18
that were very scary for engineers to
42:20
work in because they were written in
42:21
JavaScript you know they've had some new
42:24
life reached into them were able to do
42:25
mastery factors and and big overhauls to
42:28
those things as a result of using cotton
42:31
and yeah and thanks to cotton
42:33
multi-platform folks from all parts of
42:35
the Quizlet engineering org are excited
42:37
to write shared code instead of feeling
42:39
a little bit hesitant because of the
42:41
shortcomings of JavaScript we've
42:43
consistently found that it's pretty
42:45
enjoyable experience for our engineers
42:47
developers who hated the idea of
42:48
JavaScript like writing and Colin and
42:50
developers who liked writing JavaScript
42:53
also liked writing in Colin so yeah is
42:56
now the time for you all to dive
42:58
headfirst into Colin multi-platform it's
42:60
definitely an exciting time to be doing
43:02
that I would not say that it's you know
43:04
the ideal situation the ideal solution
43:06
for everyone's needs
43:06
Colin multi-platform is not perfect but
43:10
the ecosystem is growing really quickly
43:11
JetBrains has been engaging the
43:13
community and consistently addressing
43:15
pain points you know if you want to do
43:17
multi-platform networking persistence
43:19
and serialization there are real solid
43:22
libraries out there and Kotlin
43:23
multi-platform to do that we just don't
43:26
use them at Quizlet so they weren't
43:27
really part of our evaluation of the
43:29
toolkit but tooling has reached a state
43:31
where I'd happily recommend using Collin
43:33
multi-platform for the right use cases
43:36
you know 50 million Quizlet users a
43:38
month end up touching code that was
43:40
written in Kotlin multi-platform in one
43:42
way or another and no company should go
43:44
all-in on a tool just because it's
43:46
really popular right now so instead of
43:49
thinking about you know how can I use
43:50
Collin multi-platform at my company
43:52
think more about you know what are the
43:54
bottlenecks in our development process
43:56
right now because shared code is not
43:58
free it's not one-size-fits-all as
44:01
experienced by a bunch of those
44:03
companies earlier you know choosing the
44:05
wrong path can end up costing you later
44:07
and shared code is only as good as the
44:10
amount of time that it saves you so if
44:11
you end up creating more problems for
44:13
yourself by sharing code you might not
44:15
actually you know be doing the right
44:17
thing for your company so at Quizlet by
44:20
keeping our eye on our like our secret
44:22
sauce and thinking very critically about
44:24
what parts of our codebase do we want to
44:26
share do we need to share we were able
44:28
to you know ship Kotlin multi-platform
44:30
great but more importantly
44:31
we were able to develop a framework for
44:33
evaluating tools like shared JavaScript
44:36
vs. cotton multi-platform within the
44:38
context of our business needs so I hope
44:41
that our journey and framework for
44:43
evaluating those tools was helpful for
44:46
you as you you know continue to think
44:48
about things like sharing code and your
44:49
projects and organizations I don't have
44:53
time for questions or anything I'm
44:54
already a little bit over but feel free
44:56
to hunt me down after this or tweet at
44:58
me if you have questions or criticisms
45:01
thank you
45:03
[Applause]
droidcon News
Tech Showcases, Developer Resources & Partners
EmployerBrandingHeader
jobs.droidcon.com
![]() Latest Android Jobs
Kotlin Weekly
![]() Your weekly dose of Kotlin
ProAndroidDev
![]() Android Tech Blogs, Case Studies and Step-by-Step Coding
Zalando
![]() Meet one of Berlin's top employers
Academy for App Success
![]() Google Play resources tailored for the global droidcon community |
Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.