(The original post was published on February 01, 2024)
Hello! We are Jong Sic Kim, Sanghyuk Nam, and Yeonho Park, app developers at LINE Plus ABC Studio. Our team is currently working on the Demae-can (出前館) product, a delivery service operating in Japan. Demae-can, one of Japan's largest food delivery services, started in 2000 and later formed a capital and business alliance with LY Corporation (operating as LINE at the time) in 2020. ABC Studio has been involved in product improvements since the spring of 2021.
In previous articles, Rebuilding one of Japan's largest delivery apps from the ground up - The Recode Project and Why we rewrote an app with Flutter - The second attempt at recoding one of Japan's largest delivery apps, we shared our experiences transitioning from React Native (RN) to Kotlin Multiplatform Mobile (KMM), and then from KMM to Flutter. In this post, we want to share our experience of converting the Demae-can ConsumerApp from RN to Flutter.
ConsumerApp (consumer app) | MerchantApp (merchant app) | RetailApp (retail app) | DriverApp (driver app) | ||
---|---|---|---|---|---|
Framework transition (Recode) history | React Native → Flutter (Released on 2023. 12. 5) | Xamarin → Native → Flutter (Released on 2022. 11. 8) | Native → Flutter (Released on 2023. 2. 27) | React Native → KMM → Flutter (Released on 2022. 6. 28) | |
Key tech stack | State management | flutter_bloc (BLoC) | flutter_bloc (Cubit) | flutter_redux | ChangeNotifier-based (Custom Implementation) |
Navigation & routing | Router & Navigator | Single Page (Custom Implementation) | Router & Navigator | Single Page (Custom Implementation) | |
Modularization | △ | X | ○ | ○ |
For reference, we refer to the consumer app, where users can order food or retail products, as "ConsumerApp". We'll continue to use this term throughout the article.
Differences from previous Recode projects and setting project direction
At ABC Studio, we set a goal to provide all apps with a unified technology to offer a better delivery service. We initiated Recode projects for many apps and successfully transitioned them to Flutter.
However, the ConsumerApp was a bit different from previous Recode projects. It has more users and higher traffic than other domain apps within Demae-can, requiring agile operational responses. It's also an app where various business departments focus their requirements. As a result, a development team has been established in Japan for a long time. The product specs were fragmented across numerous projects attempted during the service's rapid growth, and in many cases, the history was hard to trace.
Since around 2022, feedback and satisfaction analysis from users indicated a need for UI/UX improvements within Demae-can. The service aims to provide a nearly identical experience on both the app and web, with about 80% of orders occurring through the app. The decision was made to focus more on the app in the future. Therefore, it became crucial to revamp the ConsumerApp's technology to enhance the app service experience while achieving business goals.
Is changing the tech stack necessary?
While we confirmed the need for a tech overhaul, we had to reconsider whether changing the tech stack was the best choice for the future.
ConsumerApp was implemented with RN, while all other domain apps had transitioned to Flutter. Having a split tech stack makes it challenging to exchange technology across domain areas. It also means maintaining and developing with two different technologies, which isn't ideal for team operations, especially when considering long-term understanding and recruitment.
Moreover, Flutter was gaining more attention than RN, as shown below.

If we were to transition ConsumerApp to Flutter, developers could easily participate in product development across Merchant, Retail, and Delivery areas, and vice versa. This also simplified recruitment, as there was no need to distinguish between two technologies. However, we were concerned about the potential departure of engineers who preferred RN, so it was crucial to gain consensus among all project members and align on the same goals.
Setting the Recode project direction considering ConsumerApp's characteristics
As mentioned earlier, ConsumerApp has more requirements than other apps. The business needs to keep growing, so requirements from business, marketing, and CS departments continued to pour in, regardless of the Recode. Additionally, the product organization constantly identified improvements to address security and technical issues.
To proceed with the Recode project in this context, we discussed with various departments to postpone tasks that could be delayed until after Recode was completed, while prioritizing those that couldn't be postponed. For example, tasks like implementing a new coupon system or location correction during orders had a significant impact on sales or customer claims, so they couldn't be delayed.
Considering these factors, we anticipated challenges but decided to proceed with the Recode project by deploying tasks with set schedules on the existing product (RN version) while simultaneously developing the new product (Flutter version) in parallel.
Going back to square one for the last time 😆
Proof of concept (PoC)
The development of the Flutter version began with the Korean development team, who were more familiar with Flutter. Meanwhile, the Japanese development team focused on operating and improving the existing RN version while learning Flutter (the entire Japanese team started learning with "Flutter Day 1" sessions).
All the basic features provided in the RN version had to be implemented in the Flutter version. Therefore, the most crucial aspect of the PoC was verifying whether all features used in the search, navigation, and order-to-delivery flow could be transitioned to Flutter. Below are some of the key considerations and confirmations from the PoC stage.
Requirements | Detailed review items | Review results |
---|---|---|
Minimum feature requirements | Is third-party login possible? | flutter_line_sdk, sign_in_with_apple can be used |
Can RestAPI and GraphQL be used for server communication? | dio, ferry can be used | |
Can location information and map libraries be used? | geocoding, platform_maps_flutter can be used | |
... | ||
Order experience | Can all previously used payment methods be used? | Apple Pay can be implemented with pay In-app browser payments can be handled with flutter_inappwebview |
Is push notification display possible? | firebase-messaging can be used | |
... |
We reviewed whether all nine payment methods provided by the Demae-can app could be used as they are and confirmed if 3D Secure compliance was possible. We examined whether the existing interfaces for server communication using RestAPI and GraphQL could be implemented in the Flutter version and whether various features like third-party integration provided by RN could be implemented in the Flutter version. Additionally, we considered whether we could modularize and share the design system to provide a consistent UI/UX experience across all app services offered by Demae-can in the long term.
For more on Flutter modularization, check out ABC Studio's article on Refactoring Common Modules with Flutter Packages (article in Korean).
We also examined non-functional elements from various angles. For example, in the existing RN version, we used CodePush to quickly patch situations where orders couldn't be placed. We conducted about 3-4 patches per month. In the Flutter version, where CodePush isn't available, we needed to explore alternatives. For instance, relying on frequent, irregular deployments with CodePush could lead to unstable QA processes, so we considered transitioning to a regular deployment process to ensure more stable QA times.
During the PoC, we also identified areas for improvement compared to the RN version. For example, in the RN version, we used Hooks for state management. In the Flutter version, using BloC allowed for better code analysis and maintenance. While there was a downside of needing more Boilerplate code for UI and BloC interaction, we found that using build_runner or IDE assistance could sufficiently address this. There were also positive evaluations regarding performance improvements in interactions and animations. Personally, I was impressed by the continuous improvements in Flutter SDK and rendering performance, as well as the release of a game toolkit during the Recode period (Flutter > Casual Games Toolkit).
Project kickoff meeting
In May 2023, the Japanese and Korean development teams for ConsumerApp met offline in Kyoto for a two-day workshop to kick off the Recode project.
Establishing work methods and ground rules
During the kickoff, we first discussed how the two teams would collaborate and develop. With 14 members (4 from Korea, 10 from Japan), it was challenging to run Scrum while handling both RN and Flutter versions, especially with language barriers. The RN version was developed on a task basis, while the Recode process was conducted on a release version basis, so we needed to discuss the transition method. To overcome these obstacles and collaborate as one team, we shared opinions on communication, development methods, code reviews, and more, and established the following work methods:
- In Flutter, we enable as many lint options as possible and aim for Effective Dart: Style coding conventions.
- Code reviews related to Flutter are primarily handled by Korean developers, while Japanese developers also review business logic or areas requiring background knowledge.
- Communication is primarily in English, and for detailed discussions, we use translation bots on Slack to utilize Japanese, Korean, and English.
- We conduct Scrum meetings three times a week and a regular meeting once a week. Scrum meetings are 15 minutes in the morning, and regular meetings are one hour with an interpreter.
Establishing resource plans
For the 10 members working on the RN version, we planned for each person to join the Flutter version as their respective tasks were completed, as shown below.
Sharing progress and establishing task execution and verification methods
The Recode project's requirements involve fully implementing the features provided by the existing app in the Flutter version. To achieve this, it's crucial to maintain stability while completing development as quickly as possible and coordinating QA schedules effectively. The main points discussed were as follows:
- Sharing detailed progress of the Recode before the kickoff
- As Japanese and Korean developers proceeded with the Recode for each feature, they shared histories or development stages that were difficult to understand or verify, test APIs available for use, and potential impact areas as tasks progressed in parallel. They aligned their understanding by confirming each issue more specifically. Notably, the logging area, which the Korean team hadn't paid much attention to, required more work than expected and wasn't well-organized in the RN version. We decided to recreate and implement the logging specs, with the Japanese team taking responsibility.
- Confirming and aligning on upcoming tasks
- We first identified the top nine priority tasks, confirming the person in charge, current progress, and expected release schedule for each task. To respond quickly to various issues arising during operation, we reviewed the Japanese team's response methods and decided on who would handle them in the future. The Korean team planned to update the Flutter version whenever the RN version was released, and Japanese developers who could join Flutter version development after completing major tasks in the RN version would learn Flutter for their previously assigned tasks and proceed with the Recode process. This allowed those who knew the specs well to work on the same areas in Flutter.
- Discussing Recode project verification methods
- The Japanese development team, who had a better understanding of the detailed specs for each feature, took charge of the necessary verification during the development stage. We decided to conduct QA in three major phases, with the Japanese development team handling communication with the QA team regarding verification scope, schedule adjustments, and discussion of the process. We also considered conducting internal CBT with QA (QA is conducted locally in Japan).
By conducting the kickoff offline, we were able to understand how the existing RN version was developed and operated. It was the most helpful activity for proceeding with and completing the project as one team. Additionally, the Korean team proactively reviewed the Recode code offline, and through activities like team dinners, we were able to build trust and respect for each other.
Starting development
Collaborative efforts from ABC Studio app developers
Now, the remaining task was to complete development as quickly as possible. After the kickoff meeting, we created Jira tickets for each feature and assigned responsibilities. In Korea's ABC Studio team, not only ConsumerApp developers but also MerchantApp, RetailApp, and DriverApp developers participated. As shown below, developers from other domains joined in to complete development for each feature as quickly as possible. For example, a DriverApp developer took charge of four areas, including ItemTop, and MerchantApp and RetailApp developers participated in the Login/Register area.
Additionally, the task of building a new CI/CD environment was handled by RetailApp developers. ConsumerApp had been using Bitrise for its CI/CD environment, but considering cost advantages, we transitioned to Teamcity, which was already well-prepared for use in other Flutter projects. This allowed us to create and utilize common scripts for automation, and we continue to improve them.
Issues encountered during development
Of course, not everything went smoothly as planned.
First, after the project kickoff, there were parts that we hadn't identified as tasks and added to the task list. For example, there were overlooked areas in the flow where users change or register addresses in ConsumerApp. The image below shows six paths where users can change or register addresses in ConsumerApp. While the "Delivery Address Information Bar" and "Delivery Address Registration Guide UI" on the left were identified as tasks from the start, the "Search by Address", "Search by Postal Code", and "Search on the Map" on the right were not identified and were missed.
This feature is executed through various paths, such as ordering as a guest when the app first launches, registering an address during sign-up, and checking the delivery address list and address list in My Page. Additionally, there are subtle differences, such as only supporting postal code search in the sign-up path (red line in the image above).
However, due to the failure to identify the three tasks on the right, someone temporarily implemented them while developing each feature, resulting in the features being linked. This issue was discovered later, and we had to quickly assign responsibilities and verify detailed specs to proceed separately, making it challenging to manage the project schedule.
Additionally, as we proceeded with major feature units, we discovered hidden small specs that needed to be implemented. This made it difficult to predict the overall development schedule. There were frequent cases of discovering these issues late, causing anxiety about not meeting the development schedule. We promptly registered these additional tasks as Jira tickets and assigned responsibilities. Thanks to the collaborative and respectful efforts of everyone involved in the project, we managed to avoid significant gaps and ultimately met the schedule. However, we learned the importance of thoroughly understanding the specs in advance when proceeding with a project.
The RN version released 34 times during the Recode, and the Flutter version kept up
This project involved operating and improving the existing app while simultaneously creating the Flutter app. We anticipated some RN version releases from the start, but there were more releases than expected.
At the start of the Recode project, the RN version was at 22.2.0 and went up to 28.2.1. There were a total of 34 releases, including major tasks and unexpected patches related to issues during operation. This led to ambiguity in determining Recode completion for each feature unit, and when conducting code reviews for completed Recode features, the RN version viewed by the author and reviewer differed, increasing communication costs.
In such a Recode process, the most crucial aspect is repeating development and verification to ensure the specs of both apps are at the same level. The Japanese team, who had a detailed understanding of the specs, took charge of regression testing, allowing the Korean members conducting the Recode to identify areas they couldn't grasp at the code level. Especially since the Recode project and existing app feature improvements and releases were conducted in parallel, we could understand the specs and background in detail when reflecting new features implemented in the RN version back into the Flutter version.
Updating the minimum OS support version during this opportunity
With the transition to the Flutter version, we boldly raised the minimum OS support version. Looking at the OS status of ConsumerApp users, the share of iOS 11 to iOS 13 was 0.32%, and the share of Android 5 and Android 6 was about 0.1%, which was very low. Considering this, we decided to update the minimum version during this opportunity and planned the release schedule to allow enough time to inform existing RN version users of these changes, such as guiding lower version users to use the web.
As a result, the existing RN version was available on iOS 11 and above and Android 5.0 and above. From the Flutter version, it became available on iOS 14 and above and Android 7.0 and above. For reference, the Flutter SDK version used at the time of release completion was 3.10.7.
Finally released, did users notice?
As mentioned earlier, we divided QA into three phases, considering the development progress and verification priorities, and conducted internal CBT once it was somewhat stabilized. We then monitored crash occurrences and user reactions using the staged rollout feature of Google Play Store and the phased release feature of Apple App Store. Although a few errors were discovered during the staged release process, we responded quickly to resolve them, and fortunately, there were no critical issues.
The Recode project is more successful when users don't notice the changes after release. Fortunately, many didn't! In Japan, after the FlutterKaigi event, some people shared on social media that the license screen had changed. It was a relief that no major issues arose after updating to the Flutter version. 🙂
Post-release retrospective
The project, which began as winter was ending, was completed as the next winter approached.
Looking back, I believe most members involved in the project were concerned about replacing the technical foundation without slowing down the fast-paced product. I also regret not being able to prepare a more refined collaboration method due to prioritizing speed during the project. In the Korean team, since not all members participated from the beginning, there was a tendency for all code reviews to be concentrated on one or two individuals. Additionally, while the Japanese team had some role division by domain, the Korean team lacked clear role division and had to work on feature units without background knowledge. Despite receiving significant help from the Japanese team, there were cases where code reviews were conducted without knowing the background knowledge, leading to many difficulties, especially in the early to mid-stages of the project. Although there were areas for improvement in the collaboration rules, such as the Scrum method decided during the kickoff, we didn't have the opportunity to discuss them deeply due to time constraints, which was regrettable.
However, I believe we achieved a lot with this Recode project. Now, all app products within Demae-can have been transitioned to Flutter. The foundation for the Korean and Japanese app development teams to work as one team technically has been established. Although users may not notice, there are many internal changes as well. I believe that the ConsumerApp Recode project was completed without issues because both teams approached it with mutual respect and consideration, filling in each other's gaps.
After releasing the app, we conducted an online KPT (Keep/Problem/Try) retrospective using Miro. After recording events that occurred during the 10 months of the project, we each posted Keep/Problem/Try thoughts that came to mind at specific points in three different colors. The most common feedback was that it would be great for both teams to meet offline more frequently for collaboration after the project ended.
The Korean and Japanese teams have now started discussing product development more closely as one team. We share the latest Flutter technologies, issues, and solutions from different domains, and even discuss non-development activities like collaboration methods and deployment processes. The environment where we can collaborate to create the best product as one team has been established, and I believe that building mutual respect and trust through the Recode project is an invaluable achievement that cannot be quantified.
In conclusion
The Recode project is a step back for two steps forward. This project required the longest breath among all Recode projects. Thanks to this, ConsumerApp is now technically and humanly prepared to deliver the necessary value to users more quickly, including UI/UX improvements. It's time to take two steps forward. We look forward to greeting you with an ever-improving product.