In 2019, we made the decision to completely rewrite our mobile app using Flutter. Now that all the pieces of the puzzle are falling into place, I’d like to zoom out on what has happened in the past few months, why we felt this was the right thing to do and, even more importantly, what advantages Flutter brings us compared to our previous implementation in Xamarin. Read on to discover our road to Flutter.
The issues we were facing
There is a lot of competition in the travel & expense management software segment. At Rydoo, it is our mission to be amongst the absolute best in this market segment. In our team we’ve always understood that speed of iteration is extremely important if we want to bring our users the best experience possible and keep up with the top.
Soon enough, the team and I felt we were in a situation where we couldn’t iterate fast enough. As an example, delivering a sizable feature could take two months or more to be available for our customers. This observation led us to halt for a moment, to evaluate what we were doing and to look at the issues that were keeping us from achieving a faster iteration process. We concluded that there were a number of problems:
- Legacy code was holding us back. Introducing new code usually meant breaking a lot of other features, because of built-up “technical debt” over the years. The code was brittle and too tightly coupled.
- Although we were using a hybrid SDK (Software Development Kit) like Xamarin, we were still forced to write our UI (User Interface) twice: one for Android and one for iOS. Developing component libraries based on our in-house design system was hard with traditional native APIs and wasn’t giving us the benefits it was supposed to give us.
- Building for Xamarin was taking forever, thus crippling developer productivity. Depending on the machine you were building on, build times for our solution could go up until 20 minutes per platform.
- Lots of attempts and investments had been made over the years to automate our testing of the app. However, we never got to the point where we as developers could fully rely on test automation when fixing bugs or introducing new features. Automated and Manual QA went hand in hand but the feedback loop was simply too long.
- Xamarin had a hard time keeping up with what was happening in the mobile development scene. For example on Android, jetpack libraries were missing which provide developers with amazing tools to simplify development. On the iOS side there was not a single sign that things like Swift UI was ever going to be supported.
The hybrid app development landscape
Looking at all the above-mentioned issues we knew that something needed to happen. We started looking at the possible technologies out there and how each of them could address our problems. We mainly looked at Xamarin, React Native and Flutter.
To get a general idea on people’s interests or on how many people are using a specific technology, Stack Overflow Trends or Google Trends can give you good insights. Our suspicions were proven to be correct according to these tools: Xamarin was rather on the downfall, while technologies like React-Native and Flutter seemed to have overall more active communities.
Google Trends Report, October 6th-12th 2019
Stack Overflow Chart, October 13th 2019
Popularity should of course not be the only criteria in choosing a technology. However, an active community does bring a lot of value in the form of third-party packages, open source contributions and support. In times when you find yourself stuck crushing that bug or finding how to make that specific feature work for you, you will praise yourself lucky that you can count on the community.
So we decided that Xamarin was a “no go” and started taking a closer look at Flutter and React-Native. The choice between these two technologies is in fact a choice that is very dependent on the circumstances of your team and company. Opting for React-Native, which our designers advocated for, would be the best thing to do if you have a team that is familiar with web technologies. However, as our team was rather a team of C# and Xamarin developers, we were rather looking at Flutter, and just loved the type safety that Dart could offer us.
React-native doesn’t offer this benefit and, to be fair, the tooling that comes with Flutter is just amazing. So the decision was made. We believed that every single issue we were having before would now be answered by switching to Flutter.
How Flutter solves our problems
– Developer productivity
Flutter’s Hot reload functionality allows us to experiment and test our changes almost instantly. Building for 10 minutes or more is officially a thing of the past. Saving the performed changes and seeing them appear on the emulator in a matter of milliseconds feels like magic when you experience it for the first time. (It also makes you think how incredibly spoiled web developers always have been). This is incredibly useful when trying to fix that bug or getting that UI component pixel perfect according to the design specs.
In general, providing quality has become a much easier task as developers tend to go that extra mile a lot more easily. Waiting minutes for a change to be visible just isn’t very motivating.
– Automated testing
Being able to get that rapid feedback to a developer on whether his changes impacted another part of the app is incredibly valuable. Traditionally, we were speaking of days to get feedback. Next to unit testing, we’d get extra feedback from daily UI test runs, manual regression testing, etc.
Flutter however allows us to do more than UI Tests and unit tests, it offers us widget tests which allows us to do component testing. These widget tests allow us to mock api responses while testing specific use cases created by the QA team.
These tests run on the Dart VM which doesn’t require us to target and deploy the app to an emulator or a physical device. This allows the developer to get immediate feedback on his local machine or from the CI pipeline without having to wait for manual QA testing or the daily UI testing run.
All of this means less time spent on manual testing and regression testing, while also reducing the amounts of cycles between QA and development.
Being able to write your UI once and run them on both iOS and Android is a serious time saver. The widget libraries that are already present are extremely customisable and allowed us to easily implement our own component library.
Implementing this component library has proven to be very useful and has been far more effective than what we were doing with Xamarin. Instead of saving us time it was actually costing us time.
Verifying designs has been a lot easier for our design team, with the out-of-the-box Flutter inspector. In general, we can say that the work that reaches this stage is already of a different level of quality than what we were used to with Xamarin.
How we implemented an entirely new technology in less than 10 months
When we eventually decided that Flutter was the way to go for us, we got the rare opportunity to start from scratch. There was no gradual move possible so a complete rewrite was needed.
When the first functionalities to be developed were documented and the first designs were finalised, we started by defining an architecture and a testing strategy. With respect to the architecture, the following few concepts were particularly important to us:
Ability to adapt to backend changes.
This was very necessary, as not only the mobile front end was undergoing big changes, but also our backend was in need of some serious refactoring. We knew that those plans would require us to change endpoints and data models, this led us to develop a layered architecture where we made our business logic as decoupled as possible from the data layer by implementing the repository pattern.
We previously highlighted that Flutter has some useful tools to perform automated testing. But without a testable architecture, these tools are useless. We chose Redux as a state management solution as it provides us with simple unit testable reducer methods (for more on Redux: https://pub.dev/packages/redux#docs). Our side effects were easily mockable as we would inject our repositories from the previously mentioned data layer into these methods.
The usage of a component library.
As mentioned before we have a design system here at Rydoo. We created a separate Flutter project that implements a component library based on that design system. This means no business logic, only reusable components. We did not attempt to completely decouple this from the actual app implementation. The component library grew as the app was being developed and our team was smart enough to reuse what they could and adapt what was needed.
In conclusion, all of this has led to an architecture that we feel is future-proof. We should be flexible enough for changes that are out of our control as we are able to adapt both to changes in design and backend, while the testable nature of the architecture allows us to keep existing features working, while adding new ones.
Our first experiences after a few months of coding with flutter.
As we are launching the new app, we couldn’t be happier with the results. The app looks great, it is a lot more user friendly and the performance is overshadowing our current app. While our app got bigger over the last months during development, the performance remained excellent, which I think is a huge achievement when we compare it to our previous app.
The development experience did not disappoint. Although, initial build times are still quite large, the hot reload makes up for it. The debugging tools and IDE support only got better as a multitude of updates were pushed to flutters stable channel. For us this is a testimony to the Flutter team effort and ability to keep evolving and improving Flutter. While these updates might break a few things once in a while, the fix was often not very hard to find because of the great documentation that flutter offers. On a few occasions an update broke our CI builds, however most of the time a fix is found quickly because of the large community that Flutter has to offer.
Today the team and I are happy to have taken the Flutter path towards a simpler, faster and overall more performant app. Let us know what you think about it!