Modules, modules everywhere

I have joined asanarebel.com as Senior Android developer at the beginning of February. Asana Rebel is a health and fitness app with workouts for different body and health goals, but all of them are yoga inspired.

When I joined, the plan was to increase the team size and raise the code base overall quality, focusing on the release process, testability and modular architecture, keeping a fast pace and short feedback cycles.

Today I want to talk about modularization and how I managed to split the code base to improve separation of concerns and build speed.

I believe modularization is a key point in any modern Android app. I don’t necessarily mean having many Gradle modules, but having the source code nicely organized in cohesive and decoupled entities is definitely a good idea.

My strategy was mainly to create Gradle modules to increase the build speed, leveraging Gradle parallel build and caching strategies.

Attempt #1 — The bold way

I have a rough idea of what the app is doing. I’m going to split it by use case!

After a quick project overview, it seemed a reasonable idea at the time. I mean, we have the onboarding, we have the login/sign up, we have the Today screen, we have the programs, Settings, Profile screens: it looked feasible.

I started moving pieces around, keeping an eye on the build results, leveraging IntelliJ massive refactoring capabilities, and I failed miserably. No surprise though, this already happened in the past, I had the exact same experience in previous companies: no matter how much do you think what you are doing makes sense

IT.WON’T.BUILD.

Single module projects evolve in a very specific way: messy. It sounds harsh, but unfortunately it’s true. When you can access everything from everywhere, you stop thinking about how things are connected, the only thing that you see is the next dependency that you need to satisfy to complete the current task. If you are keen to zero testing #yolo, static methods, getInstance()s or poor Dagger design, well, it’s just a matter of time before everything turns into a spaghetti code hell, where everything is referencing everything else accessing things in a blocking fashion or, even worse, playing Russian roulette with race conditions.

After two days of digging my way down to the bottom of the rabbit hole, I gave up. I reverted everything and I went back to my app-android/app/src/main sadness.

Attempt #2 — The :base module way

A few weeks ago I came across an inspiring talk by Marvin Ramin (https://twitter.com/Mauin), http://uk.droidcon.com/skillscasts/10525-modularizing-android-applications, where he shows an interesting approach he’s been experimenting with for a while now: pull the monolithic module :app down in the dependency graph, renaming it to something like :base and from there walk your way up and sideways, pushing classes to new separate modules that may depend on :base instead of :app.

This is an oversimplification, of course, and I advice you to give the talk a try. I’m pretty sure you will relate with the pain that Marvin brings with himself after God knows how many trips down to the rabbit hole. I definitely could relate.

Unfortunately I couldn’t get any progress with his approach either. For some reason the :app is too entangled and I’m also in the middle of a conversion to Kotlin and Kotlin DSL, removing 12 flavors and 4 build types doesn’t help, documenting as much as I can to prepare the project for the new coming developers.

I know what you are thinking:

Come on, man! How about fewer things at the same time?! 😂

I know I know. What can I say? #yolo

Attempt #3 — Patience

I believe Marvin approach is solid and can work as a starting point, so even if I couldn’t move the :app module straight away, I started to browse the code base looking for another attack point. I’m new to the project so it was also a nice exercise to better understand the architecture, overcoming the fear of touching too many things and breaking everything.

I started looking for leaf classes: data classes, utility classes, interfaces with meaningful names. I had one simple goal: moving as many files to new modules as possible.

It sounds a bit pointless, but my goal was to break things to figure out how things were connected and coupled together.

  1. So you open a file
  2. you check the imports
  3. if they feel like reasonable you move the file to a new module that makes sense with the file
  4. run the build, run the tests, run the app, looking for crashes or regressions.

This is super tedious, and can be very frustrating, but if you are lucky, things don’t break too much and after a while you end up with a git history looking like this:

Sneak peak into the future

In real life, we are not lucky. Com’on! If we were lucky we wouldn’t have been here in the first place, right?

Things that broke during the journey

The first things that blew up were the Dagger scopes.

There were:

  • @ApplicationScope, a custom application wide scope. Not sure yet why this was preferred to Singleton
  • @UserScope, a scope designed to exist only when a user was logged in
  • @ActivityScope and friends

Scopes were OK from a design point of view, but in the practical implementation they were very messy and fragile due to a lack of proper dependency injection.

The UserScope set, for instance, initially looked like a nice candidate for a :user module. Working with the usual approach

  1. I created an Android Library module called :user and added it to :app build.gradle.
  2. Once I had the module, I started moving the tiniest and decoupled files I could find that could belong together in a :user module.

If you are like me, in a similar scenario, you would get in the zone, you would iterate over and over: move, build, test, commit, push. After a couple of hours, you sort of hate yourself, your CI hates you for the 150 builds your ran since you started, you start questioning your career, you feel like you will never see the end of this, imposter syndrome ramps up.

No worries, you are just low on sugar. Take a break, drink some water, eat a banana, troll an iOS colleague about how hard it must to be for them to support 4 devices and 2 OS versions at the same time and than go back to your task.

So far so good, then I reached a point where things started breaking for real: I found a couple of classes in the UserScope domain that required ApplicationScope classes. If you scope your app properly this shouldn’t be possible, but if you are working for a startup, accepting hacks and technical debt is part of the game and things get tricky.

To solve this issue one would have needed to rework the whole scoping, but I believe there was no time, so the solution was the infamous quick and dirty hack, a.k.a. quick to be forgotten, dirty forever:

I need the [ADD RANDOM]Manager, so I’m gonna access it statically. getInstance() FTW #yolo

This is, in my opinion, the starting point of the decline of the whole code base maintainability. After this moment, every time you hit a blocker, you will feel the pressure, you will know that you should try to make things right, but you will use that one time you did the hack as an excuse for every single future hack, because hacks take the discomfort away quicker than the proper solution.

For me, eventually this will need a major rework, but at that point it will be a technical/business decision: investing today to be consistently fast over time.

The second major thing that broke without mercy was Proguard.

No surprise here. I believe Proguard with its hard-coded package paths and carved in stone file names is not ready for dealing with this kind of infrastructure rework.

I’m sure that pro-Proguard people will yell at me that if you configure it properly everything will be fine, but I work in the real world where projects evolve quickly, people change, things get complicated exponentially because of… reasons and Proguard has never been a factor of simplification, on the contrary it always added more uncertainty and frustration.

In AsanaRebel, for instance, everything looked OK, no warning, no error, builds were successful, but at runtime the app just kept blowing up due to some missing resource file wrongly taken away by Proguard. Tracking down these things is exhausting and builds up a lot of frustration, for a still unclear final gain of some KB in your final APK.

Current status

At the moment we have a dozen of modules and counting. Some of them are pretty fat, others just contain a few files.

The :core module, for instance, is a sort of safe-harbor module, where dependencies that should be moved from :app to a module :foo can’t be moved there because they are also needed by a module :bar and this would create a weird circular dependency, so :core becomes this thirth party that helps to decouple things in the meantime that a better solution is put in place.

The going forward approach is creating a new module for every new feature. With this approach we are certain to leverage Gradle parallel build, we can also enforce a better separation of concerns and testability, and most of all, in the very fast and very experimental mindset that we have in AsanaRebel, if we want to drop a feature because it’s not performing well, we simply delete a folder.

Eventually we can guarantee that only the old and very coupled code is still in the :app module, contained and waiting for improvements.

On this note, the :app module will also contain a package with the same name of every Gradle module we have. These packages will contain Activities, Fragments, Adapters, Views related to that module and will allow the dependency injection. Only files in :app can access the main Application file and then the Dagger component and perform the injection. So far I was not able to find a solution to perform the injection from an Activity living in a module.

The more familiar I get with the code base, the easier will be for me to see how things are connected, how to rework the dependencies and how to move things to the proper modules. This is an incremental process, it’s slow, it takes patience and it can be very frustrating, but I believe it’s the right thing to do if you really care about the product you are working on.

With more developers joining the AsanaRebel Android team in the future, refactoring the old code base will be a team effort, somehow a team building exercise, instead of a single developer going John Wick mode, head-shooting every getInstance() until the end of time.

Kotlin DSL Gradle files, multiple modules and shared variables

This doesn’t want to be a real article, but more a crash course about Kotlin DSL for Gradle.

The crucial point is the possibility of migrating an existing project to the new Kotlin DSL for Gradle, in an incremental, modularized fashion.

Below we have a pretty standard root project build.gradle file:

https://gist.github.com/hamen/7fa25a3a7ad8452873051f7782c77db5

The parts that I want to highlight are those shared version numbers, i.e. rxJavaVersion, timberVersion and so on.

This is a standard build.gradle file for an Android Library Module:

https://gist.github.com/hamen/1961a93d56d4f976b5892f3149991e58

As you can see, we reference those constants.

If you want to migrate this file to the new Kotlin DSL, you need to create a build.gradle.kts file in place of your build.gradle one and make it look like this:

https://gist.github.com/hamen/b906ad200fc94fb6f3be761b22f8fbb7

Notice how we can access those constants in a different way, but still pretty straightforward.

You will also notice that the dependencies are not the same in the two files, some versions are hardcoded, other are referencing the global constants.

It’s messy on purpose: this is an working example from one of my projects and I want to show a few of different choices you can have in those files. It’s up to you.

If you want a better overview about Kotlin DSL for Gradle, I would suggest the good article by Antonio Leiva https://antonioleiva.com/kotlin-dsl-gradle/.

Thank you all for the feedbacks and the support!

Happy Kotlin ❤️

My week at Droidcon Zagreb and App Builders

Last week was quite intense for me. For the first time I had two conferences in two countries in 3 days. I’m definitely too old for this 😜


Droidcon Zagreb

Droidcon Zagreb started like this:

The moment they dropped us at the hotel, we got a bag full of gifts and candy!

There were plenty of interesting talks and I perceived that the general code quality level in the Android community is increasing. Rx is getting very popular: not considering my talk and Sasa Sekulic’s one, also Mouna Cheikhna from BlaBlaCar talked about how they use Rx in their Android app:

Florina Muntenescu showed how to use RxJava for MVVM:

If you are wondering, yes, more than 25% of the speakers were women. This shows how much Droidcon Zagreb is working in the right direction: kudos, folks!

Another topic than seems to be quite accepted in the community are MVP and MVVM: finally they are widely used, for better app design and to improve testability.

This is definitely my favorite takeaway from Droidcon Zagreb: Use the right tools! Don’t be afraid of trying and failing, experiment, challenge the old ways, have fun!

App Builders

I’ve been living in the Geneva area for a few months now and I’m always looking for developers to talk to and share experience with. I created the Google Developer Group Geneva and I started attending Swiss conferences: Voxxed Zurich a few weeks ago and the newborn App Builders this past week.

App Builders was quite different from the other conferences I’m used to. I usually attend Droidcons and Java oriented conferences. App Builders was all about apps, no matter on which platform — Android, iOS — or technology — Java, Swift, C#.

Being a cross platform conference, the atmosphere was different from a typical Droidcon: attendees and speakers came from different backgrounds with different experiences to share.

The technical level of the talks was high, as expected, but something that pleasantly surprised me were the non-technical talks: I saw very few of them at Android conferences. At some point, it was like being at a TEDx event: very inspiring!

The Android track was full of interesting talks and I met a few new GDEs… all Spanish 😀

This was the first edition of App Builders and I think it was a great conference. The final touch was the github repo for slides, videos and feedback https://github.com/swissmobidevs/appbuilders16 . I love it 😀

Takeaways

iOS developers seem a little bit happier than Android developers

They surely are very fond of their platform, but they suffer Xcode 😁 (however, they have an alternative with JetBrains’ AppCode). They don’t struggle to support all those glitchy Samsung devices, because… well… they have to deal with just a couple of phones and tablets. They don’t need to support old devices and OS versions because they simply don’t exists: 95% of the market runs the latest or the previous version of the OS and that’s it.

Most of them migrated to the new language, Swift, the moment it went out, while most of us are still complaining about how Kotlin is 2% slower at build time, but if you ask me, a modern language that allows you to create more elegant and more readable code it’s just worth the longer build time, because you build it once, but you have to read and maintain it for months!

A lot of iOS devs envy our Google Play Store rollout feature, alpha/beta channels and stats, but at the same time they managed to overcome the problem, as Spotify and others do:

I think we can learn a lot from the iOS community. We should complain less and act more, we should be braver, jump on the next new thing, try it, embrace it and push it to our companies if we like it, instead of waiting that the thing gets mainstream and everybody else is using it already. We must experiment more, instead of following others.

We should be able to influence the way manufacturers address the market, through a sensible feedback on how difficult it is to support 2+ years old versions of Android, on dozens of different models. If we stop supporting 3 years old Android versions, maybe manufacturers will start thinking about keeping their devices updated. Who knows?!

If we start using Kotlin today as our main language on Android, we will be able to have an impact on future improvements of the language itself, and on the enhancement of all the development tools we’d love to have and, maybe, getting Google involved, for the future of the Android community, for the greater good.

Happy coding 😊