Sasha Heinen

Re-Engineering the Pivotal Tracker Android App


On the Tracker team, “rewrite” has come to be a dirty word. With a legacy system that entails as many features as we offer, building from the ground up always takes 4x longer than expected. However, sometimes a codebase reaches a state where it’s the only option, and about a year ago the Tracker Android app and productivity had ground to a halt.

The issues with Tracker Android 0.9 revolved around the Model-View-View Model (MVVM) design paradigm and a contingent library that didn’t scale with the complexity required by Tracker’s rich feature set. Switching projects became painfully slow, and frequently didn’t work at all as the app chugged to reload SQL models at the same time as spinning up a new set of complex views in project tables. The application was designed around this library entirely, making feature development excessively slow as developers had to work around the extant performance issues.

We made three key design decisions based on the pain we’d felt with the previous application once we were cleared to do a rewrite.

First, we switched from MVVM to Model-View-Presenter (MVP), allowing for more code reuse and simpler architecture design. We wrote several custom interfaces with a unifying design for our externally sourced code so that it could easily be swapped out if libraries were found to have similar performance issues.

Second, we decided to use a NoSQL database instead of SQL, which made local database queries 2–6x more performant, in the case of switching projects and caching data locally. This shifted a performance bottleneck from the database layer to networking.

The Android android riding a skateboard.

“The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.”

Finally, the key to bringing all of these components together was the RxJava library, an implementation of the observer pattern. In the previous app, we’d used AsyncTask, which required much more manual memory management to prevent leaks and was generally trickier to work with because it doesn’t offer convenient chaining of streams. RxJava has the advantage of being very contained in both error and success handling and offering developers flexibility with operators such as .combineLatest and .distinctUntilChanged to compose complex event streams without requiring much code.

Now that we’ve rewritten the Android app with these new paradigms in mind, the pace of development has increased, adding new features is less error prone than before, and the application itself is much more responsive. We’ll be shipping more features soon!

This post was co-authored by Mark Sliva and Sasha Heinen.