Konstantin is a Web Developer at Facebook who is lucky to work at React Native Open Source team. For the last few years his passions were infrastructure, Continuous Delivery, JavaScript and stable builds. Before joining Facebook he worked at a New Zealand startup www.booktrack.com.
23. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
24. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Thinking to give it a try...
Some time later?How about now?
25. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
26. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
There is nothing that stops
you to try it
27. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
• Rebuilding UI with React Native
• Rebuilding native code Bucking fast
• Rerunning tests continuously to prevent breaking your
code
28. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
When compiling native code is inevitable
29. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Gradle is good because
• A long time open source and has many solutions to
common problems
• Great scripting language
• Integration with Maven and familiar dependency resolution
30. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'• Reliable, scalable and distributable build caching
• Consistent dependency resolution
• Exopackage for Android: a shell app that speeds up build
time by 5X
Buck was created to deal
with an ever growing
codebase
31. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
32. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
https://buckbuild.com/article/exopackage.html
33. Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
Some people may complain 'Eew you are running JavaScript, it is slow and interpreted'
React Native: Gradle -> Buck === 226 seconds -> 64 seconds
53. Follow the best practices
• github.com/Originate/guide/blob/master/android/guide/
Continuous%20Integration.md
• github.com/circleci/EspressoSample
• github.com/facebook/react-native/blob/master/circle.yml
54. • Rebuilding UI with React Native
• Rebuilding native code Bucking fast
• Rerunning tests continuously to prevent breaking your
code
Questions?
Editor's Notes
I am Konstantin, for a few recent years I have been involved with JavaScript development and maintaining web systems continuous delivery. Last year I joined Facebook and React Native team.
Before I start let me ask who is in the audience. Are any of you IOS developers? Android developers? Anyone here enjoys JavaScript?
I'll start with a short story from back when I joined Facebook. My beard was much shorter as well as my body mass and I was a joyful fella... So in Facebook when you join there is a Bootcamp program. And you are supposed to learn the products by fixing bugs and implementing small features starting from the second day of your work there. This is how you find a team in Facebook and this is mandatory for both developers and managers. It is not rare for people to have their contributions being deployed to a billion of devices in the first few weeks of work.
One of my first tasks was to fix a bug in a quite popular Android application.
The task was to make a separation line thicker by 1 pixel. So I set up my computer, checkout the source code, compile it, runt it on a device and then change the value expecting to test how good was the change.
The code looked something like that. Just an android UI component XML declaration. So when I changed the code and hit recompile do you what happened?
I waited for 114 seconds to get it recompiled.
And I repeat, changing 1 parameter resulted in a 2 minutes wait.
And did I do it only once and then was off with it? Not at all.
It took me many iterations to get things right because of the rounded corners being used here and there
And that is what wrong about mobile development.
Nowadays development highly iterative.
And it is not only about iterating User Interface changes.
But this is true about software development in general.
Not only about building fast.
But also about delivering product continuously.
So this is the main theme of my talk.
Iterations in the development of mobile applications are broken and some of them have been tackled by Facebook and I would like to share how we are fixing them.
Let's talk about React Native.
A question that gets asked a lot when people become familiar with React Native: Is it like phonegap?
I mean a webview running on a device, executing JavaScript and building browser DOM.
Well it is not a browser thing, React Native is real native apps.
For example here is a react native app inspected with uiautomatorviewer and you can see all the native elements being used, no tricks.
However there is a JavaScript thread running in React Native and as a matter of fact it is running React.
It doesn’t make sense to talk about React Native without talking about React first
..
Could any of you raise hands if you are familiar with React for Web?
Imagine a super simple UI component, where you have a “like” image. It starts out as gray, and when you tap it, it becomes blue to say that you like something. And you can toggle it on and off.
The imperative way of writing this might look something like this. This says, if I like this, and there’s no blue like on the screen, remove the gray like and add the blue one.
And if I don’t like it, do the opposite.
The focus here is on the transitions between the states.
I have to check what’s currently on the screen, and then make API calls to manipulate it. This can be a fragile approach, especially when we add more states, making the UI more complex.
Here’s how you would write this in React, using a declarative approach. The focus is on the UI itself for a given state.
This is much simpler to understand, and it’s much easier to see from the code what the UI will look like.
So for state transition React does it automatically.
You only need to worry about how states render leaving out the whole complexity of mutations components.
React Native is a project that allows you to write real native mobile apps using React and javascript.
Here it is in action. On the left, we have React code, and it’s powering the Android app on the right.
This code is in the same style as the React code we saw before.
In the first method, you can see that we have a single piece of state: a simple boolean that says whether the toggle is on or not - and it starts out as false.
The render method is what gets called every time the state changes. It returns the UI that goes on the screen, and as before, React calculates the transition it should make.
And when the text is pressed, we call handlePress which changes the state.
To clarify, there is no DOM and no WebView. React Native renders to native platform components.
Some people may complain 'Eew you are running JavaScript, it is slow, dynamic and interpreted.
And I would say 'Hurray, we have a dynamic language that can be reinterpreted during application runtime'
Here is an example what you can achieve by having a dynamic language running your application views.
<start>
This is what is called Hot Module Reloading, it is something that is familiar to React programmers.
In most cases you code is split into components that don't own state, they are functional.
So replacing the code that describes them and calling render again does not break the application state.
And in that case you can have the application still running while you edit the code.
The updates usually take less than a second.
And this brings us to the beginning of the talk, something that brought frustration to so many developers is now gone.
We finally have the instant feedback to explorative style UI development.
Even if you got impressed a little bit you may be thinking that you might give it a try some time in the future.
But React Native is not an all or nothing choice. You still can use React Native inside existing Native apps.
In this example we have an IOS Native App.
The UI is defined in Objective C code and on the left it is rendered in a simulator.
This app is integrated with React Native.
I'll show you how React Native can work inside a true native application.
....
The grey rectangle is a React Native view and all the React Native features like live reloading are available.
My call to action is get to using it now, there is no excuse to at least give it a try and see if this type of development suits you
Moving on to the second stage
Not all the code can be done in JavaScript. And even React Native apps need to be compiled natively.
If you are writing for Android then probably you are using Gradle.
Gradle is a good choice for a project in its early stages.
You can find common recipes on stack overflow
it has a scripting language that is not Java or XML because those are not really well suited for build scripts
and it integrates quite well with Java Open source code repositories.
Any codebase grows over time and the more mature your project is the slower your builds will become.
And as the codebase grows the features that were good in early stages are not that important anymore.
You don't need to resolve your dependencies from Maven repository every time you change a UI feature.
You rebuild very often and it needs to be very fast.
Buck was designed for fast building and everything else was developed around it.
I suppose you expect me that next I will bore you with comparison tables trying to convince you that Buck is better.
(In this case John Matrix is definitely better than John Rambo)
You were right!
Of course I will, why else I would talk about Buck?
Feel free to repeat those at home I am sure you will have similar results.
There are many steps of a build that are significant.
Some things you do often, some are rare.
Or, for example, we run builds both in Buck and Gradle in React Native Continuous Integration setup.
Which is a very slow and limited VM.
The difference is noticeable.
But I understand what you think.
You probably don't really care about some other projects compiling fast, you wonder how fast your project will do it and also how much effort it is to try.
Switching build systems is usually not something you do often unless it itches really hard.
But I'll give you a few hints how to make it easier because I have walked the same path for React Native Open Source code just recently.
First there are some basics you need to understand.
Unfortunately I don't have time to cover Buck in detail.
But there is a website with examples and docs.
It is important to note that with Buck you split your code into multiple trees and you are not allowed to have cyclic dependencies between both trees.
This way Buck can guarantee fast builds.
Buck was designed for large _flat_ codebases and dependency conflict resolution is not something you need in large flat codebases. Because all dependencies are flat.
However Java projects that use open source have a problem of conflicting dependencies and Gralde resolves them quite fine.
You may do it manually with Buck or you can still utilize Gradle to resolve and download dependencies while using Buck for builds.
Here is how you do it.
First create a Gradle task that copies all compile dependencies into a folder, say libs.
Then define a python rule in Buck that will generate the rules for every .jar and .aar file in that folder.
The biggest pain while introducing BUCK to a project is actually resolving the dependencies for the first time and this trick saves you from a huge pain that may discourage you to continue.
Over time Buck will have a nicer way of dealing with this but not right now.
Another thing to notice is there is a Gradle plugin for Buck that generates Buck files for you based on Gradle config.
Let's continue our iteration journey.
We talked about making our iterations shorter.
Now let's talk about iterations being stable when delivering the product.
Building fast is very good as long as your builds succeed.
React Native contributors list is almost 700 people. If for some reason the master branch is broken we can't effectively accept code changes until it works.
In React Native Open Source we are quite successful at testing the code that gets submitted on Github.
We run hundreds of tests that make sure internal Facebook apps won't break with contributions.
Because we run the same React Native that is on GitHub.
Let me ask you, by raising hands, how many of you run Continuous Integration systems and run tests for all your code change requests? I think I know why not many people raised their hands.
Quite often setting up CI is a hassle. Unlike web systems, for example, running tests for mobile applications is even harder because often you need:
quite large SDK library that
for end to end test you need to run emulator that is slow and clumsy
and also you can't run iOS tests on a device that is not produced by Apple so you can't run an test instance on Amazon AWS for example
Besides all that setting up your own CI system that scales is a full time job.
People built businesses around it.
So for React Native Open Source we did not even consider maintaining our own build system.
So let me share some experience around running tests continuously.
What we learned is that iOS is quite stable.
There are occasional bumps on the road but everything is quite smooth.
Compared to Android.
Android ecosystem is much more diverse with multiple parties that own the tools.
So the rest of the talk will be mostly about getting Android tests straight.
This thing happens a lot when running even simple Java tests on CI systems.
It is a crash that stops test execution and shows red on CI log.
The reason is in a process that tries to allocate more memory than VM that is running the tests is allowed to.
What makes it hard is that crashes like this don't happen every time and it may take some effort to debug.
The best way to mitigate this is to limit the build tools.
Both in terms of memory they are allowed to allocate and in terms of number of threads they can run.
You almost don't get any performance gain running tests in multiple threads on a slow CI VM but you most likely waste a lot of memory per each process.
While we are picking on Gradle, limiting it to 512 megs proved to be more stable than anything higher even though the VM has 2 to 4 gigs available.
Some tools may wrap multiple steps in one long running process for convenience because they are often called in sequence.
However a long running process is more likely to accumulate in memory because of possible memory leaks or garbage collection being delayed.
We found out that tests are more reliable to split them into short living steps.
Another good thing to note is that we are running Android end to end tests with low level "adb shell" command without a wrapper. Same thing, it ended up being more reliable.
Shell is a very good language for scripting.
And you have it by default when you execute build commands on a CI server.
But many things are very hard to do in shell language.
For example on this picture is what stack overflow suggests you do when you want to iterate command line arguments to a shell script.
For some people this is natural but not for me.
People like me like doing things in Node.js.
But the problem is that Node.js is asynchronous and is not designed for scripting test commands which are very synchronous.
<animate>
And even if you try really hard you often end up making a pyramid of calls that is hard to read and easy to mess the order of calls.
And there is a lovely project called ShellJS that combines the best from the both worlds and is just perfect for Continuous Integration scripting that requires imperative language and the power of linux tools
There are quite a few good examples that you may find online.
You may want to try them when you start.
So my final thought is that development is always about iterations in many aspects.
It is vital to work to make these iterations short and stable and I hope our experience gave you some insight.
Thanks for listening and I'll be happy to answer questions.