This is the first part of a 3 part series on Kotlin migration undertaken for the Android app at Pepperfry. This migration included the move from Java to Kotlin along with the move from MVP to MVVM architecture.
In this part we will be discussing the motivations behind our migration: legacy code structure, problems faced with legacy code, why we chose Kotlin along with MVVM and our approach to the migration.
About our Android app
The first commit of our Android app was made on the 3rd of May 2016.
In the 3+ years since:
- We have deployed 80+ versions on Google Play
- Added 5,00,000+ lines of code to the project
- Constantly added, updated, fixed and also removed features from the app.
- Followed a Model-View-Presenter (MVP) architecture
- Grown from 10s to millions of happy users
During this time, the core architecture of the app remained the same. We had abstractions for Networking, Image Loading, RecyclerView adapters, etc. These abstractions were built in the best way considering what was available in 2016. Over time, these abstractions received minor updates from us, but soon became legacy and maintaining them became tough.
Following an MVP architecture helped separate concerns and move logic away from our fragments and activities. This was a step up from the previous no-architecture era. This architecture suited us well for some time, but the way it was implemented, made testing very difficult. Another issue with MVP was the concrete dependency of the Views on the Presenters and vice versa.
Majority of the crashes our users saw were NullpointerExceptions. These were majorly due to missing
if (x!=null) checks. Considering the size of the app, it was very difficult to have null checks everywhere in the code. Some of these crashes also popped up due to MVP.
Therefore, to enhance the quality of our app for our end users and to make mistakes harder to make for developers, there was a need to improve our architecture.
Enough has been said about the benefits of Kotlin. It is a beautiful language. Everyone on the team was well aware of its advantages and keen to start implementing it. Following were some of the motivations behind our decision:
- Null Safety
- Extension functions
- Complete interoperability with Java
- Harder to make mistakes
- Powerful Standard Library
- Higher-order functions
- Rapid updates
- Development tools
- Ease of implementation with MVVM
MVP performed well for us since 2016. Although, the cross-dependency & tight coupling between presenters and views led to many issues. In 2017, Google released Architecture components like ViewModel and LiveData. These components allow the Views to observe data being emitted by LiveData across lifecycles. This observable pattern removes tight coupling between the ViewModel and the Views, Views simply observe the changes while they are active.
We used RxJava and RxAndroid through Retrofit to convert the API responses to models we required. Reactive extensions made threading and parsing objects very easy. Kotlin as a language also performed well with the MVVM architecture.
With 20+ existing features and new feature requests coming in from the product team every week, the migration needed to be planned carefully.
We decided to migrate the app in phases, alongside product work.
- We identified smaller independent modules which could be migrated. These were chosen to allow the team to learn and refine the planned architecture. At a time, each member would be working on new product requirement as well as migrating an existing module to Kotlin.
- All new development was planned to be done in Kotlin
- Bug fixes on the legacy code would continue in Java
- The entire app would be migrated in the coming months
- Test cases would be written for all migrated and new Kotlin code
In the 2nd part of this series, we look at our migration process: