Evolving Mercari's iOS codebase into a multi-product monorepo
Bài tóm tắt Mercari 2025 3. Quy mô lớn Độ khó: Trung bình
Mercari · 2025 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Một bài trong series Global App (ngoài sheet). Android đi cùng hướng: Mercari 2025.
Bối cảnh
- Lịch sử app iOS của Mercari: app Nhật native; Mỹ và Anh bản đầu dùng cùng repo với Nhật, chuyển hành vi bằng directive biên dịch, không có kiến trúc module, rồi mỗi nước fork riêng vì quá phức tạp; Mỹ bản 2 lai native + React Native, bản 3 React Native toàn bộ; Nhật viết lại từ đầu (GroundUp App) native; Mercari Hallo dùng Flutter; Atte, Kauru mỗi app một repo mới.
- App Nhật đã dùng Bazel với kiến trúc micro-module (ranh giới chặt, trừu tượng rõ), remote cache và Remote Build Execution sẵn có; cộng cả hai app hiện hơn 900 module.
Thay đổi chính
- Global App phát triển trong repo của app Nhật, đổi repo thành monorepo nhiều sản phẩm:
- Cấu trúc mới:
Products(code riêng từng sản phẩm),CompanyvàInHouse(module dùng lại giữa sản phẩm;InHouselà module nói chuyện với dịch vụ nội bộ như nền tảng định danh toàn cầu). - Phân loại module: A dùng lại được ngay (kiến trúc, Design System 4.0), B cần refactor (nhiều module trong
Groupchỉ đúng cho app Nhật), C không dùng lại được vì gắn khái niệm của app Nhật. Kiểm cả việc module có nên dùng lại không về khái niệm, không chỉ hành vi hiện tại. - Script, cấu hình Bazel, rule tự viết, CI tách phần chung khỏi phần riêng sản phẩm; mỗi sản phẩm vẫn có luồng nộp store riêng. Phụ thuộc ngoài (Firebase) mặc định cùng phiên bản và cấu hình, cho phép khác khi cần.
- Chuyển dần: hơn 50 engineer iOS vẫn đang làm trong repo; tạo cấu trúc và chuyển nhóm A trước, rồi refactor nhóm B từng module một (phải thống nhất với các bên) song song với phát triển Global App.
- Cấu trúc mới:
- Quy tắc: không bao giờ dùng lại module ở mức tính năng giữa hai app, dù lúc đầu hai app trông giống nhau; phải viết lại theo nhu cầu Global App. Lý do: dùng lại mà không có định hướng từ trên dẫn tới trừu tượng sai, sửa một app làm đổi app kia. Ngoại lệ phải đồng ý với đội sở hữu module chung.
- Global App mặc định theo kiến trúc và công cụ của app Nhật (swiftui-atom-properties, SwiftUI, design system, DI, A/B test), chỉ khác khi có lợi rõ: gRPC tới BFF, Phrase cho đa ngôn ngữ. Cải tiến làm ở Global App đôi khi được chuyển ngược về app Nhật.
- Kích thước binary 38,8 MB ở bản 1.17.0 (App Store Đài Loan).
Vì sao
- Đội nhỏ, lịch gấp; dùng lại nền móng, kiến thức và hạ tầng build đã tối ưu thay vì dựng lại như các app trước.
Bài học cho SWE
- Monorepo nhiều sản phẩm chỉ khả thi khi ranh giới module đã chặt; Bazel chỉ kéo module cần nên app mới không gánh build time hay kích thước của app cũ.
- Dùng lại nền (design system, client dịch vụ nội bộ), không dùng lại tính năng.
- Mặt trái theo bài: thay đổi module chung phải review, QA cho mọi sản phẩm; sinh project Xcode "chứa tất cả" chậm và nặng (đội enablement cho chọn phạm vi target Bazel); quy tắc phụ thuộc giữa sản phẩm mới chỉ là guideline, cần kiểm tự động.
- Lợi ích phụ không dự tính: code nhất quán giữa hai app làm agent AI sinh code đúng mẫu hơn.
Liên quan
- Kiến trúc frontend · CI và build ở quy mô lớn
- Cùng series: Mercari 2025 · Mercari 2025 · Mercari 2025 · Mercari 2025
- Codebase nhất quán là hạ tầng cho agent: Spotify 2026
#iOS#monorepo#multi-product#Bazel#micro-modular#remote cache#remote build execution#module reuse#Products/Company/InHouse#React Native#Flutter#fork#wrong abstraction
Được nhắc tới trong
Chủ đề (2)
Công ty (1)
Bài tóm tắt (3)