Play Store’s “Best app of 2018” — Drops — is built on React Native

Mark Szulyovszky
Drops Engineering
Published in
6 min readDec 7, 2018

I never really thought this day will come. Two things that seemed extremely unlikely happened:

  • The app that I started hacking on in my free time 4.5 years ago became “Best of 2018” on the Play Store
  • We end up building it on Microsoft’s & Facebook’s open source infrastructure

To give a bit of background: we’ve spent the last 16 months building probably the most polished React-native app, called Drops. If you fancy learning (human, haha) language, check it out!

Also, we’re the perfect of example of small, profitable, bootstrapped, high-growth, remote product company that you probably have never heard of. — That’s fine, we had other priorities 😉.

We’re a senior development team of 4 — Anton, Daniel, Lucas and me, and we hope to shed some light on how some technical decisions can help you stay small and build for scale at the same time.

Our journey with react-native

Having been a native mobile developer before, the rewrite made a few things very obvious:

Most of the accidental complexity that we, software developers are battling with day-to-day is created by either local or hidden state, mutation or broken boundaries.

Okay, okay, I’m being captain obvious here. Let me explain:

Local, or hidden state

Every reasonably complex piece of software that uses local (or “distributed”) state will start to have “synchronisation issues” at some point. If you have a property that’s duplicated in multiple classes/modules, how do you make sure both of them are updated at the same time? One of the reasons why these issues are hard to debug and eliminate is that there’s no explicit “source of truth”, and prescribed update path. Object-oriented programming, that popularised the concept of local state, doesn’t provide a clear direction here.

You can easily draw the parallel between the local state synchronisation issue and the infamous cache invalidation problem. 😱

Hidden state can be more dangerous —usually it starts with a module/class pretending that its operation is stateless, which makes it hard the API’s consumer to debug issues, and can prevent them from fixing user-facing issues.

Mutation

Mutating an object can create a variety of side effects — in a multi-threaded, OOP environment, it can cause race conditions. In a “functional programming” environment — it can break memoization (and also many boundaries).

99.9% of the time (sample size: my career), creating a new object is much cheaper than dealing with the accidental complexity introduced by mutation. From a purely performance perspective, it’s much faster than what people (including myself) expect!

Broken boundaries

One module changes another module’s state, but without respecting its public API. Maybe the boundaries were not set correctly from the beginning, maybe someone was in a rush implementing a feature — the result is the same: something implicit & unexpected happens.

Limitations can help

Software frameworks don’t just define the code you write, they define how you think, and the boundaries you work in.

According to my experience, on mobile, every developer has their own casual, non-consistent implementation of MVC, MVVM, VIPER architectures — or sometimes a mix of these. This includes me of course. These architectures all share the same goal: to create a separation of concerns, to encourage developers to create small, reusable pieces of code.

But the loose definition, the lack of great examples — a room with the size of Carnegie Hall for interpretation — usually makes native mobile code hard to read, and to be honest, usually pretty horrific.

MVC, MVVM, VIPER simply don’t limit you enough.

In contrast, when using React + Redux, you’re pretty much forced to think in a “functional” way.

Want to show any kind of changes on the screen? You’ll have to go through the “main loop”. By forcing you to handle every bit of state explicitly, it shifts your thinking to writing declarative UI code. By having only one state container for your application, there’s no more confusion where to put any shared variables.

If I’d need to find an an analogy, I’d say: imagine a person on your team, who’s only job is to prevent people from using mutation, writing code that’ll create “local state synchronisation issues”, or not respecting the boundaries of components.

If someone is using mutation, the framework will “punish” them by not updating the screen. If they start using local state, that doesn’t go through the “official update mechanism” — they’ll be staring at a static screen! If they do manage to actually write code that violates these principles— for some miraculous reason— it also works, well, it’ll be pretty easy to spot: it’ll scream for “HAAACK”.

As a tech lead, technical co-founder, or project manager, would you prefer a codebase that’s written by 4 people, with 4 different ways of thinking about structuring code, or one that’s ruled by a single programming paradigm? — Okay, this was a leading question!

Also a side-note: as an ex-iOS developer, it’s surprising to me how bad Apple developer tools have become compared to what the JS community can offer. Re-compiling our old native Swift codebase takes more than a 5 minutes — this in itself has proven that our complete re-write was worthwhile.

Why you should be using react-native if you’re optimizing for developer productivity?

  1. You can use a single language across mobile, web & backend.

Types are either implicit or explicit — and you have a choice here: we use Typescript, a superset of Javascript, that adds (and enforces) a strongly typed layer on top of JS. You should expect the same kind of strictness as if you were writing, let’s say Java.

As a small company, the last thing we want to build is silos. Typescript helps any of us touch both the backend and the front end easily — which we do regularly.

2. You can share the vast majority of the code.

Less than 2% of our code that’s different on mobile, between iOS and Android. We’re optimising for a small, super-capable team, and implementing a feature only once makes a huge difference, both in terms of speed and in handling product complexity.

There are currently two libraries that’ll let you compile your react-native app to target the browser: react-native-web & react-native-dom. We’re using the former, and so far it has been working surprisingly well.

3. You only need to optimize once.

Optimization usually means shifting the code’s mental model away from humans, closer to the machine’s, so it can run faster — and it usually becomes less readable. If you’re optimising a React + Redux codebase, sweating on performance issues usually ends up in… adding more memoization.

Memoization does not fundamentally makes your code harder to read. It automatically deals with the infamous “cache validation” issue 😱 — and it only costs you memory.

And you’ll only need to optimise your (JS) code once — because it’ll have the same effect on both platforms.

4. You rarely have to deal with platform-specific issues

You have to factor in a lot of quirks when you develop with the native iOS / Android SDKs. Our original Swift codebase is full of hacks that UIKit forced upon us. React-native mostly abstracts this away. It’s not perfect (we’re talking about software here 😉), but it’s at least never broken on both, at least!

Verdict

React-native provides us a more appropriate abstraction level for developing mobile UI.

This is what matters the most — looking at a native codebase now is like what I imagine a Rust developer would feel when they’re suddenly presented with a large amount of C code. It feels… backwards.

On top of that, the widespread support, rich JS ecosystem and the speed we can iterate with hot reload makes this framework a clear winner.

Yes, you’ll face issues with a react-native app. Like navigation, slowly loading listviews, etc — exactly the same things we had issues with when writing native code. If this post was too optimistic for your, we’ve written about some of these problems and how we solved them here.

I believe if you start on a greenfield project, there is very little downside of starting using react-native now. And you hear this from a technical co-founder, who has a a company on the line here.

After you figure out how to overcome the initial hurdles, you’ll enjoy developer productivity that’s unprecedented in the mobile world — if that’s of your liking!

Would I have thought 3 years ago that we end up building Play Store’s “Best of 2018” with a true web development mentality, in Javascript, without writing more than a few hundred lines of native code? No way.

Bootstrapped, product companies: this can be your technical edge.

By the way, if you fancy scaling up our web presence from tens of thousands of visitors to millions, we’re hiring a senior web frontend developer!

--

--