At FullStory we’re using Rust for core parts of our cross-platform mobile framework. We believe this choice gives us significant advantages, but we didn’t make it lightly. In this two-part blog post we'll talk about our experience. Read Part 1 right now to find out why we chose Rust!

polyglot adjective
poly·glot | \ ˈpä-lē-ˌglät \
: speaking or writing several languages : MULTILINGUAL

Like many tech companies, FullStory is a polyglot organization. In an ideal world, an organization would standardize on a single language, which would bring many benefits: it would be easier for developers to contribute to projects outside of their team, and the support load for language-specific tooling would be smaller. However, whatever your personal language preferences may be, it’s hard to deny that quite a few languages have built very effective ecosystems around particular problem spaces. Trying to mandate a single language across an entire organization becomes painful as soon as you need to solve problems where your chosen language doesn’t have a developed ecosystem. While this doesn’t mean that you should let every engineer pick their favorite language for each project, it does suggest that choosing a few languages will serve you better than just one.

A painting of a dark-skinned man in a white t-shirt shaking hands with a light-skinned man in a red t-shirt. Their hands are gripped above their shoulders. On their clasped hands is a heart containing the letters FS. On the left man's arm is the Go language logo. On the right man's arm is the TypeScript logo.
Original Epic Handshake drawing by MILOSLAVvonRANDA

At FullStory we have historically used Go for backend services and TypeScript for frontend web applications. Go has proven itself to be very effective for writing network services (of which we have many!), so it’s a natural fit. For web frontend development, JavaScript is still unquestionably the industry standard, but TypeScript has staked a claim in that space by using its type system to make large projects more manageable. By choosing both Go and TypeScript as language options we give ourselves best-of-breed tools for building the core functions of our products—and by limiting ourselves to just two primary languages we ensure that engineers across the organization can contribute to virtually any project without having to learn a new language.

When we started looking into turning our mobile framework prototype work into a shippable product we knew that the choice of implementation language would be critical to our success. Could we leverage our investment into the languages we already used, or would we need to look to something new?

The Core Problem

FullStory certainly isn’t the first organization to wrestle with this decision. The current mobile ecosystem consists of primarily iOS and Android devices. Anyone who wants to reach a majority of mobile users finds themselves having to develop two versions of their app—one for each operating system. The two platforms present significant differences: at a language level iOS apps are primarily written in Swift (historically ObjC); Android apps, on the other hand, are primarily written in Java, with Kotlin gaining traction nowadays. From there, it only gets more difficult as the platforms have different abstractions for all the things a modern mobile app wants to do—everything from creating a button widget to geolocation.

Many other companies have shared their experiences at trying to lessen the pain of cross-platform mobile development. Dropbox recently talked about how they’ve moved away from using a shared C++ codebase for a variety of reasons. Airbnb invested heavily into React Native, but has decided to change tactics and try other solutions.

Our problem is further complicated by the fact that we’re not shipping an app but a framework for others to use in their own apps. We have to be mindful of the impact our technology choices have on our customers, such as the potential for harmful interactions with their code, and user-visible performance impact in terms of CPU and memory usage.

Why Rust?

The Rust logo: the letter R inset in a gear
The Rust logo, used under CC BY 4.0

We initially built prototypes for Android and iOS using the native languages and SDKs on each platform. Our proof of concept gave us confidence that we could build this product, but we quickly realized that we were duplicating a large amount of core logic between the two codebases. It was critical to figure out a solution that would allow us to share a common core of functionality, and focus our resources on the platform-specific pieces that truly mattered.

At FullStory we are proud to have built a company culture where anyone can challenge existing decisions. Our “Prove-It” process empowers all of us to push for change, but does require coming equipped with evidence! To this end, we came up with an objective set of criteria to help us evaluate languages for their suitability:

  • Did we have existing experience with the language or would we need to train engineers or hire new engineers to develop in it?
  • How easy would it be for developers to learn this as a new language?
  • Would use of the language in one library interfere with the use of the same language (or a different language) in our customer’s applications, including other libraries they may already be using?
  • What’s the average performance of code written in this language?
  • What’s the size impact of this language on the framework we would deliver for our customers?
  • How well does this language handle thread safety?
  • How well does this language handle memory safety?
  • What degree of control over memory allocation does this language provide?
  • How easy is it to write code in this language that works with Flatbuffers? How mature and optimized is the Flatbuffer backend for this language?

Some of these criteria are obviously specific to our use case; we had already selected Google’s Flatbuffers as the data transport format from our client code to the server, so that made it important to choose a language that could work with them in a reasonable and performant way.  Other criteria were more general (arguably, every programmer should be concerned with memory safety!).

The set of languages we chose to evaluate were Swift, Rust, TypeScript, Go, C, C++, Kotlin/Native, and Java. Go and TypeScript made the list because they were already in use at FullStory, so we had considerable in-house experience with them. We included Swift, Kotlin/Native, and Java because they were the platform-native language choices on one of the mobile platforms.  Finally, C, C++, and Rust represent the set of languages that we thought could fit most of our technical criteria as systems programming languages.

After careful consideration, Rust came out ahead on the criteria we chose. While we admit that some of us were hoping that this would be the end result, it was not a foregone conclusion! We have quite a few engineers with deep knowledge of Go and TypeScript, and we used their experience to make sure that those languages didn’t get short shrift—but in the end, the consensus decision was that Rust was, in fact, the right choice.

👀

If you’d like, you can check out the specifics of our scoring and see which language comes out on top with your own priorities in this Glitch app I wrote.


Stay Tuned for Part 2

Choosing the right programming language can have a huge impact on the way software gets written. At FullStory we believe Rust helps us solve some incredibly important problems in mobile development. In Part 2 of this series we'll talk specifics about how FullStory uses Rust in our codebase. Stay tuned if you're interested to hear about how we organize our codebase, interface with native platform code, and leverage the Rust ecosystem.