Configurable Features using Kotlin Multiplatform

Artem Zasypalov
YAZIO Engineering
Published in
4 min readAug 30, 2023

--

At YAZIO we love working efficiently. All business logic of our app’s new features is written using Kotlin Multiplatform, and we share its code up to the ViewModels. This approach reduces engineering effort, increases stability, and conserves resources.

We are always on the lookout for the next step. Some screens and even entire features require swift reactions in changing situations to provide the highest level of experience to our users. The first candidate was Onboarding. Our creative Product Team strives for excellence in first user impressions, meaning that changes in this part of the app occur more frequently than in others.

That’s why our Development department decided to make this feature not just flexible for content changes, but fully configurable. This means that engineering effort will be reduced to zero for changes that involve only existing Screen Types. Here comes our first term. A Screen Type is a combination of layout and business logic used for the screen in a Flow. And a Flow is a sequence of screens presented to a user in the feature.

Our goal was to provide the Product team with a way to construct Flows and use them in the app without code changes. We took advantage of Kotlin Multiplatform again. First, we decided to have a single source of truth for both the app and the Flow Configurator. Configuration data structures and their validations are in a separate multiplatform module. This module is shared between our KMM client library with business logic and the Compose Desktop Flow Configurator app.

In the Configurator, we can use our Screen Types to create screen instances and connect them to construct a Flow, create branching to cover all user’s questions and concerns, and experiment with content and screen sequences. The saved configuration is then exported and used in the YAZIO mobile app.

By delegating responsibility for screen sequences and content, we didn’t neglect the safety of our solution. Each flow undergoes a series of validations to ensure there are no traps (cycle paths) or unreachable questions, and that tracking ids and content ids are correct. This way, we can be confident that the client app will not break due to a Flow Configurator user’s mistake. This allows for worry-free experimentation with the entire feature.

On the client side, the feature configurability option is designed to be scalable. This means that when we decide to make another feature configurable, we only need to replicate a Flow’s Coordinator that takes care of providing the right ViewModels with the right data for screens and define the screen types the new feature will be compatible with. Using Kotlin Multiplatform, we created a platform-agnostic solution that manages ViewModels and their states, event tracking, and navigation through the feature screens graph. Currently, we have several configurable features released.

There are other solutions to achieve the same result or even build a server-driven UI, but the advantage of our solution is a fully native UI, shared logic, modularity, and scalability. That’s why we decided to do it ourselves. This approach allowed us to have configurable features without incurring huge upfront costs. After just a month, we were able to deliver the first fully functional version of the Configurator and have one configurable feature. Subsequent features requiring configurability were added in a matter of days.

The results of this initiative are still being evaluated, but we can already say with certainty that the journey was worth it. We once wondered what if all screens from Onboarding were done in code only? Currently, there are more than 100 screens in total (there are many branches, of course, one user won’t see all of them). The lack of freedom for minor changes and creativity, and the increasing amount of engineering effort to support, are things of the past.

Screen sequences, content, types, and layouts are changing, but thanks to the implemented solution, engineers can focus on other initiatives to create a better nutrition app for everyone.

One important detail that occurred during this initiative: our iOS engineers were involved in the process of designing and implementing this solution and were able to gain hands-on experience in using Kotlin Multiplatform. Involving iOS team members is crucial for the long-term sustainability of the whole Multiplatform approach. Solving problems on both platforms and growing together not only as Android or iOS engineers but as Mobile Engineers who still deliver a 100% native UI experience to users is the unique career opportunity that we offer at YAZIO.

In this article, we provided an overview of the beginning of our journey with configurable features. This journey continues, and we can’t wait to share details about what went well, where we decided to refactor, how we experienced scaling challenges, and more technical details. Stay tuned!

--

--