Mobile Tech Lead

Explore mobile architecture, modularization, technical ownership, leadership practices, and cross-platform decisions.

~14 min reading
Suggest content

Mobile Tech Lead and Architecture

This sheet is the canonical reference for mobile leadership and architecture. Interview routes should point here instead of restating these explanations.

Senior engineer, Tech Lead, Engineering Manager, and Staff Engineer

RolePrimary accountabilityHands-on expectationTypical evidence
Senior engineerDeliver a complex slice safely and improve nearby systemsRegular implementation, testing, reviewMakes good local tradeoffs; follows behavior into production
Tech LeadKeep a team technically aligned and shipping a coherent productStill codes, investigates, reviews, and prototypes; coding share varies with team riskClear interfaces, decisions, sequencing, quality bar, unblocked engineers
Engineering ManagerTeam health, staffing, performance, and organizational deliveryUsually less production coding; may review technical contextCoaching, hiring, sustainable delivery, team outcomes
Staff / Principal engineerImprove outcomes across multiple teams or a technical domainHands-on through design, prototypes, incidents, or critical codeResolves cross-team constraints and enables durable technical direction

These titles vary by company. Ask what decisions the role owns, who manages people, and how much implementation is expected. A Tech Lead is not automatically the manager, architect, or person who approves every line.

🟩 Practical split: Stay hands-on where code or production evidence is the fastest way to reduce uncertainty. Delegate bounded implementation and review; keep ownership of system coherence, sequencing, risks, and feedback loops.

Technical ownership and decision-making

Ownership means making an outcome explicit: scope, constraints, decision-maker, success measure, failure plan, and follow-up owner. It does not mean unilateral control. For consequential choices, write an Architecture Decision Record (ADR): context; options; decision; consequences; risks; revisit trigger. Keep it short enough that a future team can discover why the choice was made.

Use a decision loop:

  1. Clarify product goals, user impact, constraints, and non-functional requirements.
  2. Identify reversible choices versus expensive-to-reverse commitments.
  3. Compare a small number of credible options against agreed criteria.
  4. Prototype or measure the highest-uncertainty assumption.
  5. Record the decision, owner, rollout, metrics, and rollback trigger.
  6. Revisit when evidence or constraints change, not because a pattern became fashionable.

For example, do not choose micro-frontends or a new mobile module system because a large app “needs scale.” Identify the coordination or build bottleneck, measure it, then compare the least complex remedy with the expected cost of migration.

Technical leadership in day-to-day work

ResponsibilityUseful behaviorAvoid
Engineering standardsState a small set of enforceable defaults; automate checksStyle rules that require constant manual policing
Code reviewPrioritize correctness, privacy, compatibility, testing, and clarity; explain the riskRewriting code to match personal taste
MentoringSet a shared goal, offer a stretch task, give timely specific feedbackTaking over the task when it becomes uncomfortable
DelegationDefine contract, owner, decision boundary, check-in, and integration pointAssigning a vague outcome with no authority or support
Technical debtLink debt to user or delivery risk; prioritize reduction with feature workTreating all old code as equally urgent
Estimation and planningDecompose unknowns, surface dependencies, use ranges, update from evidencePromising an exact date before discovery
Stakeholder communicationDescribe impact, options, confidence, and the next decision neededReporting only implementation detail or optimism
Incidents and releasesStabilize first, communicate, preserve evidence, learn without blameHiding uncertainty or making an unmeasured risky change

Planning, governance, and production ownership

A Tech Lead usually partners with product and delivery peers on sprint planning; the lead does not promise an estimate alone. Break work into reviewable slices, identify platform/backend/release dependencies, set confidence ranges, and make technical risks visible before they become schedule surprises. Maintain a rolling technical roadmap tied to product capabilities, reliability/build health, staffing, and migration windows; distinguish committed work from options and discovery. Coordinate cross-team API and release contracts early. An ADR records a decision; architecture governance should make exceptions, ownership, and review triggers clear without requiring committee approval for routine implementation.

During an incident, reduce user impact first: establish incident owner and communication cadence, use safe rollback/feature disablement if available, preserve diagnostics, and coordinate the right specialists. Afterward, examine contributing system conditions and convert findings to owned reliability work. Release responsibility includes staged rollout, compatibility, crash/performance signals, a stop/rollback threshold, and a support path—not just pressing a publish button.

Mobile architecture patterns

Choose boundaries from change ownership, testability, and team topology. Patterns are tools, not maturity levels.

PatternTypical shapeMain responsibility / dependenciesStrengthCost / common failureFit
MVCView ↔ Controller ↔ ModelController coordinates view/modelNatural UIKit entry pointFat view controller when logic has no seamsSmall screens, legacy UIKit
MVVMView → ViewModel → UseCase/ModelView renders state; view model coordinates screen behaviorTestable presentation logicView model becomes a second application layerUI with meaningful state/transitions
MVVM-CCoordinator → MVVM screen → domainView model emits route intent; coordinator owns navigationSeparates flow from screen stateCoordinator graph can become a global routerMulti-step UIKit or hybrid flows
VIPERView → Presenter → Interactor → Entity; Router → next screenView, interactor, presenter, entity, router splitStrong ownership boundariesToo many files and ceremonial pass-throughsLarge teams with stable, independently owned features
Clean ArchitectureUI / DB / API adapters → domain policyDomain policy inside; adapters outside; code dependencies inwardFramework and transport changes are containedOver-abstraction without real volatilityProduct/domain rules outlive UI and API details
HexagonalAdapter ↔ Port ↔ Domain ↔ Port ↔ AdapterCore defines ports; adapters implement themExternal systems replaceable in testsPorts mirror every implementation detailIntegrations, persistence, and transport have real variation
Modular architectureFeature A → Shared contract ← Feature BBuild and ownership boundaries; no sibling internalsParallel work and selective reuseExcessive targets, cycles, slow builds, shared-module dumping groundMany features/teams or measured build/ownership problems
RepositoryUseCase → Repo contract ← API / StoreHides data source selection and mappingCentral cache/offline policy“Repository” becomes a generic god objectMultiple sources or meaningful persistence policy
Dependency injectionComposition root → object graph → consumersConstruct dependencies at a composition boundaryReplaceable dependencies and visible ownershipService locator/global container hides graphTest seams and cross-module composition
CoordinatorFlow owner → route intent → featureOwns navigation transitionsFlow testability and deep-link mappingOverbuilt routing framework for simple navigationUIKit/hybrid navigation with complex flows
FactoryConsumer → Factory → configured productEncapsulates creation/configurationCreation policy stays out of callersFactory per type with no varying policyComplex setup, variants, or lifecycle rules
Observer / reactivePublisher → subscribersPropagates state/eventsDecouples event producer and consumerHidden ordering, retain cycles, unbounded event streamsUI state or event fan-out with explicit cancellation
Unidirectional data flowAction → Reducer → State → ViewState plus actions/reducerTraceable state changesGlobal store and giant reducers for local screensComplex interactive state and reproducibility
State machineState + Event → State / EffectExplicit states and legal transitionsInvalid transitions become visibleState explosion if every detail becomes a stateAuth, payment, upload, onboarding workflows
Domain-driven designContext A ↔ Contract ↔ Context BDomain model/language reflects business rulesShared understanding for complex domainsTactical DDD ceremony for simple CRUDRich, evolving business rules

The pattern table is a selection summary. The existing architecture sheet has concrete canonical walkthroughs for MVVM-C, VIPER, Factory and Observer, protocol-oriented design, and architecture testing. Prefer those explanations over cloning an implementation into every interview route.

Architecture boundary example

TEXT
SwiftUI / UIKit feature
        │ user intent + render state
        ▼
Presentation model ──→ Use case / domain policy
                              │ defined port
                              ▼
                       Repository contract
                              ▲
              ┌───────────────┴───────────────┐
              │                               │
        API adapter                    Local-store adapter
              │                               │
       URLSession / DTO                 SwiftData / SQLite

The diagram shows source-code dependencies, not necessarily runtime call direction for every operation. UI frameworks and transport types should not leak into domain rules without a deliberate reason. Keep a boundary only when it clarifies a real change, test, ownership, or security seam.

Swift implementation: port, adapter, composition root

SWIFT
struct Account: Equatable, Sendable {
    let id: String
    let displayName: String
}
​
protocol AccountRepository: Sendable {
    func account(id: String) async throws -> Account
}
​
struct LoadAccount {
    let repository: any AccountRepository
​
    func callAsFunction(id: String) async throws -> Account {
        try await repository.account(id: id)
    }
}
​
struct AccountScreenModel {
    let loadAccount: LoadAccount
}
​
// Composition root chooses concrete adapters once.
let repository: any AccountRepository = LiveAccountRepository(client: apiClient)
let screenModel = AccountScreenModel(loadAccount: LoadAccount(repository: repository))

LiveAccountRepository and apiClient are app-specific implementation names. The example demonstrates the boundary; it is not a complete networking client. For the authoritative dependency inversion and DI explanation, see Dependency Inversion in Swift.

The view owns only presentation of an input value; a parent owns the shared expansion state and passes a binding. For the full state-ownership explanation, see the @Binding question path.

SWIFT
import SwiftUI
​
struct AccountDisclosure: View {
    @Binding var isExpanded: Bool
​
    var body: some View {
        Toggle("Show account details", isOn: $isExpanded)
    }
}

Modular architecture and enterprise mobile platforms

Start with feature ownership and stable contracts, not a target-count goal. A possible banking app tree is:

TEXT
AppShell
├── Features/Accounts ───────┐
├── Features/Payments ───────┼──> DesignSystem, DomainContracts
├── Features/Cards ──────────┘          │
├── Core/Authentication ────────────────┤
├── Core/Networking ────────────────────┤
├── Core/Observability ─────────────────┤
└── PlatformAdapters <──────────────────┘

Enforce acyclic dependencies, narrow exported APIs, explicit resource ownership, and one direction of dependency. Shared frameworks should contain capabilities used by multiple owners—not code that merely lacks a team. Track target build time, incremental rebuilds, binary size, API breakage, and ownership friction before/after modularization.

For multiple apps/teams, version shared packages, document deprecation windows, publish compatibility checks, separate internal and public APIs, and make release trains explicit. Use remote configuration and feature flags for controlled rollout, but define an expiry owner and kill switch; flags are not authorization. API compatibility is a client/server contract: support older app versions during adoption windows, validate schemas, and make additive changes before removals.

Cross-platform engineering decision matrix

OptionSharing leverageNative fitMain risksTeam / product conditions that favor it
Native Swift + KotlinLowest shared implementationHighest per-platform access and idiomDuplicate domain/UI work; coordination driftDistinct platform experiences, deep OS integration, platform-specialized teams
FlutterHigh shared UI and application codePlugin/platform-channel boundaryPlugin maturity, framework expertise, platform-specific UX gapsConsistent UI and a team willing to invest in Dart/Flutter
React NativeShared UI/app logic with JS ecosystemNative modules bridge to platform APIsJS/native boundary, dependency and upgrade managementExisting TypeScript/React strength and meaningful code reuse
Kotlin MultiplatformShare selected domain/data logicNative UI remains an optionInterop/toolchain complexity and ownership ambiguityTeams want shared business/networking logic but native presentation

Compare total lifecycle cost, accessibility, offline behavior, performance on target devices, testing, hiring, release ownership, platform API lag, code-sharing percentage, and migration exit cost. A “shared code” percentage alone is not a decision metric.

KOTLIN
data class AccountSummary(val id: String, val balanceMinorUnits: Long)
​
interface AccountRepository {
    suspend fun load(id: String): AccountSummary
}

This small Kotlin sketch illustrates a platform-native suspend API and typed model. Kotlin Multiplatform can share such domain/data code where it is valuable while the Android and iOS UI layers remain native; the interop/build ownership cost still belongs in the decision.

Mobile security, delivery, and reliability cross-references

Keep server authorization authoritative, protect credentials with platform storage, minimize sensitive analytics, and threat-model deep links, local data, and network boundaries. The shared threat-model and agent controls are in AI Security and Governance; mobile OAuth, TLS, token, and API controls are in Backend Engineering. For the iOS CI/CD rollout pattern, see release-process interview answer; for crash/startup/performance investigation, see mobile production diagnostics.

Quick Recap · Architecture interview hit points

🧠 Say: “I begin with product constraints, team ownership, and measured pain. I choose the smallest boundary that isolates a real change, assign an owner, define a migration path, and verify the outcome with build time, defect rate, lead time, or user metrics.”

🟨 Gotcha: Clean Architecture is not synonymous with more layers. A protocol for every type, a repository over a single immutable array, or a shared module owned by nobody can make an app harder to change.

References