Scaling Mobile Development
How can we scale mobile development practically?
Mobile-Client-Specific Back End
Back end for front end (BFF)
Not too long ago, the number of users on apps surpassed the number of users on the web. Many web companies have realized this and started app support. How can we best scale this mobile development?
Mobile-Client-Specific Back End
Traditionally, most companies started off as web companies. They grew their business, and had a group of developers for web front-end and back-end work.
When apps came into the market, like any other business stakeholder, management were careful not to over invest in the new area in case it never picked up. They either contracted this work out or hired very few developers in that space. Mobile-client development is a side project. Hence it has to piggyback on the web back end for its service.
As we know, mobile is not the web, hence its design and logic differs from the web. To cater for the differences, the mobile client has to implements a ton of logic to transform the payload from the web back-end service before using it.
With the limited number of developers working on the mobile client and all of the additional logic required, things don’t bode well for many mobile clients. Further obstructing things is the dilemma that the knowledge skill set for web and mobile is different, so you can’t always just transfer developers from one team to another.
Back end for front end (BFF)
As app development becomes more crucial, we should consider partitioning out specific web back-end services just for the mobile clients.
There are few advantages of this model.
- We could leverage the existing skilled developers in the web back end for the work in the mobile back end.
- We could move some of the logic and decisions needed for the mobile-client side to the mobile back end, reducing the work needed on the mobile client.
- If carefully partitioned and constructed, we could make the mobile client more dynamically change-driven from the mobile back-end without needing to release a new version of the app for each update.
This model is known as back end for front end (BFF).
Contract between back end and client
Another way to further speed up developments is having a common contract between the server and the client. In most cases, JSON is used. However, JSON, as a serializable string, does need to be housed in a model that both the server and client have to implement separately.
With this in place, we would:
- Reduce the need to generate separate model codes on different platforms.
- Reduce the risk of error due to model-interpretation differences.
- If we carefully craft the model, we could potentially directly map the model to the view of the client, reducing the need of translating the model into the view of the client.
All this makes mobile-app development more scalable in the long run.
Modularize and partition responsibility
While it is true having a mobile-client-specific service helps, we still have to increase more development on the mobile-client side due to more features being delivered, more UI changes, etc.
Traditionally mobile apps only have a single module, with everyone working and contributing to it. But as we add more and more developers, this will not scale.
Such a model will produce more:
- Conflicting change, making code reviews more of a challenge
- A vested interest in everyone else’s code, as they are in same module and could be related
- Potentially bad architecture that possibly violates boundary rules, as there are no strong boundaries between codes (everything could access everything)
- Increased building time, as changes to the module have to recompile the gigantic module
Modularize and partition responsibility
Apps development could be modularized for both Android and iOS. Below is a recommended way of partitioning. It is separated into three layers.
Note: The below model is referenced from Google’s original proposal of Instant App as stated here.
- The coordinator module
- The feature modules
- The base module
Note: In case you’re wondering how to wire up the dependencies (in Android), you could refer to Setup Android modules with Dagger 2.
The coordinator module
This is the app-level coordinator. At this level, the work involved is coordinating the app-level logic flow across features. It could have its own interface or view (activity, fragment, ViewController, etc.) as the main entry views and is potentially responsible for the navigation logic between the features.
The group over here could also be responsible for the continuous integration (CI) work. It’s likely to improve the overall tool, chain grouping all other modules together to build the full app — as well as the entire release process.
If there’s a bug or issue, this group might be the first to triage and investigate and pass on to the relevant parties to look further. The group would be responsible for looking over the overall health of the app (e.g. monitoring crash rate, performance, etc.).
The base module
This is the module that defines all the needed consistent architectural patterns each group needs to adhere to. It could have:
- The relevant interfaces/protocol for feature groups to implement and follow suite. It would have the base reference of a design-related item (e.g., common color, views, text, etc.).
- The API to access various systems or app-level requirements applicable to all features (e.g., the network client, the repository, the analytics, the authentication, etc.)
- The common utilities that each feature group will need and use (e.g., various extension functions, test classes)
- Sharing of common external libraries used by all features
This group is responsible for the communication and education of all feature groups of what’s available and what’s in place for the feature group to adopt and follow.
In the diagram, I’m just showing a single base module. However, if needed, one could partition into multiple base modules with different sets of responsibilities.
The feature modules
In the diagram I’m showing three features of a single-layer module. This could be further partitioned if needed into more features and into multiple layers, depending on the scale of the apps development.
Some might consider having a feature-based module that can only share across some modules.
Each group is fully responsible for the code within, and another group would be concerned less. It is not mandatory for each feature group to follow the same development pattern as the group might have differing needs. Besides, using different ways produces better synergies and learning across groups. Everything that should be standard and consistent should be defined at the base level, with interfaces adhered on top.
Having such a setup allows groups to have the flexibility within to work on their module, while also having a set of rules/patterns inferred from the base module to follow upon. Such flexibility within boundaries enables the work to scale better.
Having said this, developers could work across modules. But the owner of the module would need to review the changes done to the codes. If the changes are on the base module, a communication process is needed to disseminate the knowledge of what has changed and how it could impact the other feature groups.
Note: With the partitioned modules, we are tempted to make them into libraries generated from different GitHub repositories. My personal recommendation is not to do so, as that complicates CI due to versioning control. Having all of them in the same GitHub repository, just partitioned by module, does help to ensure better integration and faster identification of misalignment and issues.
Avoid the tradeoff of platform advantage for consistency
In mobile development, we have iOS and Android. While they are similar, they are different in many ways. Ideally, we should make them as consistent as possible so development can be smoother.
However, each platform does have its advantage and challenges. Android has its challenges of life-cycle handling and dependencies injection. iOS modularization is more tedious with CocoaPods compared to Gradle for Android. Language is different as well, (e.g., Swift and Kotlin). Android has the architecture component, while iOS doesn’t have one. iOS has it’s SwiftUI released, but Android still has to rely on the XML codebase.
Making them fully consistent — by not implementing some features on one platform because the other has limitations or not taking advantage of one platform’s available features because it doesn’t look good on another — is OK for a start. But in the long run, it will bite us, as the app will lose its industry edge. We might spend more time fixing and addressing suboptimal issues later.
Limit making generic cross-platform development
I would advocate cross-platform learning. It’s always good for an Android developer to know something about iOS and vice versa.
However, with mobile development getting more attention, specialization is getting more important than generalization. Just like how in web development front end and back end is considered two parts to one whole, we should specialize instead of having one person who tries to know everything. Generalization leads to slow progress and expertise development. It is only handy when we have limited resources and it is not a main project.
Nonetheless, we should still try to have some people with some level of cross-platform knowledge, so planning and architecting for development consideration can be better handled, as this person could bridge across platform discussion.
In house-code structure
As you hire more web developers in-house, you might be thinking of transitioning them to do app development — just with a limited scope to work on. To do that, the app developers will introduce a set of interfaces and in-house built generic coding that allows limited customization that one could implement on a feature.
It may seem helpful, as now we could redeploy the web developers for mobile work. While theoretically this sounds good, practically it’s just not feasible.
Even if we successfully made one possible, the future enhancement needed on it is not going to scale, as the mobile development platform continues to changes. Plus, the base-module and coordinator-module developer will have to keep up with the needed pace to supply code structures for the feature developers.
The feature developers would not enjoy the work, as the learning is not industry code but an in-house built tool. In the long run, this is not ideal.
So it is better to start with the painful path to train up and transition the web developer to do the features in a mobile way, and in the long run, they will learn and be able to contribute more widely in the work.
Leverage industrial libraries and tool sets when possible
We should avoid reinventing the wheel. If there’s a library set widely used and supported for the side feature we need, we should just use it. We should avoid in-house built items unless they are a core product of the organization or the library/tool set is not matured.
Image-loading libraries, networking libraries, DI frameworks, and reactive-programming frameworks are some examples of great leverage pieces we could use. This means should not avoid using Dagger 2 in Android just because iOS doesn’t have that framework, unless Dagger 2 is not bringing its benefits to the Android work.
It is an open-source world. One could either use the library or fork the entire codebase to improve on it, as long as it adheres to its licenses and provides the needing credit to the owner of the those libraries. Hence, there’s no fear of using third-party libraries if it helps and speeds up the works.
The above are some generic high-level ideas with some personal opinion on how to scale for mobile development. Feel free to share if you have other related ideas and experiences. I hope this post has been helpful to you.