This post has been republished via RSS; it originally appeared at: Microsoft Mobile Engineering - Medium.
Scaling Teams Mobile Development — Evolving the design pattern.
Monolithic mobile app codebases and stories about making them modularized is a common topic of discussion now-a-days. There are articles written on this from Slack, Square, Reddit, Robinhood and many more. Here is a series of posts that provides a systematic way and a design pattern to modularize mobile apps effectively. This is especially applicable in an ecosystem where multiple partner teams works together in the same code base and compete to highlight and surface their respective features.
This is part 1 of the series and talks about an effective design pattern that can be adopted for a modularized and easy to integrate code base, where multiple teams collaborate.
Evolving the design pattern.
Microsoft Teams is a massively successful product by any parameters. Teams Mobile is an integral part of the success story. With tens of millions of active user base, supporting multitude of critical workflows across different sectors in a dynamic area of realtime communication, collaboration brings its own unique challenges. The codebase has continuously evolved to support a diverse feature set. Teams being a hub product, multiple teams in Microsoft work closely with the Teams core team to enhance the functionalities. Given this, we needed a clean architectural framework to remain agile, reduce side effects and to establish clean contracts for the developers. How we achieved this, is the topic that we will discuss in this article.
Any mobile app performs a bunch of useful functionalities. These functionalities are broadly categorized as features. Apps can have features exposed via an app bar, hamburger menu or tabs. Search bar on top the app is a very common. Some apps have a landing page with tiles that segue to features within the app. Let’s categorize these facets of a mobile app as entry points for these features.
Microsoft Teams mobile app has multiple such entry points. It has Feeds, Calendar, Chat, Channels, Files etc. (features) surfaced through an app bar and app tray (entry points). It has user profile, settings etc. (features) as part of the hamburger menu (entry point). It has search (feature) as part of the top bar (entry point). We evolved a design pattern to streamline the features and how they integrate with the entry points. Let’s explore this in more detail.
Introducing Contributor Design Pattern
Any feature team would love to have independence, developer velocity, stability, predictability, ease of integration their features. To facilitate this, we came up with a Contributor design pattern which has these key aspects:
- Contribution SDK
Let us look at the components of this design pattern in more detail:
Any independent feature is called a Contributor. They live in their own module, decoupled from the main app module. They implement a Contributor interface which provides a way to query the contributions that it supports. Common interface dependencies needed by contributor modules are provided through Contribution SDK.
Site is a well-defined area in the app where features can be surfaces. Site will expect a certain Contribution interface to work with. Typically, site works with a list of contributions, rendering them in a generic way and passing on user actions such as tap, long tap etc to the contribution for handling. Site is a mediator between feature and entry point.
Contribution is the contract between Site and Contributor. This typically consists of state and actions. State includes any data that is needed by the site to render the feature in the entry point. Actions represents handlers that Site can invoke based on user interactions.
Every contributor requires some crosscutting functionalities such as Logging, Telemetry, Networking, Navigation, Experimentation, Storage etc. Contribution SDK is a module containing interfaces and types that are needed by a Contributor. The implementation for these interfaces comes from the respective functional modules and gets dependency injected as needed.
Let us take a small example on how this will work in practice. Let us assume that our example app needs 2 features exposed in the app bar such as Feature1 and Feature2. Here, Feature1 and Feature2 are two Contributors. They live in their own modules. They take dependency on Contribution SDK. Site needs certain information from each of the contributor such as the icon, text, color for it to render these feature entry points and a bunch of callbacks to pass on user actions like tap, long tap etc. to the contributor. All these requirements are converted into a contract to form a Contribution interface. Contributor will implement this interface and site will use those interfaces. We will see a full fledged example in the next part of this series.
Once we have these building blocks, then the next question is, how does it come together? For this we need to address the following:
- Contributor Registry
- Contribution Management
Since contributor modules are independent, we need a way to bring them all together. Contributor registry is a central mechanism where all the contributors are registered. We will explore more about this in the next part of this series.
Once we have contributors registered in the system, there should be a way for sites to query them. Contribution management provides this functionality. It collects all registered contributors, initializes them as needed and queries the contributors to get their contributions for the site in question and provide it to the site. In addition to this, management layer takes care of enforcing policies governing the contributors, such as, ordering, filtering etc.
Putting all the components together this is how the layering looks like:
Wrapping it up
Modularized code base with proper layering is a dream come true for many. The contributor design pattern can be effectively applied to answer the “how” of it.
In Microsoft Teams mobile app, entire app bar and app tray area and many parts of chat feature have been cleaned up using this pattern. There are more and more sites being created. The guideline for new features is to build themselves as a contributor modules and integrate through the clear APIs contracts provided by this design pattern.
This effort has paid off really well. Partner teams are finding it much easier to integrate using well-defined APIs. They are able to write better tests mocking the API layer which is stable and reliable. Regressions are lesser and problems related to sites are now easier to diagnose. Telemetry has been built around these sites to monitor their performance and detect any anomalies. Even changes inside the core logic, such as changes in ordering policies or toggling features, are centralized and easier to manage.
Since features in Microsoft Teams mobile app are huge in themselves, the design paradigm should be democratized and should be easily adopted by every feature team. This will make sure that the central architectural team does not become a bottleneck. A more pragmatic approach of creating a feature module and bridging the existing code via that module was taken in some cases. This way we lay the foundation for the feature team to refactor their area into the contributor module.
Integrating this design pattern along with the existing legacy code path and ensuring nothing regresses for the user was yet another challenge. We used A/B fork along with experimentation flags to flight this feature and gradually roll out, addressing any issues that we found along the way.
We have gone through the various components of contributor design pattern in this post. In the next post, we will take a look at an example app which implements this design pattern to make it more concrete. Until then!
Scaling Teams Mobile Development — Evolving the design pattern. was originally published in Microsoft Mobile Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.