· 6 min read Posted by Kevin Galligan

Scaling: Why it's hard

KMP For Native Mobile Teams

The "final" phase is scaling, where feature dev is implemented with KMP. That allows the total volume of KMP code to significantly increase, and KMP's potential efficiencies to be realized.

This is a post series with several sections. If you landed here first, make sure to go back to the Intro to get the full story.

Javascript disabled?
Our site requires Javascript for some sections. Please check that Javascript is enabled.

Overview

There is much to consider for the scaling phase. We’ve found that most teams who start with KMP, and stick with it long enough to include real code within their apps, will continue using KMP. Measuring success is more about:

  • How much of the code is actually shared?
  • How much more efficient is development with KMP?
  • How “happy” is the team, both with the process and the outcome?
  • How much time and effort were required to get there?

As stated at the beginning, KMP’s potential is huge, but any team’s particular outcome depends heavily on approach. Let’s start with a review of what “scaling” actually means.

Scaling and Feature Dev

Scaling KMP means implementing features with KMP because that is the bulk of the work for app development. Using KMP to publish a few modules with significantly less code and code that changes less frequently means you’re not using KMP that much.

Amount of kmp diagram

Obviously, if KMP can only be used for a subset of code, its impact on your development is severally limited.

In short, KMP needs to be used where the bulk of your work is being done.

Internal SDKs

We’re focused here on scaling in the feature area of a particular app, but this is a simplification. If you do happen to have a lot of code that would be described as a “library”, and/or where a lot of regular work is being done, then using KMP there isn’t an “efficiency blocker”. If this code is currently written in both Swift and Kotlin, with multiple developers and potentially conflicting changes, that code will be subject to the same team and workflow issues that “feature dev” is in our discussion. The solutions will be similar as well.

For larger apps, different portions of app logic may “naturally” be split into versioned SDKs purely for organizational purposes.

The same logic might need to be shared between projects. Alternatively, many apps divide into “feature teams”, where different teams focus on different “feature groupings” of a larger app. In general, as an amount of logic and code grows, there’s a “complexity limit”, beyond which formal libraries and publishing make sense.

Within those divisions, similar analysis should apply.

If the module is pure logic, then KMP should probably be treated like any other library. You’re not going to need much platform specialization. A prime example is the work Google is doing with KMP and Workspace. The internals have an enormous amount of complex shared logic. While certainly portions need platform-specific work, the vast majority is “logic”, and largely moving from “Java-oriented” logic.

If an app’s features (screens, data flow, etc) are split into feature teams, each focused on a particular area of the larger app, each of those features and teams would face the same issues presented here. Team coupling, and specialists trying really hard to be generalists. Similar approaches should probably be considered. Just “scaled out” to the feature teams.

Teams often have difficultly with scaling. The difficultly emerges from a few areas. Most prominent:

  • Workflow model. Library publishing models don’t support feature dev. Having a single repo with the KMP and native apps’ code solves library publishing issues, but can introduce its own.
  • Team member roles and “lanes”. This is essentially who is editing what, and what is the process that a new team member takes to “get there”.
  • Clear “borders” and architectural patterns. Where does the shared code “stop”, and what patterns does your team adopt for that architecture?

Tooling choice, configuration issues, and other similar topics, while important, are more mechanical. They generally have clear solutions. Team structure, app-specific decisions, and day-to-day workflow are less clear-cut and critical to resolve.

Workflow Model

Much of the trouble with scaling is with the workflow model. In our work with teams, we’ve observed some basic core issues with structure and planning.

Library publishing doesn’t scale

Teams almost universally introduce KMP by publishing “libraries”. That is what we recommend in the Piloting and Iterating phases. In review, publishing libraries is what it sounds like. Code is developed independently, and “published” with sequential versions. In our formal terminology, it is “unidirectional”.

The first major issue is that scaled development requires frequent, often conflicting changes.

Frequent, conflicting changes.

Areas of code where multiple engineers are making frequent, often conflicting, changes require version control. That is one of the main reasons everybody uses git. Development would be slow-motion chaos without it.

“Scaling KMP”, as a general goal, means using KMP where you’re doing most of your work. Not coincidentally, that is where you would find multiple engineers making frequent, often conflicting, changes.

Library publishing is linear. A published library is the end product. You can apply the same thinking to your app as a whole. Apps are published to app stores, with linear, infrequent versions.

It is simply the wrong model. Most “shared code” has “distance”. It is used less often, if at all, and whatever it does needs be valuable enough to justify its inclusion. Any team could write their non-UI code in C++. Some have tried. Those efforts have largely been abandoned. However, many apps include C++ for specific purposes. That code, if not edited by different engineers, or even different organizations, is rarely edited in the way KMP needs to be.

The mental model of “shared code”, however, is often a library model. That is because it can, and sometimes needs, to be. Scaled KMP requires new mental models.

The second is the disconnect between portions of code that should be edited as a single unit. Editing UI separately from the architecture that supports it is difficult and inefficient. Also quite frustrating for the team. They won’t like it.

Screen/Architecture Disconnect

When implementing a feature, say some screens and supporting logic, you write them as a unit. Attempting to first write the logic, then the UI on top of it, without being able to edit the logic, would be very frustrating and inefficient.

Yet that is the basic workflow modeled with library publishing and KMP.

Logically, it’s similar to how back end and front end code are developed. The needs of the app will drive changes to the server, but there’s a lot of extra communication and work involved. Because there needs to be. Adding that kind of bottleneck unnecessarily is what we’re trying to avoid.

Of course, to even attempt to function in this model, a method to locally edit the library code is introduced. That makes this workflow model at least possible. However, once editing is complete, the library must be “published”, then updated in the app code, then reviewed, etc. The workflow’s overhead eats any potential efficiency that KMP might introduce.

We’ve talked to many teams who attempt to scale KMP while continuing with a library model. For the most part, these teams fully understand the issue, and often saw it coming in advance. If you and your team are in this camp, well, we have some good news coming (keep reading).

Feature development, or scaling in general, with library publishing is the wrong configuration. The structures are simply incompatible.

Monorepos deeply couple the apps and teams

To address the library publishing problem, most KMP devs and teams assume the next step is merging all of the app and KMP code into a single repo, editing everything in place.

This is the default configuration for KMP, at least as far as all samples and default tooling are concerned. It is such an ingrained assumption that few really question it. Myself included, until we spent significant time working with teams doing it.

It is the default model, and it’s wrong.

Not “technically” wrong. It’s simply not how native mobile teams work.

The root cause is that KMP code edits impact both platforms at the same time. The effect is that both apps and teams are tightly coupled.

Coupled apps and teams

Our teams are comprised of native mobile specialists. There should be some blending of skills over time, mostly focused around the shared logic. However, a monorepo forces both platforms to immediately consume KMP changes.

That means either the platform specialist needs to complete changes on the “other” platform, or the other app team needs to be involved, before features “land”. You can structure development in this way, but there is a significantly more workflow coordination. There will also be much longer periods where implementations remain unmerged.

If you’ve ever had to work with your team to prioritize PR completions, double that, across teams. Then, of course, changes made during the “other app” implementation bounce back for a follow-up PR. It’s not great.

These workflow problems can be mildly frustrating when development is “calm”. As deadlines approach, workflow problems are magnified.

A common result, is code that should be in KMP is written in the platform-specific language instead, to avoid the overhead.

No Staggered Dev

Staggering platform implementations can be valuable. Not released implementations. Just during dev.

Much of product development is iterative. What the product manager asks for is interpreted by a designer, which is then interpreted by an engineer. For new features with any level of complexity, that is rarely the last step. There are usually iterations. The coupling described above both delays that iteration, and adds extra work that is pure waste. Most feature feedback can be done by looking at one platform.

And again, bad when seas are calm. Horrible when running up on a deadline.

Merging repos is “risky”

The final point. Physically “merging” repos, with CI, PRs workflows, etc, is not trivial. We’ve talked to a number of teams who do plan to do this, but delay “until the next release is out”. There’s always another release coming.

Once merged, “unmerging”, if you decided that was best, is similarly not trivial.

Merging repos, purely to support KMP development, is a big decision. Big decisions are risky.

Team member roles

Keeping our “native mobile teams” context in mind, a lack of role definition often creates issues. Unless the plan for your team is to completely ignore platform specialties, from day 1, some form of task alignment to skill set needs to be implemented.

Even if fully “merging teams”, it’s important to remember that no matter how long your team has been operating in that mode, every new team member is at “day 1” all over again.

“Onboarding” is obviously not unique to KMP, but the added platform dimensions, and relative lack of experience at present (KMP is new), exacerbate it.

Clear “borders” and patterns

Most platform teams develop general guidance around architectural patterns used in their codebase. An “anything goes” approach is chaotic.

That, of course, is within a context where the developers have a general familiarity the platform’s conventions and patterns.

In a KMP context, engineers will be significantly less familiar with each other’s platform. Clear agreement and documentation on the patterns is more valuable than it would be in a purely native-dev environment.

Of particular importance is the “border”. Android and iOS have different lifecycles and conventions. With the addition of Compose and SwiftUI, both platforms on their own struggle with applying general guidance to specific app situations.

Libraries and patterns in the KMP community are virtually all “Androidy”. The majority of libraries and patterns developed recently will assume Compose as the UI. Applying this general KMP guidance, itself limited and new, can be problematic.

That general KMP guidance, besides causing more technical issues, also makes the sub-par iOS developer experience worse.

Measuring Results

Assessing developer efficiency is notoriously difficult. Speaking as a manager and business owner, I understand the desire. Speaking as a developer, and also as a manager, I understand just how difficult that would be to do.

Implementing any new platform is a risk. Regardless of approach, it will consume people’s time and focus. Being able to confidently say that KMP has been a benefit to your team is necessary. Being able to show that KMP has improved your development efficiency would be great. For some, it is critical. Understanding that your team is enjoying KMP, and that their expertise with the technology is continually improving, would be very valuable as well.

Most measures of efficiency are either verbal reports, or something like “velocity charts”. While neither is particularly accurate on their own, again, KMP is adding a different dimension of complexity.

We’ve talked with teams who have self-reported metrics that are all over the range.

Some Example Discussions

(They’re anonymous, but they’re real)

A team struggling with KMP came for help. It turns out they’ve had amazing success, but were always focused on improving. This was one of the examples of a team that attempted to “merge” the specialists. They’ve been shipping production KMP apps for quite a while, but struggled with the “merged” aspect.

Another team reported that things were going great, and had a very different approach to the tech than we were discussing. I was curious. It turns out “great” meant that they were enjoying learning KMP, but were far from shipping anything. The deadline was, essentially, “when it’s ready”.

Several teams discovered that KMP’s efficiency didn’t scale. The tech was useful, but fell rather short of expectations. Spoiler: they were all using the library publishing model. Each individually had other build and mechanical issues, but the big problem was workflow.

The earlier phases, Piloting and Iterating, are explicitly not focused on performance. Scaling is. Having some useful assessment of progress is important.

Next Section : Scaling: What to do?

While teams struggle to scale, the problems aren’t really complex. They simply need to be identified and addressed.