By Steve Coffey, Senior Software Engineer at Sonder
I joined Sonder last May, excited to build a next-generation hospitality platform, by bringing together the best of what hotels and home shares can offer. I joined a nascent, but hungry and wildly bright team of engineers with the same goal.
At Sonder, one of our core engineering values is around driving impact. We strive to come up with hypotheses, test them, and either kill our experiments or build them into even more robust products. It’s a marriage of product and the scientific method, and it works for us.
In September of 2018, we started thinking about how we can scale our own world-class guest support team, and improve the experience of staying in a Sonder by making your stay more self-serviceable. We came to this hypothesis:
Hypothesis #1: Sonder can create a next-level experience for our guests by putting the lobby on your phone.
When I joined back in May, the guest experience looked very different. Our guests booked online and received their check-in instructions over email. If they needed something from us, they reached out through a phone call or SMS. It was clear that there was a big opportunity to take a lot of the biggest guest requests and make them serviceable through an app. For example, we realized that 20% of our guests’ requests were simply asking for an early check-in or a late check-out.
Now, just several months later, 27% of guests are requesting late check-outs through the app, taking a huge load off of our support team. The guest app is a big part of our strategy to make the Sonder experience even more self-serviceable. So how did we get here, and how are we powering this experience with a small and scrappy team of engineers and designers?
Hypothesis #2: There is no faster way to create a production-quality app in 2019 than React Native.
In the event that you’ve never heard of React Native, React Native a framework for building cross-platform mobile apps in JavaScript using the React paradigm.
Most of us in the engineering community will already be familiar with React, which changed the game in how we think about developing for the web. It took complex web apps and broke them down into components, which themselves are built of the basic building blocks of HTML, including, div
and p
tags, and all their friends.
React Native is built on this same idea, breaking down complex UIs into their basic native building blocks. However, instead of using div
and p
tags, we use View
and Text
tags. Here’s what the above example might look like in React Native.
import * as React from 'react';
import { Button, Text, View } from 'react-native';export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
devCount: 0
};
} render() {
const { devCount } = this.state;
const verb = devCount === 1 ? "is" : "are";
const pluralizedObject = devCount === 1 ? "developer" : "developers"; return (
<View>
<Text>There {verb} {devCount} react {pluralizedObject} viewing this blog
post
</Text>
<Button onPress={this.increment(1)}>
+1
</Button>
<Button onPress={this.increment(-1)}>
-1
</Button>
</View>
);
}
increment = number => () => {
const { devCount } = this.state; if (devCount + number >= 0) {
this.setState({ devCount: devCount + number });
}
};
}
The only real difference between these two examples is the type of tags we’re using as our building blocks. Unlike the browser or an electron app where these elements are rendered into a DOM, in a native context our views have to be constructed using the native building blocks provided in the Android and iOS SDKs. Text
in React Native becomes TextView
on Android, UILabel
on iOS, and so on. When you launch a React Native app, the JavaScript runtime builds and traverses your component hierarchy, and creates the corresponding native views.
Your JavaScript code runs in a separate thread, communicates with the native code through the React Native Bridge, which batches up messages from your JavaScript code, such as requests to update the state of a view, and propagates those changes to the native layer.
The bridge is implemented on both iOS and Android, and share a common interface, allowing your code to run natively on both platforms. What this allows is for developers to create truly native, cross-platform apps quickly, while re-using a lot of the same code they may already be using in their React web apps.
It takes a village to build a product
Since you’ll be writing most, if not all, of your app’s business logic in JavaScript, you’ll have access to a very mature and active community of NPM packages. Many of which provide native functionality that doesn’t exist in the standard library, such as navigation or state management.
Probably the most important benefit of React Native is the speed of development. JavaScript is one of the most popular programming languages out there today and has a very low learning curve, so most of the developers on your team are likely already are familiar with it, or could ramp up in a very short amount of time. And since your business logic is all implemented in JavaScript, you only need to compile the native code, or “shell” of your app, when the native code changes. If your native code doesn’t change very often, it can mean that incremental JavaScript build times are less than a second, allowing you to iterate on your code incredibly quickly.
What all this meant for us is that we were able to create a production-quality app, launch it to guests in select markets, and test the results in just a few short months.
Not all sunshine and roses
We’ll all remember from Econ 101 that there’s no such thing as a free lunch, so what are the costs?
For one, there are performance drawbacks. Running a JavaScript runtime on top of the native frameworks does not come for free. For example, here’s a GIF showing the CPU and memory consumption of a native iOS app scrolling through a list of 1000 items.
By comparison, the equivalent React-Native app is much more resource-hungry.
This example is somewhat contrived, but the difference is striking. You’ll notice that the native iOS app uses about 8% CPU and about 50MB of memory at its peak. Meanwhile, the React Native app’s CPU usage spikes to over 80% and plateaus at 180MB of memory usage, to achieve the same functionality. It’s important to keep in mind that both of these examples are running in a simulator on my Macbook. On a real device, these numbers represent much more significant percentages of the total available resources on the device.
Sounds bad right, but is this really an issue? The answer depends on your use case. Even though our app is in its early days, we haven’t run into any performance issues. Granted, our app is still pretty simple, if you’re creating a more complex app or a game requiring frequent UI updates, some of these performance constraints could definitely come back to haunt you.
Live by the Community, die by the Community
The other potential hurdle, depending on how you choose to view it, will be familiar to everyone who’s worked on a JavaScript project in the past, and it boils down to three words: NPM dependency hell.
To summarize, NPM is a package manager for JavaScript projects that makes adding vendored code to your project extremely easy. As a library author, it’s also very easy to add your own dependencies when you don’t want to reinvent the wheel. What this means for application developers, however, is that when you add a library to your project, you’re not just getting the code written by the author of in the library you added, whom you may trust, but you’re also getting all of its dependencies, and all of the dependencies of those libraries, and so on.
Is this a problem? Depends on your outlook. Many will remember the famous left-pad kerfuffle that allowed one author of a tiny JavaScript library to hold a large part of the community hostage.
React Native projects, due to their complex nature, take this issue to the extreme. Here’s a visualization of all the dependencies you vendor into your project, just by running yarn add react-native
.
Just by starting a React Native project, you’re saddling yourself up with 586 dependencies, with over 300 unique maintainers, using 17 different types of licenses. It’s impossible to vet each one, and if one of them makes a breaking change that affects its upsteam dependencies, development could grind to a halt. By comparison, Apple and Google have gone to great lengths to ensure that their native SDKs are robust enough to build rich and fully featured apps with no third-party dependencies.
Is all this really worth it?
In a word, yes! Even though the potential performance issues might feel hindering, and the NPM issues daunting, remember: we build products, not benchmarks. Good engineering doesn’t always mean optimal performance or using the latest technology, it means building the best product for your customers at the right time.
At Sonder, React Native helped us spin up an app on two platforms, release it to our customers, and make a big impact on their experience with us in just a few short months. Depending on the scope and complexity of your needs, React Native is likely the right solution for you too.
Come join us!
We’re building the next generation of hospitality with products like our app that directly impact the lives of our guests. It’s an incredible company and my only regret is not joining sooner. Our engineering team is growing quickly and we need talented developers like you to take our team to the next level. Come work with us.