What We’ve Discovered in Order to Scale Up Our iOS Mobile Team

As expected, it is not just about hiring and/or adding more talent.

Agung Pratama
Stockbit-Bibit Engineering
6 min readJul 8, 2022

--

I wrote this paper as a follow-up to my prior presentation at the StockBibit Engineering Class. This article is a more condensed, less-technical version of what we learned in our team because the original talk’s topic mentioned how typically an iOS app grows over time (you may wish to access the original talk’s slides here to get a more detailed glimpse).

Prologue

As some have known since the middle of 2021 the previous year, Stockbit and Bibit are quickly expanding and hiring heavily for all of their numerous teams. Certain teams’ headcount increased tenfold compared to their previous totals; this also applies to the iOS team at Stockbit. To cut a long story short, the iOS team has gathered some lessons we learned over the past year that we want to offer in this article.

1. Hiring with Purpose

Let’s start with the most obvious one. Hiring with purpose, especially when we intend to make a sizable number of hires all at once. We started by separating each team’s hiring slot and necessity per tribe. Even when we talked specifically in iOS, each team member’s hiring requirements could vary. Some of the iOS team would need candidates with concurrency knowledge such as DispatchQueue, some teams would prefer strong experience in unit-test, some would prefer a candidate with WebSocket experience, and others would want a candidate with a little bit of knowledge of the products’ features (e.g., stock). Some teams might even further expand their experiences in SwiftUI in the requirement (psst…Yes, we also work on a fascinating hidden project for SwiftUI).

Since there are different hiring opportunities for each team, we will present the recommended applicant profiles for each team separately. We evaluate each candidate’s profile as an individual, listing them in the team that may be the fittest. Thanks to the recruitment team, this process was quick and simple for us.

2. Start By Improving Your Code Base While It is Still Easy

Many teams frequently fall into this trap for a variety of reasons. They are trapped on a codebase in an unchangeable state, making it difficult to evolve or improve. A building’s architecture is completely unrelated to how software architecture operates. Contrary to how a building is completely designed from the beginning, software must be flexible and open to change while also making no assumptions about the change itself.

3. Your Choice of MV** Matters Less Than You Think

Some people might get into an argument when it comes to this point. We, the iOS Engineers, tend to divide and debate within ourselves. We are starting to mention that choosing one of the popular UI design patterns such as MVVM, MVP, and VIPER is important. However, in this situation of expanding your team, it doesn’t bring any huge impact. There is no perfect pattern that fits all teams’ needs. As a result of the never-ending discussion and ongoing holy grail pursuit, numerous new design patterns have appeared out of nowhere over the years.

As our goal is to enable the separation of concern at the level of each business unit, how one screen component is separated is less relevant at this point. In this particular scenario, it is more crucial to choose a strategy for how each isolated and independent business module can communicate with each other, leading to the next point.

4. Turns Out Business Flows Was Actually Never Been That Isolated After All

Starting from point one, you might already guess that as a growing iOS Platform team, we are starting to work as a separate business unit for each team independently and are no longer viewed as one entire platform team (which typically only has two or three iOS engineers). That is why we also try to strategically move our monolith codebase to be more modularized to reduce friction between Tribe Engineers.

We started experimenting with tribe-based modules in the hopes that everything would go smoothly, but — shocker — a lot of our business flows, logic, custom views, and other components actually need to be shared and discussed among business modules!

For a starter, we only had a simple goal: to make it into separated modules and make it work. We had our time trying to figure out how to make modules that can work with other things inside the other module. We started our first mistake by importing a business module inside another business module. That might technically work, but it brings us to the problem of the “Wanted a banana, but what got us was a Gorilla holding a banana” problem and compilation time. Fortunately, this issue is quickly addressed and handled by strictly prohibiting business modules from importing each other to keep the relation horizontal instead of vertical.

This process of moving into modular was done in a period of time. After a while, it makes a quite interesting study case to learn: We put too many things on shared/low-level modules. The idea of creating its own separated fast compiled mini-app and fast independent unit test is blocked due to this problem. One of our modularized demo module compilation times is around 65% of our full app compilation time. This makes the benefit of modularity in terms of faster compilation itself not achieved.

We realized we need to work on how we put things on which layers and revisit how we manage dependency. We want a modularized architecture, not a “distributed monolith.”

5. The Value of Managing Dependencies Strategy

We took our time to take a step back and revisit how we fell into this problem. We realized that all the problems that arose were just about managing dependencies. Then we realized that we could just put the practice of the Dependency Inversion Principle to tackle this problem (just like we always have done at the components/design pattern level, but we never thought that we could do this on the module level).

At this point, we decided to take our time in experimenting and move our protocol to be separated into a different framework. In short, we try to rethink our definition of a module.

If we have Trading.framework, we will add a new target called TradingAPI.framework, which contains all the protocol layers moved out from its original framework. This way, all of the entry point of Trading.framework would only be done through its TradingAPI.framework. Therefore, all other frameworks will be able to get the functionality of a module just by importing the small API framework, without importing the chunky implementation detail of the necessary framework.

This way, we could achieve:

  1. Keep each business module horizontal to each other
  2. Taking back all the extra things being put on low-level/shared modules.
  3. The module is still able to talk to each other
  4. Faster local compilation time when working on implementation detail modules.

By putting this method into practice, we can also compel and recognize the critical role that a Composition Root and Dependency Injection Engine plays in an iOS architecture. The method of how they operate in integration is enough complexity to merit its article. So, if you’d want to be informed when the post is published, please think about following Stockbit-Bibit Engineering on Medium!

6. Keep the Team in Sync!

Since we have brought more people to join the team and combined our Remote-Friendly Working policy, we acknowledge the potential of feeling lost and out-of-sync with each other, especially since we moved into independent business units/tribes’ team-based approach.

We have put so much effort into bringing the whole iOS team member in sync on time every week. We also blocked our calendar from every non-prioritized meeting to attend Stockbit’s iOS team sync, which takes around 60 minutes. We take turns to present technical and/or non-technical material to learn, unlearn and re-learn from one another, having small talks, syncing codebase improvement updates, and just having a quick announce feature updates for each tribe.

Closing

We hope that our study case of how we experienced growth in a short amount of time would be somewhat beneficial to other iOS teams that in the process of scaling up their team.

As was already indicated, a separate essay in addition to this one would only be appropriate to cover the specifics of how we deal with dependencies. To avoid losing out, please think about following Stockbit-Bibit Engineering’s!

References:

--

--