The Critical Role of Platform Team in Modern Android Development

Adnan Khan
Bazaar Engineering
Published in
11 min readJul 16, 2024

In the world of Android app development, the concept of platformization has emerged as a game-changer. But what exactly does platformization mean in this context, and why is it becoming so crucial?

Having worked with various companies like Bazaar, Careem, Airlift, and Telenor, I’ve noticed a recurring challenge: dynamic feature teams often struggle with non-functional requirements such as upgradation, cost management, security, automation, and more. Without a dedicated team to handle these concerns, feature teams can become bogged down, leading to inefficiencies and slower development cycles.

This is where the platform team comes into play. By establishing a specialized team to manage and optimize these critical aspects, feature teams can focus on innovation and delivering high-quality features. This article will delve into the key responsibilities of a platform team, including upgradation, cost reduction, security, automation and tooling, modularization, standardization, and more. We’ll explore how this approach not only streamlines the development process but also enhances app stability, performance, and overall quality.

Upgradation

Upgradation is crucial for various aspects of an Android app to ensure compatibility, security, and performance. This includes supporting the latest versions of Android, Kotlin, and Gradle, as well as updating Firebase SDKs (including Crashlytics and Analytics). Regular updates are also required for Jetpack Compose, Room, Navigation Component, and Firebase Cloud Messaging (FCM). Additionally, changes in user permissions, updates to test frameworks, third-party dependencies, and security patches are necessary. These updates typically involve changes in the codebase and user flows.

Cost Reduction and Budgeting

The Platform team assists in reducing costs by carefully evaluating and selecting the most cost-effective tools and services. By comparing options like Apptimize or Firebase Remote Config for feature flags and A/B testing, Mixpanel or Firebase Analytics for event analytics, and OneSignal or MoEngage for push notifications and in-app messaging, they ensure optimal resource selection and allocation. They also determine whether Firebase Crashlytics or Sentry is more suitable for crash reporting and performance monitoring, considering both functionality and cost. Additionally, they help in selecting other tools, such as Firebase Authentication or Auth0 for user authentication, and the most appropriate app distribution tool, thereby optimizing the overall budget.

Security

Ensuring robust security is critical for an Android app. This includes measures such as implementing code obfuscation to protect the app’s codebase and preventing app cloning. Carefully consider what data to store in internal versus external storage, and use encryption for any sensitive data stored on the device. Implement SSL pinning for secure communication over the network and ensure that APIs used by the app are secure and authenticate all requests.

When you check the merged manifest file of your app, you may find many permissions added by dependencies included in Gradle. It is necessary for your app to request only the permissions that are essential for its functionality.

Validate all input from users to prevent injection attacks, and sanitize input to remove potentially harmful characters. Avoid loading untrusted content in WebViews; if necessary, ensure that the content is sanitized. Use parameterized queries or ORM libraries to prevent SQL injection.

It’s important to restrict GCP API keys (such as those for Maps and Firebase) to only authorized applications and manage GCP and Firebase access with strict authentication and token handling practices. Utilizing the Secrets Gradle plugin for securely storing keys is recommended. Implement checks to detect rooted devices and emulators, especially in customer-facing apps.

Automation

Automating redundant tasks allows the team to focus on more critical work. For example, automating the creation and publishing of builds on the Play Store can save significant time.

In the continuous integration (CI) process, using linting tools for code styling ensures the team doesn’t have to worry about formatting — the linter handles it. Tools like Detekt and SonarQube automatically review code for quality, security vulnerabilities, and technical debt. Code coverage for unit and UI tests can be run both locally and remotely.

In the continuous delivery (CD) process, tasks such as UI tests, build creation, build sharing with the internal team, and releasing builds on the Play Store can be automated. Additionally, release notes and version bumping can be automated based on time periods or tags. For instance, feature releases could be rolled out automatically every Monday, with CI creating the necessary commit.

Any CI platform can be used, but building your own on AWS has advantages. AWS charges only for processing power, which is often cheaper compared to services like GitHub Actions, Bitrise, CircleCI, or Travis CI. Your platform team can set this up.

Automating the management of multiple live versions on the Play Store is essential to avoid issues with users on older versions. Manually enabling force updates for specific versions and tracking release versions can be automated using Google Play APIs and the Firebase SDK.

When multiple teams contribute to one app or several apps, they often code in separate repositories for different features, such as rewards, promotions, network modules, and analytics modules. Automating the publishing of these repos to an artifactory and making them available to container apps through Gradle dependencies can eliminate the need for version bumping commits. Version bumping automation for third-party libraries can be managed using Dependabot or similar services.

Modularization

What is Modularization and How Can the Platform Team Help You with It?

My definition of modularization is structuring your codebase in a way that multiple teams can contribute to the same app while working independently in their own GitHub repositories. Each team would manage their own repository, without needing to know about other teams’ code. The code from these repositories would then be used as dependencies in the main app code.

For example, in an e-commerce app, features like Cart, Rewards, Referrals, Promos, Networking, Analytics, Utility code, Widgets, and Identity code can be developed as separate modules, with their code residing in their own repositories.

Communication Between Modules and the App Code

To enable these modules to communicate with the app code, the platform team can focus on the following:

  1. Networking: Define a common interface for network communication that each module can implement. This ensures consistent and secure network interactions across all modules.
  2. Database: Create a shared database schema or interface that each module can use to read/write data, ensuring data consistency and integrity.
  3. Events: Implement an event bus or observer pattern to allow modules to subscribe to and publish events, facilitating seamless communication between different parts of the app.
  4. Notifications: Standardize notification handling so that modules can easily send and receive notifications without conflicts.
  5. Deep Links: Set up a centralized deep linking scheme to manage how different modules handle and respond to deep links.

Benefits of Modularization

  1. Independence: Teams can focus on specific areas without worrying about the complexities and potential issues in other parts of the codebase.
  2. Reusability: Modules developed for one project can be reused in other projects, saving time and effort.
  3. Clean Code: By separating concerns, each module can maintain a clean and focused codebase, reducing the likelihood of introducing bugs.
  4. Scalability: As the app grows, new features can be added as new modules without affecting existing code, making the app more scalable.
  5. Simplified Collaboration: Multiple teams can work simultaneously on different parts of the app, speeding up development and improving productivity.

By modularizing the codebase, you can create a more maintainable, scalable, and efficient development process, enabling your teams to deliver high-quality features independently and effectively.

Standardization

When managing multiple apps and teams, standardization across various areas is essential. This includes:

  • Analytics Events: Establish a consistent naming format.
  • Code Styling: Standardize naming conventions, function size, class size, constants, and resources.
  • Branching Strategy: Define a clear and consistent branching strategy.
  • Image Guidelines: Specify formats to use (JPEG, PNG, WebP, vector, etc.).
  • Design Units: Define and consistently use dp and sp units.
  • Modern Architecture: Provide guidelines on what belongs in each layer.
  • Testing Strategies: Define unit, integration, and UI testing strategies.
  • Testing Frameworks and Libraries: Standardize the use of frameworks and libraries such as JUnit and Espresso.
  • Test Coverage: Set minimum test coverage requirements.
  • Common or Utility Libraries: Clearly define what goes into common or utility libraries.

Unit Test Coverage and UI Tests

Given that the Android app release deployment has significant differences compared to the backend (BE) service deployment, an Android hotfix release reaching to users may take days. Therefore, it is crucial to ensure that every line of code is thoroughly tested. Achieving high unit test coverage in Android app projects is a challenging task. Most teams and companies struggle to meet even the 80% coverage mark. Some teams settle for 40%, others for 60%, and some manage 70%, while a few do not write unit tests at all.

Even when teams write unit tests, they often do so merely to increase coverage numbers rather than to maintain high quality test that covers the happy cases and the edge cases. However, at least some teams are writing unit tests. That said, the practice of writing integration tests and UI tests are almost completely missing in many companies, with little thought or effort dedicated to them. Unit tests alone do not provide the necessary confidence; it is the integration and UI tests that assure the app will function correctly and meet requirements.

So, who should take responsibility for the initiative to achieve 100% coverage, and to write integration and UI tests? Additionally, who will implement checks to encourage or enforce teams to write unit tests, integration tests, and UI tests?

I believe it should be the platform team’s responsibility to onboard the teams for writing tests. They should add constraints to CI pipelines and perform the necessary integrations to ensure that UI and integration tests are run within these pipelines. By doing so, the platform team can drive the effort to achieve comprehensive test coverage and stable releases.

Tech debt

Tech debt in Android applications often accumulates when apps are developed hastily, leading to code quality issues that need to be addressed later. Identifying and managing this tech debt becomes the responsibility of the platform team, who must gradually improve the code quality in accordance with industry best practices. In my experience, many apps suffer from violations of Separation of Concerns (SOC) and the Single Source of Truth (SSOT) principles. This results in business logic and application logic being spread across the view layer, with no centralized source for data or calculations.

Additionally, improper implementation of the Navigation component and careless use of Hilt annotations are common issues. Custom coroutine scopes are also frequently misused, leading to potential memory leaks and unexpected behavior. Other common forms of tech debt include inadequate unit testing, poorly defined APIs, lack of documentation, and inconsistent coding styles across the codebase. These issues not only make the code harder to maintain but also increase the risk of bugs and slow down the development process over time. To tackle tech debt effectively, a systematic approach involving code reviews, refactoring, and adherence to design patterns and coding standards is essential.

App Stability and Performance
Ensuring optimal performance in an Android app is critical for user satisfaction and retention. Performance issues such as slow startup times, unresponsiveness, high memory and CPU usage, excessive battery consumption, and frequent crashes can significantly degrade the user experience. Users expect apps to be fast, efficient, and stable. Meeting these expectations requires careful performance optimization and testing. Effective performance measures ensures that the app runs efficiently under various conditions, providing a seamless experience for users.

The Platform team can help integrate tools into the codebase, such as LeakCanary, WatchDog, Firebase Performance SDK, Firebase Crashlytics ,Macrobenchmark, and Microbenchmark. They can also utilize tools like Android Profiler and Systrace to gain insights into various performance metrics, helping identify and fix bottlenecks. By incorporating these tools into the development and testing processes, the Platform team can ensure that apps are not only functional but also optimized for performance, leading to greater user engagement and loyalty.

Adoption and Experimentation

Adopting new tools and technologies within an organization can be a daunting task, particularly when it involves significant changes like switching from Gradle to Bazel as a build tool, moving to Kotlin Multiplatform Mobile (KMM), or using a different dependency injection library than Dagger and Hilt. The platform team plays a crucial role in facilitating these transitions by experimenting with new technologies and managing their adoption across the company.

The platform team can lead the way in evaluating and integrating new tools and frameworks. For instance, they can explore the benefits of Compose Multiplatform for building UI components that work seamlessly across Android and other platforms, or the advantages of KMM for sharing code between Android and iOS. By creating prototypes, running pilot projects, and conducting performance and compatibility tests, the platform team can gather valuable insights and address potential issues early on.

Furthermore, the platform team can establish best practices, provide training, and develop documentation to ensure a smooth transition for the rest of the development teams. They can create standardized templates, configurations, and scripts for new tools like Buck and Bazel to simplify their usage. By setting up CI/CD pipelines and integrating these tools into the development workflow, the platform team can streamline the adoption process and minimize disruptions.

Wrap-Up

In essence, the platform team’s role is indispensable in modern Android app development. By managing upgradation, cost reduction, security, automation, modularization, and standardization, they empower feature teams to concentrate on innovation and high-quality feature delivery. Their efforts in ensuring comprehensive test coverage, addressing technical debt, and optimizing performance lead to more stable and efficient applications. Moreover, by adopting and experimenting with new tools and technologies, the platform team keeps the development process at the cutting edge, ultimately driving higher productivity, better user experiences, and sustained success in the competitive app market.

Finally, I would like to extend my sincere thanks to Talha Zia for diligently reviewing this article and offering valuable suggestions for improvement.

--

--