An honest look at migrating an Android app to a modular architecture (Part 1)

This article is the first chapter of a multipart series presenting the study of modularizing an existent large-scale Android application, highlighting the challenges and tradeoffs of a certain approach above the other.

The Tale

At PagSeguro we are on the verge of migrating to a modular architecture on our 4 years old PagBank app. The app was launched on December 2016 and it was conceived to be a simple digital wallet with limited functionalities. It was quite an experimental product and a chance to increase the company’s portfolio at that time, but throughout its journey it went from a simple prototype to a product used by millions.

The first version was built by just two teams, with roughly 7 people each, and time to market was a key strategy to the company, who wanted to launch first and then iterate to achieve a product market fit. As the ambition for this project was very limited at its inception, architecture, patterns and decisions towards scalability were not a big focus.

As the app grew the company followed along, scaling the business and the number of teams developing the app. This brought a variety of challenges as coordination, standardization and quality. Soon enough the app’s architecture was compromising the scalability and productivity of several teams who need to deliver an increasing number of features.

The Ageing Hero

The app was structure on a single module with features divided by package, which gives quite good code organization, but lacked the means to enforce decoupling of flows. It was also designed to have a single activity and every flow would be implemented using a stack of fragments coordinated by this master activity. We have to remember that back then the Navigation Component (Android Jetpack) did not yet exist, so the abstraction to facilitate the navigation between fragments and its coordination through the main activity were all done by “hand”. Singletons were also used widely throughout the app to ease the use of common abstractions, but without an aim around testability and separation of concerns.

This strategy is fine for small apps, as it reduces architecture complexity, but takes its toll fast on large applications. The main problems we wanted to address were:

The Quest

Modularization came as one of the initiatives that could help us deal with those problems. Each feature could have its own independent module and be completely decoupled from the rest of the app, that address testability and team independence. Base structures could be encapsulated on separate modules as well and be injected as dependencies where required, addressing code reusability. And finally, build and CI times could benefit from caching by building and testing only modules that has been modified on that branch.

As modularization looks as the right fit for bringing the app to its new era, we are researching different approaches, trying to find one direction that will meet our requirements. First, let’s list what those requirements are:

Having those requirements established, we had an internal look into our current scenario and raised the following points with potential to affect the solution we are seeking:

On the next part we will present the first step we’ve took on our adventure of breaking down the app into various pieces and learn that what it seems to be straight forward would be more treacherous than we anticipated.

I’m Fred, an electrical engineer who fell in love with software development and started to materialize ideas into apps.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store