The Red or the Blue Pill? A Developer's Hard Choice.
Guilherme Holz, Android Engineer in our Mobile Team, writes about how to make the right choices before starting a software project.
Legacy is one of the major aspects a developer needs to consider when choosing between implementations, patterns, libraries - all sorts of these things. A simple design decision can cause problems years later. I’ve been a programmer for about twelve years now, and can no longer count how often I’ve had to deal with somebody else’s decisions.
What happens, though, if it’s your turn to make the decisions? How do you avoid being “the guy who created the thing”, that is, the person who is usually blamed for all the poor decisions on your project?
In the beginning of 2017, the mobile team at GetYourGuide decided to empower our tour suppliers with an improved app. At that point, we only had a ticketing app (created during a hackathon), so it was clear to us that a proper supplier app would ultimately help to amaze our customers with great and seamless travel experiences.
There it was: My chance to start something from the ground up, making all the difficult decisions without becoming the most hated guy in the team. In the following, I will explain some of my decisions in detail.
The War of the Worlds: Android vs iPhone
Android or iPhone? This was an easy decision. Eventually, I reasoned, this app would be in the hands of tour operators and guides all around the world. As not all of them would have an iPhone available, Android was the natural choice. But wait, what about hybrid solutions for both platforms, e.g. React Native? Two answers: First, we don’t have enough knowledge inside the company about those solutions to justify the risks. Second, it’s already hard enough to find Android developers, why add another skill set to the mix? Companies these days are very dynamic, people come and go all the time and it is still easier to find native developers compared to hybrid technologies.
Do you speak Java?
Last year, choosing the language wouldn’t have been worth a decision process. Java was the only option for Android. This changed because of JetBrains’ work with Kotlin. Kotlin is a modern language running on the Java Virtual Machine (JVM) and, being totally interoperable with Java all the way back to version 1.6, it is very suitable for Android development.
Although looking very convenient, using Kotlin in our production environment involved some risks. Most importantly, the language was not officially supported by Google for Android development. I still wanted to take the risk: I’m a huge fan of Kotlin since it’s early versions, and judging from my previous experiences on side projects the language proved itself as a safer and more elegant choice due to being less verbose and having modern features such as null safety, high order functions and extensions. So after a brief conversation with my peers, we opted for taking the risk of using Kotlin. The fact that it’s fully interoperable with Java played a big role here. We could still use Java for anything not supported with Kotlin.
Tidy House, Tidy Mind: Packaging
In many languages, it is common to group code in what we call packages or namespaces. The code’s organisation by packages doesn’t seem like a big topic, but it’s actually very controversial. Should we organise the different parts of the code by features or by components and types? At GetYourGuide, we are very customer focused resulting in all team members being involved in every feature. Consequently, it seemed more natural to us to package by feature. Packaging the code by components sometimes makes it hard to find related modules. This choice was supported by experiences we had collected with our customer app.
Winner: Packages by feature.
MV what? Architecting for change
Choosing the software architecture might be the most important decision when creating a new project. It will define how safe, easy or maintainable it is to add, modify and even remove features and components from your application. We decided to choose between the most popular architectures in the market ensuring practical experience in the community and good documentation resources. The three architecture patterns on the table were Model-view-presenter (MVP), Model-view-viewmodel (MVVM), and Clean Architecture. They all share some great characteristics such as Separation of Concerns (SoC) and are highly testable. We ruled out clean architecture pretty early as previous experiences with it showed a lot of overhead on the most simple tasks. Adding a field to a model with this architecture became a nightmare. MVVM is an excellent candidate, especially since the introduction of the Android Databinding Library (maintained by Google) which takes care of one of the most complicated parts of the MVVM implementation. Unfortunately, this architecture is less ‘unit testable’ than MVP and it has a lot of ‘magic’ going on with data binding making the code less readable for people not familiar with the codebase. Therefore, by elimination, we decided to stay with MVP. It’s probably the most used architecture in modern Android development and it has all the advantages I previously described.
There’s a lot of other decisions involved on a project and it would take hours for you to read up on all of them, but in case you’re curious here are some of the other things we decided to use on our project: Dagger 2, RxJava, Retrofit, Repositories, Google Vision (for QR code scanning) among a few others.
Our Learnings So Far
We are continuously measuring our decisions’ success at GetYourGuide. While it’s too early to make a final conclusion about our decisions for this project, we can draw up an interim balance.
Kotlin seems to be the right choice. It’s easier to develop with it, there’s less code to write, writing it is more pleasant and Google now officially supports it. Our main concern is no longer valid.
The combination of MVP, repositories, and packaging by features made the code modular. When we had to replace the app’s entire authentication structure, it was a piece of cake. We just implemented the new structure, pointed the new entry points, and deleted the old package. Easy peasy. As a bonus, we have no deprecated classes floating around.
Of course, the app is still new and problems may arise with time. So far, I’m very happy with the result of what we built, but as mentioned previously, measuring success is a constant process where we need to be vigilant and act on problems as soon as we can.