On this page
When a third and fourth team join the Android app, symptoms appear quickly: merge conflicts in the same files, inconsistent patterns, CI taking an hour, "who owns this?" questions, and releases blocked by one team's unfinished feature. These are organisational problems that need technical support.
The pillars of a scalable codebase
1. Modules and ownership
Feature modules map to teams, and core modules map to a platform team. A CODEOWNERS file routes reviews automatically:
/feature/checkout/ @shop/checkout-android
/feature/search/ @shop/discovery-android
/core/designsystem/ @shop/design-system
/core/network/ @shop/android-platform
/build-logic/ @shop/android-platformSee Modularization Strategies for module slicing.
2. A platform (or "core") team
As the app grows, someone must own build tooling, CI, shared libraries, dependency upgrades, performance and app-wide concerns such as startup and security. A small platform team provides these as products for feature teams: documented, versioned and supported.
3. A shared design system
A :core:designsystem module with themed Compose components, tokens and screenshot tests keeps the UI consistent and prevents five slightly different buttons. See Material 3 Theming.
4. Automated quality gates
- Build, unit tests, lint and static analysis (for example detekt or ktlint) on every PR.
- Module dependency rules checked automatically.
- Screenshot tests for the design system and key screens.
- Performance benchmarks on a schedule, with regression alerts.
- Build caching and test sharding to keep CI fast. See CI/CD with GitHub Actions.
5. Feature flags and release trains
With many teams, waiting for every feature to be "done" before releasing doesn't work. Instead:
- Release trains: branch and release on a fixed cadence, such as weekly or every two weeks.
- Feature flags: merge unfinished work behind flags, and enable it remotely when ready.
- Staged rollouts on Google Play, with crash-rate monitoring before going to 100%.
interface FeatureFlags {
fun isEnabled(flag: Flag): Boolean
}
enum class Flag(val key: String, val default: Boolean) {
NEW_CHECKOUT("new_checkout", false),
UPI_AUTOPAY("upi_autopay", false),
}
@Composable
fun CheckoutEntry(flags: FeatureFlags, onLegacy: () -> Unit, onNew: () -> Unit) {
if (flags.isEnabled(Flag.NEW_CHECKOUT)) onNew() else onLegacy()
}Remove flags once a feature is fully launched. Track an owner and an expiry date for each one.
6. Architecture decision records (ADRs)
Short documents that record significant decisions: context, decision and consequences. For example, "We use Navigation 3 for new features" or "Repositories throw; ViewModels map errors". They prevent decisions being re-argued and help new developers understand why.
# 7. Error handling between data and UI layers
Status: Accepted (2026-09)
Context: Teams handled network errors inconsistently (Result, exceptions, nullable).
Decision: Repositories throw typed exceptions; ViewModels map them to UiState messages.
Consequences: Consistent tests and UI; Result types allowed only inside data layer.Scaling milestones
| Team size | Typical needs |
|---|---|
| 1โ5 developers | Shared conventions, code review, CI basics |
| 5โ15 | Feature modules, CODEOWNERS, design system, feature flags |
| 15+ | Platform team, release trains, dependency rules, ADRs, performance monitoring |
Common mistakes
- Relying on tribal knowledge instead of documented conventions and automated checks.
- A "common" module that everyone edits, which becomes a merge-conflict hotspot.
- Feature flags that are never removed.
- The platform team becoming a gatekeeper instead of an enabler.
Frequently asked questions
Monorepo or multiple repositories?
Most Android apps use one repository with many modules. Atomic changes across modules are simpler, and build caching mitigates scale.
How do we keep architecture consistent?
Use templates (convention plugins and module templates), documented ADRs, lint rules and review by owners, rather than relying on memory.
When do we need a platform team?
When build, CI and shared-library work regularly blocks feature teams, or no one owns app-wide quality.
Conclusion
Scaling an Android codebase means structure (modules and owners), enablement (platform team and design system), automation (CI gates) and controlled delivery (flags and release trains), held together by documented decisions.
Next step: go deeper into the data layer, starting with Retrofit in Android: Complete Guide.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Architecture makes the most sense when you apply it to a real project and see why each boundary exists. Learn how maintainable Android applications are designed by building one.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.