Talking about React Native implementation in Traveloka without mentioning district will give you only fraction of the entire picture. In this article, I will share how Traveloka developed district system as a foundation for multiplatform development and how we handle the intricacies of brownfield(hybrid, main Traveloka app) and greenfield(full React Native) apps.
First thing first, District is not a framework, architecture, or whatever complicated definition that might sounds cool. Instead I love to mention district as a mere system. We don’t introduce anything new on top of React Native, but we specified several sort of standards for all of the apps inside this system. The main keyword here and the rest of this article is all about reusability.
District system provides different way to see the apps development in Traveloka. We never faced multiple apps development before. Doing this with the smallest effort as possible is a new and fun experience.
The Bigger Picture
The way we see district system is pretty much similar to how district works in Hunger Games. There is one central Capitol which is our district-core repository (libraries, shared code, storybook, etc) and several districts connected to core instead of with each other (in our case it is district apps).
Anyway, district system is not tied to React Native since it is an idea and replicable using other language and framework. But in Traveloka, all React Native related development is only happening inside district. It’d be great to see district system concept in other language too!
District Core: One Core to Lead Them All
In order to maximize code reusability, we developed core library to contains the most abstract implementation: provide once, use in any district. We are doing our best to ensure less or no business context to intrude our core. Even though our main app(brownfield) is the major contributor to our company’s sustainability, creating library that’s tuned for our main app is obviously a good idea. But we treat it and every other district as equal.
Internal library that doesn’t contain company context inside might sounds funny. But in our case, it let us reuse most of the work and reduce modification effort to remove unnecessary context. Also, by centralizing all packages in one place, it is easier to orchestrate system-wide development, and reducing effort in our release pipeline.
Starting from a library that supports only Android and iOS platform, core library is now able to support Mobile-Web platform as well. Although the addition wasn’t smooth at first, we’ve learnt many important lessons along the process.
It is worth to mention that we only support single version for packages we developed in core. We cannot afford to maintain multiple versions that tuned per district needs, because that will raise our development & maintenance cost. While this might look like we force our way to code in each district app but on the bright side, people from other district able to help faster whenever there is a problem with core library integration, because everyone is in the same page. You can also note that it is typically easy for us to determine which version of third party dependency that will work across system as long as it works in at least one district.
District Apps: Similar Construction, Different Repositories.
The creation of district app is pretty much a cloning of a template project (we called it district-template) into new project, changing the app name, author, and several metadata and boom! You have one project ready for Android, iOS, and Mobile-Web. Although we did extra configuration when we integrate React Native inside our brownfield app, the JS-side code is practically the same, by the convention and by the project structuring with greenfield app.
Having every district built in similar pattern, allow us more flexibility when allocating engineer to help each other across product, platform, even app.
The main purpose of templating is to provide a faster way to code in district system and also bootstrap most of the configuration. Most of the time, the engineer only needs to worry about creating the product in React Native, because most of the native side has been provided inside the template.
The downside of this approach is the frequent maintenance for district-template to meet latest development. But honestly, it is better than the necessity to recreate and redo all those needed config for every new district apps (read: we are too lazy to do those things again and again). Just in 2018 we are having several new district apps and it is pretty handy to keep this template around.
From maintainer perspective, importing platform code has never been easier. We can be very confident to always import our shared style object (we called that R object) from same location in every district app, the project structure is pretty similar and it reduce the effort to find out how things works per app. Guess what, the inside of R is also same in every apps!
import R from 'src/shared/res';
//we take inspiration from android R
//good thing is it only takes one char to type!...
Note: You can read more about each product and their goal inside our fourth article in this series: Boosting Agility in Product Development with React Native
Evolution of District System
First Iteration: District-Core As Submodule
We began with zero knowledge on what and how to build a platform capable for us to support multiple apps at once, soon after we released district-axes as our first greenfield app, the retrospective hit hard. Core library that is supposedly to be the solid foothold for future is significantly dragging the whole initial development down. We took one month to develop district-axes but in fact it may take fewer days than that if we didn’t overcomplicate the development.
Being unstable and incomplete also pouring salt to the wound. It took more time for developer to update something in core library and they ended up creating their own implementation. In conclusion having incomplete core library especially in pilot project, with submodule approach is terrible idea.
We will explain more about our experience with submodule in our next article in this series.
Second Iteration: District as Single NPM Module
Learning from our mistake, we took out our district-core and use NPM to deliver core update. In this iteration we began employing template project to create another standalone app called district-tera. Is this concept future-proof? Apparently not really, as also in this timeline we realized supporting multiple apps at once with different needs of component styling will not scale, so we push our designer team to adopt a single design guideline for all of our apps, whether it’s consumer facing app, internal tool app, or playground app.
Third Iteration: district-ui As a Separate Module
Our current single module core library cannot satisfy the needs of fast paced UI-related update. We came up with plan to create a separate package that contains only standard definition and atomic components that you can use to reconstruct into more complex component. We called this package district-ui.
This package contains the shared style blueprint, that i mentioned it before as R. This shared style concept is also well known as design token. In our case it is used for all district apps and sketch. Basically it is a comprehensive value definition that we can always refer, to create standard implementation. Everything is properly defined for our use case whether it margin, elevation, color, or padding. This way, we are able to enforce common usage of all standardized components in every district app.
At first, we extracted district-ui into different repository called district-storybook, together with the infrastructure to simplify UI development successfully let our Design team to move faster, without worrying about other non-UI related code.
After several weeks running like this we were quite exhausted in every ends. Whenever we fix a bug in district-ui, we need to update dependency in the district-core and later we update the app side dependency. We also unable to reuse things we developed in district-core. While this looks tidier, it’s (truly) not a good idea.
Fourth Iteration: Current Form, District as Monorepo
In order to overcome the issue for working with different repositories all the time, we tried monorepo with lerna, and we are now very satisfied with this decision, lerna allow us to simplify yet retain the multi module development with inter-dependency between them.
This approach allow us to make collaboration easier, people can easily contribute new package. For us, task to update dependencies that was plaguing our daily life is still happening though, but not as horrible as before.
Splitting our core into smaller and specific package will allow every district-app to choose any library they need rather than importing everything that is only partially used. Also the config and environment to test each library can be individually set per package.
Scale of District System
Currently we are having 2 greenfield apps and 1 brownfield app in our app store. We are currently developing more greenfield apps, as well as several product addition to our brownfield app. Furthermore, every district app is built to support mobile-web development from the beginning. But until now, only our main app already reap this capability though.
Despite the growing number of new district apps, we are still able to manage the whole system with only five people in the core team. But we also got some help from our core design team to handle company-wide UI related development (yeah, they can code React Native!).
What Happen After District Implemented
- Better Flexibility: As the apps share the similar code, moving developer from one app to another is the matter of mere context transfer, removing big chunk of effort needed to understand how things run in different apps.
- Easy Prototyping: Not only we can deliver new update faster to all our apps, we also enable people to test new concept and things within district. Iteration is faster and less costly with multiple platform sharing the same code.
- More Collaborations: To support the React Native usage in Traveloka, we are trying really hard to find one use case after another. Then we realized, there is so much we can do with React Native. We collaborate with Content Engineering to empower our content creation process. We also collaborate with Design team to test and implement Traveloka’s Design System to all district apps, as well as doing some experimentation in our little design-to-code project.
Our Next Checkpoint
For now, it is already proven in our use case that district system performs well in closed environment e.g inside our company. We are currently pouring more effort into better on-boarding process. If previously we onboard each new engineer by assigning some of core team member as their mentor since earliest possible, we are moving forward to make this process more scalable.
Compared to last year when it was pretty hard for us to have React Native product, we are currently overwhelmed with the excitement and high demand in React Native product development.
Our capability to prepare new district project outpaced our capability to onboard new React Native engineer until they able to perform well.
So we are planning to put more effort in documenting our work, creating more straightforward tutorial, and learning material for new engineer. We are pretty excited to explore and create our own codelabs documentation. More use case means more solution needed, so contribution guideline is also in our priority list to let as many as possible people to contribute in core library development.
Hopefully we can reach more people who wants to learn React Native rather than limiting people to on board by number of core team member. We can end up with core team member to act as a guide for multiple people at once.
This is the Part 2 in a series of articles about React Native Adoption at Traveloka.