Meet Android at Indigo

Seb
IndigoAg.Digital
Published in
8 min readJul 7, 2020

Welcome! In this article I would like to give you an overview of our Android Technology Stack here at Indigo. I will share with you the decisions we made and highlight the tools we are using to help us build the best app possible for our users.

Farmer Bugdroid says hello!

A Bit of Context

The Indigo Mobile team is very young. We started building apps using React Native and it was only a few months ago that we decided to go fully native. This change of direction allowed us to leverage some of the latest technologies available on Android.

As we started our transition to fully native, we had the opportunity to work with Android developers from RocketInsights. Communication and collaboration between us were the keys to define our tech stack and to ensure the technologies we were using would not only help us today but also in the long run.

The technologies we ended up choosing was also highly dictated by our desire to make our code testable and easily shareable. To fulfill these requirements we started by defining an architecture.

Architecture and Core

From an architecture point of view we decided to follow the Clean Architecture principles, a popular concept in the Android landscape for the last 2–3 years. Despite a higher level of boilerplate code, it’s been an easy way to write testable code and manage our dependencies. Both our apps and shared libraries leverage the clean architecture principles which simplify the introduction of our code to new team members.

For more information about clean architecture, the original article is a must-read! 👇

In addition to choosing the architecture, we also decided to simplify our core tech stack with Kotlin.

Kotlin for the Win!

Today, most of the projects are using Java/Kotlin + RxJava, which is a solid combination. However, it’s only been so popular because Android never had a proper way to manage concurrent calls. Adding RxJava complexity just to get a better multi-threading solution didn’t seem worth it. On top of that, building our app around RxJava would mean our code would be highly dependent on the library and would require extra maintenance to keep up with their releases and breaking changes (e.g: migration from RxJava 1 to 2).

In opposition, Kotlin became an official language. Its popularity has been growing and lightweight (but not less powerful) additions like Coroutines, Channels and now Flows make it the obvious substitution to the pair Java/Kotlin + RxJava. Sure, some classes are still marked as Experimental but Coroutines and Flows are here to stay.

Actually, while I’m writing this article, I noticed that Google recently announced that Coroutine is now the recommended way to execute concurrent calls. This confirms that we made the right choice early on.

Coroutines is our recommended solution for asynchronous programming on Android

With Coroutines/Flows/Channels being in charge of the data flows between the different layers, we can now focus on the UI and the Presentation layer.

Presentation Layer

For the UI layer, the choice was even easier. We picked MVVM by using ViewModel from JetPack. LifeCycle has always been a pain point on Android but it seems like Google finally found the right solution. Regarding our implementation, however, there is still some work we can do to make our data flow unidirectional. Code readability and robustness would benefit from a more structured way of communication between the components.

Alongside the ViewModel library, we are also using Navigation from JetPack. Navigation’s power is to get basic flows done faster, but more advanced behaviors still require some workaround or less obvious implementations. After all, it is still a young library and despite the fact that it’s using Fragments, it definitely has huge potential. 🚀

Another library that’s saving us time with our UI work is Material-Component. Being able to use material design components in our app without having to write custom code is a huge win for small teams like us. We can now safely promote Material Design to our Designers and Product Owners without spending extra time to build it — it’s a win-win-win. 😃

Data Layers

Beside the Domain and Repository layers which are simply using Kotlin Coroutines, Channels or Flow, the remaining layers leverage well-known libraries.

Database

To interact with the local database, we chose Room. No surprise here, however, a library like SQLDelight could be a valid alternative. Being able to write a traditional unit test to validate your database code is a huge plus.

Network

Depending on which API we’re interacting with, our projects use Apollo for GraphQL and/or Retrofit + Moshi for more traditional Rest APIs.

Here at Indigo, most of our Product APIs are using GraphQL. It’s used by both Mobile and Web apps and is a great tool to easily optimize/tailor the contracts based on the View’s needs. In addition, we get validation and type checks out of the box.

Shared APIs between Products don’t use GraphQL, they are simple Rest API. To communicate with them, we decided to use Retrofit + Moshi. Moshi is a great library developed by Square. It works extremely well with Kotlin as it supports both non-nullable types and default parameter values out of the box which is not the case for GSON, for example. The library size and method count are also low and the performance has been solid. Comparisons can be found online but the results vary greatly between articles. At the end of the day, the simplicity of Moshi and its full compatibility with Kotlin tipped the scales! ⚖️

Dependency Injection

All our dependency injections are managed using Koin, a pragmatic lightweight dependency injection framework. The way I see it:

Koin is to dependency injection as Coroutine is to asynchronous calls.

It’s very easy to use and doesn’t have boilerplate code — a drastic contrast to Dagger.

With a DI framework in place, our capacity to test our code increases, which I’ll talk more about below.

Testing 1,2,3

To test our app we follow the Testing Pyramid principles and therefore try to prioritize the use of Unit Tests over other types of automated tests.

http://developer.android.com/training/testing/fundamentals

We don’t neglect Instrumented, End-to-End and other types of tests but because Unit Tests are so fast and stable, we try to leverage them whenever possible.

  • Unit Test
    Written using Mockk, Robolectric and Koin.
  • Instrumented Test
    Written using Mockk, Espresso and Koin.
  • End-to-end Test
    Written using Espresso and run on Firebase Test Lab.

In addition to these tests, we also put in place Code Quality Gates via SonarCloud and use Snyk to make sure we are compliant with the open source software licenses in our projects.

All these tests and validations are part of our CI/CD and are meant to give fast feedback to our developers.

Continuous Integration/Delivery

The last topic I’d like to share with you is our CI/CD framework. Like everything else, it’s a continuous work in progress. There are many ways to improve it which is one aspect that makes being a developer so interesting.
Our DevOps team works in such a way that we, as developers, are empowered to own and build the best CI/CD environment possible.

At the time of this article, our CI/CD for Android is composed of:

  • CircleCI as CI/CD platform
    CircleCI is a great tool, easily configurable and scalable based on your project’s needs. With the addition of CircleCI Orbs, it is now very convenient to start sharing CI/CD configuration between projects. Our web teams are already heavily using this sharing mechanism and it would make sense for Mobile to follow their path once our portfolio of apps grows.
    If there was one CircleCI limitation to be aware of, in regards to Android, it’s the lack of support for the Android emulator. You can’t run an emulator when you use their Android images. It would be more efficient to run a small Suite of Instrumented unit tests directly on the image instead of having to execute them on another service…
  • Fastlane to publish our apps
    Fastlane helps publishing our release app to the Play Store. However, we are still manually promoting the build to production. We are planning on automating this step very soon! This improvement will prevent human errors and give time back to the developer.
  • AppCenter for internal distribution
    To share an app internally, we use AppCenter. Because Indigo uses Microsoft 365, accessing an app from AppCenter is free of friction and can easily be granted to anyone in the company.
  • Firebase Test Lab for our Instrumented and E2E tests
    Due to the CircleCI limitation I mentioned above and because we want to test on a wide range of devices, we are running our end-to-end tests on Firebase Test Lab.
    Since our apps already use Firebase Cloud Messaging, we already have access to the Test Lab service. CircleCI also added Google Cloud SDK to their images so executing our tests as part of a CI/CD workflow only requires a few lines of code.
  • Slack and Emails for alerts
    From a new PR being opened to a build being published, every step is reported via email and/or posted on Slack. This helps developers focus on what’s important while keeping the team in the loop.

This setup has been working very well for us, but like I mentioned earlier, there are ways to improve it. It is a continuous work in progress and something that forces you to stay up to date with the industry’s best practices.

Conclusion

I hope you enjoyed this introduction to our Android Tech Stack here at Indigo. It was fun for me to revisit my memories of the days following our decision to go fully native. We’ve built a lot in a short amount of time, but there are still things we need to figure out or optimize. As the team grows, increased experience and skills will help evolve our stack. Nothing is set in stone and when you see how Android has changed in the past couple years, it would be foolish of us to rest on our laurels.

If you are interested in hearing more about what we are doing here at Indigo and how Android can help towards our vision, please don’t hesitate to reach out. We’re always looking for Android engineers, so if this article speaks to you and you’re looking for a new opportunity with high responsibilities and growth potential, please feel free to ping me on LinkedIn: https://www.linkedin.com/in/sebrenon/

--

--