hey everybody I'm Shelly 4 and today I'm
gonna talk a little bit about the
process of API and modernization for a
little brief background on me I work
full-time on the electron project on
behalf of github where I've been for the
past about two and a half years this
work has also allowed me to explore and
actively contribute to a pretty wide
range of open-source projects and
committees including node chromium and
tc39 I'm based in San Francisco drink a
lot of coffee and do a lot of crossword
puzzles so what is modernization
modernization means very little out of
context especially in the software world
it's a fun fancy buzzword but I can't
really give a whole presentation on how
you can apply its principles to your own
organization without first establishing
some shared context for what I mean
when I say it in service of that goal
let's start with the most basic
definition of the term I could find as
it applies to software so as we see here
modernization can be understood as the
conversion rewriting or porting of a
legacy system to a modern language
software library protocol or hardware
platform that's an OK start but it still
leaves us with quite a lot of unanswered
questions what does it mean for
something to even be modern there's
still a lot of wiggle room in that
definition that I just gave and even
some of its different components might
not all be understood in the same way
even amongst you the audience for
example a highly doubt that we all
understand modern language to be the
same thing for example beyond the Erb
sorry beyond that can modernization even
be universe
we understood across contexts the answer
to that question for better or worse is
that it depends and I know that's
probably not something a lot of you
really love to hear it's kind of like
running a non-deterministic software
program it doesn't really inspire a
whole lot of confidence but I'm not
really here today to give you 30 minutes
worth of maybe so let's unpack that a
little bit further modernization is
something that takes on different
meanings in different contexts and so
for you to really understand and gain
concrete lessons from its discussion I'm
going to ground it in practical examples
from something I work on every day the
electron framework modernization as
we'll soon see
can also feel a little bit like this but
there's not always an obvious path to
take and whichever one you do take can
often lay waste your best laid plans you
may now be wondering why is
modernization even something that we
actively want to do in the first place I
haven't exactly opened with the most
shiny and positive take on the thing
some principle we modernize in order to
retain and extend the value of a legacy
investment which is an investment that
continues to provide core services to an
organization there's a lot of different
reasons that you as a developer might
want to do this but for better or worse
it's something that almost all of us
will at some point need to do now some
of you might be familiar with electron
but before you really get to the good
stuff I think I'm gonna do a brief
overview just to make sure that we all
have the same base understanding of what
it is electron is the JavaScript
framework that allows you to write
desktop applications with web
technologies like JavaScript HTML
and CSS it can be used on Windows Mac
and Linux as well as a variety of other
architectures and platforms like Windows
on arm and even Raspberry Pi it works by
integrating chromium and node in
addition to a layer of C++
an objective-c which allows it to create
fully formed GUI GUIs as well as take
advantage of file system and networking
capabilities with node typically desktop
applications for an operating system are
written in the native language for each
if we take Windows Mac and Linux that
now means that you have three teams
writing three different desktop
applications to do functionally the same
thing this can be pretty costly both in
terms of monetary resources feature and
development and velocity and developer
hours with electron you will only need
to write your app once and then it will
work on all applicable platforms and we
can't really understand how
modernization affects users without
first discussing API versioning an API
is fundamentally a contract that you as
a maintainer establish with the people
consuming your API about how it's going
to behave when developers have their API
is consumed the consumers are implicitly
signing their end of a contract and you
as the maintainer are implicitly signing
your end you can think of an API a
little bit like buying a new car when
you buy that car you've probably got a
set of expectations for how that car is
going to behave and how various controls
on the car or going to affect that cars
movement
now let's say you've bought that car and
it's pretty it's shiny and you get on
the highway and you're going 80 and then
suddenly the accelerator and the brake
they switch now I don't know about you I
love a good adventure but I really like
knowing what my gas pedal is gonna do a
hundred percent of the time your users
are the same they don't want to risk
their app on something they don't feel
like they can trust so now back to one
of the questions I had on an earlier
slide how do you set these expectations
one of the more common ways to set
expectations for an API contract is
known as semantic versioning or cember
in this approach to versioning a version
is specified with three numeric
components first major then minor
finally patch as its name suggests each
one of these components confers semantic
meaning and allows a consumer to quickly
understand how updating their app to a
new version is going to affect their
apps potential behavior major version
bumps are made when you add incompatible
or braking API changes minor bumps
indicate new but backwards compatible
functionality and then finally patch
updates indicate fixes to existing
functionality but nothing new for our
purposes today I'm gonna focus on three
main buckets modernization spurned by
updates to platform modernization
related to dependencies and then finally
modernization related to programming
language specification first off
platform modernization operating systems
maintain their own unique API surfaces
which don't always adhere to the kind of
API contract you might be accustomed to
platforms can't really adhere to
something like cember when they're
versioning
so instead they often choose to make
updates to their API surface area based
on updates with their operating system
versions as you might imagine this can
create some mismatches with libraries
which often need to work across multiple
versions of a single operating system
one example of this is Apple's App Gate
framework which contains all the objects
that a developer needs in order to
implement the user interface for a Mac
OS app like Windows panels menus
scrollers text fields you get the idea
as such frameworks like electron which
hook into you and depend on this
framework need to monitor and adapt for
changes as Apple releases new versions
of its operating system the question we
then want to ask ourselves is what's the
optimal way to account for this let's
look at how electron handled a recent
snafu with just such an issue if you've
used Mac OS you'll know that there's a
tree bar at the top of the screen the
tree bar can contain individual elements
which are known to Apple as NS status
items electron exposes a module that
enables you to create and control
individual instances of these elements
through the tree module from the very
first release of Mac OS until 10.14
mojave developers could use instance
properties on these NS status items to
customize their appearance and behavior
then in Mac OS 10 point 10 they
introduced a new instance property the
button property which also allows you to
customize the appearance and behavior of
the items at the same time they
deprecated the previous instance
properties and then finally in Mac OS
10.15 katalina
these deprecated instance properties
finally ceased to work
an electron this created some pretty
nasty consequences we've been using the
previous instance properties up until
then and only actually realize this
issue had happened because the tray
issues when we are testing our catalina
literally just stopped showing up which
is you can imagine was pretty awesome so
as a result we had to quickly and
efficiently modernize our own code for
these new constraints but there is one
catch the button property no longer
allowed developers to manually manage
highlighting of NS status items
highlighting in this case is the way
that the icon appears when you click on
it previously you could set it to always
appear highlighted only appear
highlighted when clicked or never appear
highlighted so how do we handle this
change by necessity this is a breaking
change what we chose to do is to remove
this method from electron version 7
without replacement since at the time it
was in an early beta and so we wouldn't
be breaking our API contract by doing so
we then noted that the tray would
experience display issues in older
versions of electron and encouraged
users to update where they could as you
consider your own apps platform
modernization strategies there are some
things I think that electrons experience
demonstrates here we don't always make
the right choices and so we want to
share both our successes and the things
that we think we can do better we
determined these breaking changes in
practice because one of the core team
was doing some testing on Mac OS
Catalina but this change was ultimately
more reactionary than I think we wanted
it to be it's important to strategize
research around upcoming platform
changes in advance so that you don't end
up scrambling to minimize and user
changes in the same way that we did how
do we done that we could have avoided
users experiencing any braking changes
more than one supportive version of
electron next up is language
modernization language modernization is
that relating to the programming
language in which a framework or library
is consumed that's important in the case
of electron because while electron is a
JavaScript framework its underlying
architecture is actually written in a
combination of Objective C and C++ and
so modernization and electron is
modernization that allows developers to
use new additions or changes to
JavaScript as they are added to the
JavaScript frame or to the JavaScript
language through the Ekman script
specification a variety of these are
outlined here some you might be more
familiar with include promises async
await optional catch binding our
knowledge coalescing some of these
capabilities get to electron for free
when we update our dependencies like v8
but we'll save that for later and focus
on the more costly updates right now
a notable effort in the electron
codebase this past spring was known as
the promise of vacation initiative which
took most of our asynchronous api's and
transformed their usage from call back
to promise based we undertook this
initiative for a variety of the reasons
I've already outlined call backs have
their place undoubtedly but developers
can write arguably simpler and more
elegant code and open the doors to even
more functionality when they can write
promise based code so how do we do this
and then how did we communicate this to
our users we actually chose to do it
natively and I can see some of you look
a little bit confused by that aren't
promises a JavaScript thing so the
answer to that is actually yes but also
know to do this we actually used an
engine called v8 which Google wrote for
Google Chrome
it implements the same specification as
JavaScript but in C++ by writing a
wrapper over its promise functionality
we could enable our api's to cleanly
return promises at the C++ an
objective-c level without needing to
paper over them at the JavaScript level
with something from node like util dot
promise fie so basically promises but
with surprise multi-threading this looks
a little horrifying I recognize but
honestly it was pretty fun and arguably
a lot better for consumers we also
created a document to track progress of
this migration and conform to our own
cember contract by ensuring that the
changes were backwards compatible where
possible which means that they supported
both callbacks and promises
simultaneously for at least one major
version to ensure the developers had
enough lead time to make necessary
changes we also logged these backwards
compatible functions with deprecation
warnings at runtime so is this a totally
smooth process also no on a high level
we accomplished our goal with relatively
few hiccups but that's not to say we
didn't hit any snags and one of our more
fun bugs we actually found that if you
called one API in just the right way a
native promise chain would trigger an
uncatchable promise rejection that the
user would only ever see as a silent
failure because we'd actually introduced
a way to write non typesafe C++ Jeff
Goldblum was ashamed of us for that so
what have we learned from that adventure
and what can you take away from it
for starters deprecating methods well is
a non-trivial task that shouldn't be
done haphazardly we should have been
more clear upfront that all changes what
be taking place in one major version
since we occasionally still get issues
saying that a promise based function
didn't work for promises when it hadn't
yet been converted we're looking towards
our docks to help us better fill that
gap we do want to continue our
transition path pattern however feedback
from our users indicated that they felt
we provided clear context and
information on how to upgrade methods
that they were already using in
production finally we move to dependency
modernization this is modernization that
enables access to more modern
capabilities through dependencies and
not through code we wrote ourselves for
electron three key dependencies of this
nature our chromium v8 and node updating
each of these gives users access to new
features but also brings with them
potentially breaking changes and so we
need to be very mindful about how we
coordinate these upgrades upgrading node
for example gives us access to new file
system and networking capabilities like
for example or cursive RM der since node
also adheres December minor bumps in
node also necessitate minor bumps an
electron and major bumps in node
necessitate major bumps an electron by
upgrading node we ensure that we stay on
top of security updates and see
performance improvements thanks to the
great work of the benchmarking working
group within notes governance chromium
is a bit of a different story and that
they don't really adhere to the same API
contract we do and they don't support
older versions passed a fairly short
tail we're therefore always chasing the
tip of chromium source tree security and
performance are our primary forcing
functions here as the most secure
chromium is always going to be the
latest chromium and the performance of
chromium and therefore electron is also
going to be
better the more recent a version of
chromium that we're using we also do get
some upgrades and new capabilities for
free from chromium like for example the
wick lock API that allows you to control
screen sleep on different platforms
these capabilities are still a work in
progress and are constantly being scoped
out and experimented on through what's
known as the fugu initiative finally v8
I mentioned v8 a bit earlier on as
Google's JavaScript engine that
implements the equi script specification
but in C++ for electrons purposes it's
also very important that we try to use
the latest compatible version that we
can this enables us to access the latest
features of the JavaScript spec
including some that you can see on the
slide here on the left-hand portion v8
also works extremely hard to be
performin and so as we look to make
electron faster and less costly it's
also in our best interest to track v8 as
closely as we can and now to the lessons
we've managed to consistently be
updating against chromium's tip of tree
and the latest stable node but our newly
short feedback loop was not without a
few difficult lessons sometimes your
dependencies won't all play nicely in
the sandbox together and if you know
that's gonna be the case
it's best to make your version bundling
decisions as far in advance as possible
we didn't really do that well for
electron version 6 and so at one point
we ended up having to scramble a little
bit and had to use a slowly less than
stable version of node version 12
because we were releasing slowly before
went stable this was unfortunate as it
didn't really change too much from the
consumer end but it did mildly change
some of their dependency modernization
expectations you as a developer probably
want to give your users the ability to
use whatever you've defined as the
latest and the greatest and your users
want to access these spicey latest and
greatest
without communication however these two
converging desires are like a chemical
reaction that given just the wrong
measurement of a single ingredient can
accidentally explode the whole lab what
if you and your users have different
understandings of latest and greatest
that's a non-trivial possibility
considering that you need to take into
account different platforms opinions
conventions and what you yourselves are
functionally capable of delivering the
only way to set expectations and ensure
that your users are aligned with your
efforts is to communicate with them as
far in advance and with this high a
degree of detail as possible to that end
optimal communication can be understood
to fall into three big buckets ensuring
redundancy communicating early and
providing context your users aren't and
shouldn't be responsible for digging
into the deep dark depths of your
documentation to figure out what's
coming up soon that burden is on you the
maintainer upcoming changes especially
those that break or alter existing
functionality should be as discoverable
and as context rich as you can make them
if there's only a single place that your
users can go to determine upcoming
changes that's a single point of failure
they missed that and they might get
blindsided in my opinion the opposite of
a single point of failure is not
necessarily no points of failure instead
its multiple discrete points of failure
we want to ensure that your users can
miss one or even two notices and still
be made aware of changes which can only
happen if you saturate the information
in as many places as possible
and next communicating early
communication is always great on
principle but not all communication
benefits users equally telling your
consumers about a change right as it is
about to affect them
is for all intents and purposes
indistinguishable from not telling them
at all to that end you should look
towards informing your users of upcoming
changes as soon as plans are clear
enough that it's unlikely you'll need to
alter or recant them your users should
also be made aware of when a change is
scheduled to take effect on what
versions that change is going to take
effect and what their need going to need
to consider in doing work for dealing
with your changes
a team of five might and probably is
going to be incredible but they're gonna
have to consider this work in a
fundamentally different way than a team
of say 50 might and finally context when
you make changes to your API surface
there's going to be a reason that you're
doing so unless there's a security
related or similar concern requiring
discretion why not share that context
with your users it'll help them
understand your processes more and
likely foster better community relations
when providing that context there's four
main questions I think are important to
ask yourself why are you making these
changes how does this benefit your users
how might this hurt them and then how
can they prepare answering these and
provide a strong contextual base for
smoother updates as your project
continues to grow unfortunately
communication can help against some
limits you can communicate your list of
breaking changes really far in advance
but if every major version has a hundred
breaking changes it's gonna be difficult
to keep up and the cost of updating your
library might become too high for some
of your users to that end it's important
to be intentional about minimizing churn
where possible you don't want to throw
your users onto an endless hamster wheel
of Terror because as we see here
everyone suffers and you don't want that
your consumers don't want that nobody
wants that
and as vital as modernization can be
sometimes the best choice can be either
to defer or not to modernize in
considering whether this is the decision
best for you and your team there are
several things you're going to want to
take into account chiefly as I've said
over and over again through this
presentation context is key the
information you have today might guide
you towards a slightly different
decision than the information you have
six months from now or the information
you had six months ago code base is live
and breathe and your decision now
doesn't mean you can't take a different
path down the road you want to ensure
that this change will have veritable
positive impact on your project perhaps
end-users won't see a change but it'll
decrease maintenance burden across big
parts of your codebase the real danger
lies in modernization for its own sake
if you can't prove impact to some part
of your ecosystem you should ask
yourself are these developer hours gonna
be worth it if it would increase
maintenance cost or increased technical
debt that would also likely be a vote
against electron has learned a lot from
both its successes and its failures on
all these fronts these examples are
provided in light of our experiences can
hopefully help you to shed light on how
to make better choices in your own code
bases but our constraints and
considerations are going to be different
than yours and so it follows that your
decisions may also be when you return to
your code bases next week I encourage
you to view these as heuristics rather
than rules heuristics require
situational interpretation but rules
hold fast across contexts you also have
primary responsibility for the success
of you users you should want the barrier
to their success and using your library
or framework to be as low as possible
finally all modernization requires
non-trivial work if this is something
that matters to you it should never be
planned as an afterthought and finally I
want to hear from
you do you have thoughts on the future
of electron the boundary between web and
desktop or what it's like to work
full-time on open source I'll be around
for the next two days and be more than
happy to answer any of your questions
thank you very much
[Applause]