Resources

Splitting the Atom

When we set out to build a new email client, we had a few starting requirements.

First, we felt that email was too important to be lost in a browser tab. For many professionals, an email client will stay open all day every day, often on a dedicated screen. A serious email client should feel native on Windows, Mac, and Linux. That means it should have real windows, its own icon, and the ability to run in the background or offline.

Second, we felt that truly groundbreaking software must embrace creativity and customization. Just like Emacs, Excel, Photoshop, or Minecraft… an email client of the future must be extensible by the end user. There’s no one-size-fits-all solution.

And third, our team wanted to use modern technologies with a large developer audience. The obvious choice here is JavaScript, which has won developers’ hearts and minds over the last few years. JS has grown up, evolved past blinking MySpace pages and fake snow, and today boasts a powerful set of developer tools, a huge number of mature open source projects, and a cross-platform engine for every device. The language is great for both beginners and experts alike, and we see it remaining at the front lines of developer innovation for years to come.

THE SEARCH FOR CROSS-PLATFORM

How do you build a native app that works on Mac, Windows, and Linux? Ten years ago C++ or Java would have been the obvious choice, but Apple has withdrawn direct support for Java and developer mindshare has been moving elsewhere—toward modern web technologies. Today, we knew our best option was the web. But how do you leverage the latest web technologies like ES6 and HTML5 without being chained to the confines of the browser?

The answer lies in Atom, a “hackable” text editor GitHub has been quietly building for the past couple years.

SPLITTING THE ATOM

At first glance, Atom is just another popular text editor. But under the surface, it’s much more. Atom is actually a web app, powered by a new desktop app framework that combines NodeJS and Chromium. This foundation allows the entire app to be written in JavaScript, with native access to the file system, full network IO, and a wide array of NodeJS modules. Unlike other projects offering a desktop environment for JavaScript, Atom’s core includes tight system integration, clear documentation, and many little details that push it through the uncanny valley.

Atom provides a solid foundation for JavaScript on the desktop, and its application code offers lots of well-designed generic components, such as a package manager and auto-updater. We’re looking forward to contributing patches upstream as our development progresses.

But this is where we change gears. Atom was designed specifically to be a text editor, and although it’s a great building block, our new email client presents an entirely different set of challenges.

THE NEED FOR SPEED

To feel like a native app, our new mail client needed to be extremely performant. Interface latency—or worse, visually apparent rendering—would expose its web foundation and be a jarring experience for our users. Although performance isn’t an issue for simple websites, we knew our app would grow into a large web application with a huge DOM tree — and modifying the DOM is slow as molasses. Early on, our team built and open sourced a basic mail client in AngularJS, and so we know the woes of a template-powered web framework…

Our solution is to use React. The “virtual DOM” concept pioneered by React’s authors is one of the best solutions to performance issues in large-scale JavaScript apps. React’s algorithm batches changes to the DOM, minimizing costly manipulation and reflows that degrade an app’s performance as it grows. This is a huge win for us and a primary reason our UI is so fast and stable.

THE FLUX THAT WON THE WEST

Much of JavaScript still seems like the Wild West — where excitement and opportunity is paired with an “anything goes” attitude. This becomes particularly dangerous in large applications that grow to thousands of lines and many developers. To avoid collapse, you need structure, modularity, and predictability.

When we set out to structure our new mail client, we wanted to use an architecture that would codify best practices of large application development and ideally even prevent poor architectural decisions from being possible. We also wanted to design for extensibility from day one. That meant components of the app should be loosely joined and expose rich interfaces for extension.

To achieve this, we’re using a variant of “Flux” — the pattern for unidirectional data-flow in user interfaces. Flux aims to solve common problems of large MVC applications, enforcing one way data flow through the app and loose coupling between views and business logic. It also mandates use of the “command” pattern: every change in the app triggers a globally declared action. Actions can be dispatched from anywhere in the app, and likewise observed from anywhere. This creates loose couplings between small components, and is therefore a naturally extensible pattern.

There are many other reasons we’re excited about this architecture. Flux keeps business logic out of React components, prevents the brittle intertwining of views and models, and gives our team a concise domain language for one-way data flow and loose coupling.

For example, it’s impossible for a button to directly modify a thread—an action that could cause the interface and local cache to fall out of sync. Instead, the button fires an Action, which triggers business logic in a Store, which modifies the local cache and causes the entire application to receive an update with the new state.

MOVE FAST AND BREAK NOTHING

If you have a large codebase, we believe the only way to quickly ship reliable software is to write tests. Period. People rely on email all day every day, so stability and reliability are hugely important to us. This means we need to write tests — ideally unit tests — for both our core business logic and also our UI components.

We’ve used Selenium for a long time, but always felt that its tests were too hard to write and too time consuming to run during the development process. With React, testing is easy and super fast, since components are rendering “DOM-less” to JavaScript objects instead of HTML. Most importantly, each React component is only dependent on its internal and parent state, which means that components can truly be tested in isolation. Unit tests for UI components!

PREPARING FOR AN ADVENTURE

We’ve brought together some extraordinary technologies—Electron, React, and Flux — and laid the foundation for something brand new: a powerful and extensible tool for your personal data. Over the next few months, we’ll be sharing more about the architecture of our new client and specific ways we’re making it extensible. Join our mailing list to stay in the loop!