Meta Mid-Level Mobile Developer Interview Preparation Guide
Meta's interview process for Mid-Level Mobile Developers consists of a recruiter screening phase, followed by a technical phone screen, and concludes with 4-5 onsite interview rounds lasting a full day. The process evaluates core algorithmic problem-solving, mobile-specific technical knowledge, system design for scalable mobile applications, and cultural alignment with Meta's values. All technical interviews include coding components with emphasis on communication, debugging, and verification of solutions. The process typically spans 4-6 weeks from initial application to offer.
Interview Rounds
Recruiter Screening
What to Expect
This is a 30-minute phone call with a Meta recruiter to assess your background fit for the role. The recruiter will review your resume, discuss your interest in Meta and the Mobile Developer position, explore your relevant experience with iOS and/or Android development, and answer your questions about the role and company. This is a preliminary screening to ensure basic qualifications are met before proceeding to technical interviews.
Tips & Advice
Be genuine and enthusiastic about Meta's mobile products. Have a clear 2-minute summary of your mobile development background and why you're interested in Meta specifically. Mention specific Meta mobile products you use and appreciate. Ask thoughtful questions about the team, mobile strategy, or technical challenges. Smile while speaking (interviewers can hear it in your voice). Be ready to discuss your availability and any visa sponsorship needs.
Focus Topics
Knowledge of Meta's Mobile Ecosystem
Understand Meta's mobile products: Facebook, Instagram, WhatsApp, Threads, and Meta Quest. Know which platforms they serve (iOS, Android, or both), and recent features or challenges they've announced.
Practice Interview
Study Questions
Project Ownership and Impact
Prepare 2-3 examples of mobile projects where you took ownership, drove decisions, and delivered measurable impact. For mid-level, emphasize cross-functional collaboration and mentoring involvement.
Practice Interview
Study Questions
Motivation for Meta and Mobile Role
Develop a compelling narrative about why you want to work at Meta specifically and why mobile development excites you. Reference Meta's mobile products, engineering culture, or technical challenges.
Practice Interview
Study Questions
Background and Mobile Development Experience
Articulate your experience with iOS development (Swift/Objective-C), Android development (Kotlin/Java), and/or cross-platform frameworks (React Native, Flutter). Emphasize projects you've owned and impact you've delivered.
Practice Interview
Study Questions
Technical Phone Screen
What to Expect
This 45-60 minute phone interview focuses on core data structures, algorithms, and problem-solving ability. You'll receive 1-2 coding problems to solve in real-time using a shared code editor (CoderPad or similar). The interviewer will assess how you approach unfamiliar problems, ask clarifying questions, communicate your thought process, and verify your solutions. While not mobile-specific, this round tests foundational algorithmic thinking essential for mobile development.
Tips & Advice
Ask clarifying questions for 1-2 minutes before coding—understand input constraints, edge cases, and performance requirements. Narrate your approach verbally before writing code; interviewers evaluate thinking, not just the final answer. Write clean, readable code with clear variable names. Test your code mentally against basic cases, edge cases (empty inputs, single elements, negatives), and stress cases (very large inputs). If stuck, explain your best approach, ask for hints, and iterate. Meta values candidates who debug methodically rather than freeze. Discuss time and space complexity in Big O notation. Ask if your solution can be optimized and propose improvements if time permits.
Focus Topics
Linked Lists and Node Manipulation
Understand pointer/reference manipulation, traversal, and state management. Practice cycle detection, reversal, merging, and insertion/deletion in linked lists.
Practice Interview
Study Questions
Graphs, BFS, and DFS
Understand graph representations (adjacency list, adjacency matrix), depth-first search, breadth-first search, cycle detection in directed/undirected graphs, and topological sorting.
Practice Interview
Study Questions
Binary Trees and Tree Algorithms
Solve problems involving tree traversal (inorder, preorder, postorder), tree properties (height, depth, balance), lowest common ancestor, path sums, and tree construction.
Practice Interview
Study Questions
Hash Maps and Hash Sets
Use hash-based data structures to optimize solutions. Understand time/space tradeoffs and when to choose hashing over sorting or nested loops.
Practice Interview
Study Questions
Arrays and Strings Algorithms
Master problems involving array manipulation, substring search, pattern matching, and string transformations. This includes problems like longest substring without repeating characters, two-sum variants, and array rotation.
Practice Interview
Study Questions
Communication and Problem-Solving Process
Practice articulating your approach before coding, asking clarifying questions, explaining your decisions, and walking through test cases verbally. Learn to adapt when constraints change mid-interview.
Practice Interview
Study Questions
Mobile Technical Interview 1: iOS Development
What to Expect
This 45-50 minute onsite interview focuses on iOS-specific development skills including Swift/Objective-C, UIKit/SwiftUI fundamentals, app lifecycle, memory management, concurrency, and mobile-specific design patterns. You may solve coding problems with an iOS lens, discuss architecture decisions, or implement features using platform APIs. The interviewer assesses your depth of iOS knowledge and ability to apply it to real mobile challenges.
Tips & Advice
Come prepared with concrete examples of iOS apps you've shipped or contributed to significantly. Be ready to explain architectural decisions you made (MVC, MVVM, VIPER, Clean Architecture) and trade-offs. Discuss how you handle memory in iOS (strong/weak references, retain cycles, ARC). Prepare for questions about async/await, Combine framework, or GCD depending on how current your experience is. If asked to code an iOS feature, think about app lifecycle, threading, and user experience. Mention your experience with app store deployment, TestFlight, and submission guidelines. For mid-level, emphasize how you've mentored junior iOS developers or improved code quality on your team.
Focus Topics
Concurrency, Threading, and Async Patterns
Understanding of GCD (Grand Central Dispatch), DispatchQueue, async/await syntax, Combine framework, and how to avoid main thread blocking. Knowledge of deadlocks, race conditions, and thread safety.
Practice Interview
Study Questions
Memory Management and Performance Optimization
Understanding of ARC, strong/weak/unowned references, retain cycles, and memory leaks. Ability to profile apps using Xcode Instruments (Allocations, Leaks, Time Profiler) and optimize performance for mobile constraints (battery, RAM, network).
Practice Interview
Study Questions
iOS App Lifecycle and State Management
Understanding of UIViewController lifecycle, view controller transitions, state restoration, saved state APIs, and how to handle app suspend/resume scenarios. Knowledge of SceneDelegate for multi-window support on iPad.
Practice Interview
Study Questions
iOS App Architecture and Design Patterns
Knowledge of MVC, MVVM, and VIPER architectures. Understanding of dependency injection, reactive programming (RxSwift/Combine), and separation of concerns. Ability to discuss trade-offs between architectural approaches.
Practice Interview
Study Questions
Swift Language Fundamentals and Modern iOS Development
Deep understanding of Swift syntax, optionals, error handling, and modern Swift features. Familiarity with recent Swift versions and how they improve iOS development. Understanding of when to use protocols, extensions, and generics in iOS apps.
Practice Interview
Study Questions
Mobile Technical Interview 2: Android Development
What to Expect
This 45-50 minute onsite interview focuses on Android-specific development skills including Kotlin/Java, Android lifecycle, fragments, services, background processing, and mobile-specific design patterns. You may solve problems with Android context, discuss system design for scalable Android apps, or implement Android features using platform APIs. The interviewer assesses your Android development depth and how you apply platform knowledge to real challenges. This round mirrors the iOS interview but tests Android expertise.
Tips & Advice
Showcase Android apps you've shipped, particularly any with complex UIs, background tasks, or tricky lifecycle scenarios. Be fluent in Kotlin syntax and explain why you prefer it over Java (or vice versa if applicable). Discuss your experience with modern Android architecture (MVVM with LiveData/StateFlow, Clean Architecture, MVI). Be ready to explain Activity/Fragment lifecycle in detail and common lifecycle-related bugs you've fixed. Discuss how you handle background work (WorkManager, Service, IntentService) without draining battery. Mention experience with dependency injection (Hilt, Dagger) and testing frameworks. For mid-level, explain how you've improved Android code quality or mentored junior Android developers on your team.
Focus Topics
Android Architecture: MVVM, Clean Architecture, StateFlow
Practical experience with modern Android architecture patterns including MVVM, Clean Architecture, and reactive state management using LiveData/StateFlow. Understanding of separation between UI, ViewModel, and Repository layers.
Practice Interview
Study Questions
Background Work and Battery Optimization
Knowledge of WorkManager, Service, and when to use each. Understanding of Doze mode, battery optimization, and how to minimize battery drain from background operations. Familiarity with permissions model and runtime permission handling.
Practice Interview
Study Questions
Android Concurrency with Coroutines
Understanding of coroutines, suspend functions, structured concurrency, context switching, and Job/Scope management. Knowledge of Dispatchers (Main, IO, Default) and when to use each. Understanding of cancellation and exception handling in coroutines.
Practice Interview
Study Questions
Kotlin Language and Java Interoperability
Fluency in Kotlin syntax including extension functions, coroutines, null safety, and data classes. Understanding of Java interoperability for legacy codebases. Knowledge of when Kotlin features improve code expressiveness and maintainability.
Practice Interview
Study Questions
Android Lifecycle and Fragment Management
Deep understanding of Activity/Fragment lifecycle methods, state management across lifecycle transitions, SavedStateHandle, ViewModel scope, and how to handle configuration changes gracefully. Knowledge of multi-fragment navigation.
Practice Interview
Study Questions
System Design Interview: Scalable Mobile Architecture
What to Expect
This 45-50 minute onsite interview evaluates your ability to design scalable mobile systems at an architectural level. Rather than designing large distributed systems like backend engineers, you'll focus on mobile-specific challenges: designing offline-first mobile apps, scaling real-time features for millions of users, handling network reliability, caching strategies, data synchronization, and mobile SDK architecture. The interviewer assesses how you balance mobile constraints (battery, memory, connectivity) with feature requirements. You might discuss designing a mobile messaging feature, real-time notification system, or offline data sync mechanism.
Tips & Advice
Ask clarifying questions first: How many users? How often do they interact? What devices/networks? Then propose a solution architecture. For mobile, always discuss trade-offs between user experience and resource constraints. Talk about caching (in-memory, disk, server), network optimization (batching, compression, retry logic), and offline behavior. Discuss data synchronization—how do you handle conflicts when users work offline? Mention real examples from Meta products (Instagram Stories, Facebook Messenger). Sketch diagrams on the whiteboard or virtual board to visualize your architecture. Discuss testing strategies for unreliable networks and resource constraints. For mid-level, explain how you'd mentor a junior engineer on these trade-off decisions.
Focus Topics
Mobile SDK Architecture and API Design
Designing SDKs for mobile platforms: clean APIs, backwards compatibility, version management, and minimizing SDK size and performance impact. Understanding of shared libraries, binary size optimization.
Practice Interview
Study Questions
Caching Strategies and Memory Management
Multi-layer caching: in-memory caches (LRU, TTL-based), disk caches (image caches, API response caches), and CDN/server-side caching. Understanding of cache invalidation, expiration policies, and balancing memory constraints.
Practice Interview
Study Questions
Real-Time Features and Push Notifications
Designing systems for real-time updates: push notification architecture, WebSockets/polling trade-offs, handling notification delivery reliability, and user experience patterns for real-time events.
Practice Interview
Study Questions
Offline-First Architecture and Data Synchronization
Design mobile apps that work offline and sync when connectivity returns. Understand conflict resolution, eventual consistency, and how to prevent data loss. Knowledge of local databases (SQLite, Realm, Room) and sync patterns.
Practice Interview
Study Questions
Mobile Network Optimization and Reliability
Strategies for minimizing network requests, batching operations, using compression, handling slow/unreliable networks, implementing exponential backoff retry logic, and request prioritization. Understanding of HTTP/REST vs gRPC trade-offs for mobile.
Practice Interview
Study Questions
Behavioral and Culture Fit Interview
What to Expect
This 40-45 minute onsite interview assesses how well you align with Meta's values, work style, and culture. The interviewer will ask behavioral questions using the STAR format (Situation, Task, Action, Result) to understand how you've handled challenges, collaborated with teams, resolved conflicts, and demonstrated leadership. Topics typically include: how you've owned projects, dealt with ambiguity, handled failure, mentored or influenced others, and navigated disagreements. This round evaluates your growth mindset, collaboration style, and fit for Meta's fast-paced, execution-oriented culture.
Tips & Advice
Prepare 5-6 STAR format stories covering: a project you owned end-to-end, a time you mentored someone, a conflict you resolved, a failure you learned from, a time you drove change, and a time you collaborated cross-functionally. Use specific examples with measurable outcomes. For mid-level, emphasize ownership, mentoring, and influence over just task completion. Practice articulating what you learned and how it shaped your approach. Research Meta's values (Move Fast, Be Bold, Focus on Impact, Build What Matters) and weave these into your stories naturally. Be authentic—interviewers can detect coached responses. Listen carefully to follow-up questions and provide additional detail. Ask thoughtful questions about the team, challenges, and career growth at Meta.
Focus Topics
Cross-Functional Collaboration and Conflict Resolution
Describe a situation where you worked with designers, backend engineers, or product managers toward a common goal. Explain how you aligned perspectives, resolved disagreements, and maintained relationships. Emphasize finding win-win solutions.
Practice Interview
Study Questions
Mentoring and Growing Other Engineers
Provide examples of how you've helped junior developers grow, given effective feedback, and elevated their skills. Discuss code reviews, pair programming, design discussions, or formal mentoring. Explain your philosophy on mentoring.
Practice Interview
Study Questions
Learning from Failure and Technical Debt
Share an example of a technical decision that didn't work out, a project that shipped with issues, or a time you underestimated complexity. Explain what you learned and how you'd approach it differently. Show accountability and growth.
Practice Interview
Study Questions
Handling Ambiguity and Driving Decisions
Describe a situation where requirements were unclear or multiple solutions existed. Explain how you gathered information, proposed a direction, got buy-in, and moved forward. Emphasize your decision-making process and communication.
Practice Interview
Study Questions
End-to-End Project Ownership
Demonstrate ownership of medium-sized mobile projects from conception through deployment. Discuss how you drove decisions, removed blockers, collaborated across teams (design, backend, QA), and delivered impact. For mid-level, emphasize technical leadership and how you shaped the project direction.
Practice Interview
Study Questions
Frequently Asked Mobile Developer Interview Questions
A tree stores gains and losses along a decision path. Write an algorithm that determines whether any root-to-leaf path sums to a target value. Some node values are negative, so you cannot rely on the running total only moving in one direction. How would you structure the recursion or backtracking?
Sample Answer
Approach
Use recursion with backtracking. A path sum is the sum of values from the root to a leaf. Because node values can be negative, I would not prune when the running sum gets too large, since later negative values could bring it back down.
def has_path_sum(root, target_sum):
def dfs(node, remaining):
if node is None:
return False
remaining -= node.val
if node.left is None and node.right is None:
return remaining == 0
return dfs(node.left, remaining) or dfs(node.right, remaining)
return dfs(root, target_sum)
Worked example
For the path 5 -> 4 -> 11 and target 20, the recursion checks 20 - 5 = 15, then 15 - 4 = 11, then 11 - 11 = 0, so it returns True.
Why backtracking is enough
Each recursive call gets its own remaining value, so there is no shared state to clean up. The function explores one root-to-leaf path at a time and stops as soon as it finds a match.
Complexity
Time is O(n) in the worst case, and space is O(h) for the recursion stack, where h is tree height.
You have a large in-memory image cache on Android and observe frequent GC pauses and OOMs on lower-end devices. Propose concrete strategies to reduce memory pressure: bitmap pooling (inBitmap), use of hardware bitmaps, downsampling/resizing, limiting memory cache size based on device class, and using disk cache more aggressively. Explain trade-offs of each approach.
Sample Answer
Approach summary
I’d attack memory pressure with a mixture of reuse, smaller footprints, device-aware limits, and heavier disk use. Each technique trades latency, complexity, or compatibility for lower RAM usage.
Bitmap pooling (inBitmap / reuse)
- What: Reuse existing mutable bitmaps via BitmapFactory.Options.inBitmap or Glide’s bitmap pool.
- Benefits: Fewer allocations, fewer GC spikes on heap; reduces churn.
- Trade-offs: Only works when sizes/configs match (or supported on newer APIs with flexible byte reuse). Requires careful lifecycle management to avoid accidental mutation/visual bugs. Slight complexity integrating with libraries and different Android versions.
Hardware bitmaps
- What: Use Bitmap.Config.HARDWARE (API 26+), GPU-backed immutable bitmaps.
- Benefits: Offloads memory from Dalvik/ART heap into GPU/native memory; near-zero GC pressure.
- Trade-offs: Immutable (no inBitmap reuse), can't be read/write on CPU, higher native memory usage can still lead to OOM on devices with limited native memory. Only for API 26+, and may increase upload latency for frequent draw-to-bitmap operations.
Downsampling / resizing
- What: Decode at target display size (inSampleSize, inTargetDensity) or downscale images server-side.
- Benefits: Biggest ROI — reduces bytes per image, lowers both heap and native usage.
- Trade-offs: Lower visual quality if over-downsampled; extra work to choose correct size per density/view; possible CPU cost for runtime scaling, but negligible compared to memory savings.
Limit memory cache by device class
- What: Detect device RAM/class (ActivityManager.getMemoryClass / largeMemory) and throttle cache size (e.g., Glide’s LruResourceCache).
- Benefits: Prevents a one-size-fits-all cache from OOMing low-end devices.
- Trade-offs: Smaller cache = more network/disk IO and potential jank when reloading images. Needs tuning and device profiling.
Use disk cache more aggressively
- What: Keep lower-res thumbnails or evict rarely-used bitmaps to disk cache; prioritize in-memory for visible items only.
- Benefits: Lowers memory footprint; disk reads are slower but avoid OOMs.
- Trade-offs: Increased IO, possible user-visible latency; requires good async loading and progressive placeholders.
Practical combination (example)
- Resize to view size on decode, use inBitmap pool for same-size mutable configs on API <26, prefer hardware bitmaps for static, large images on API >=26, cap memory cache based on memoryClass, and store originals/alternates on disk. Profile with Android Profiler and test on low-end devices.
This balanced strategy minimizes GC and OOM risk while keeping UX acceptable; tuning depends on image patterns and device fleet.
Design a fuzz testing harness for a mobile sync engine to surface correctness bugs across network partitions and concurrent local edits. Define mutation strategies for operations, network fault injection (latency, reorder, drop, partition), invariants to assert (e.g., convergence), and how to reproduce/minimize failing cases for debugging.
Sample Answer
Overview
I would build a deterministic, programmable fuzz harness that drives both the client-side sync engine and a mock server, injecting mutated operations and network faults to exercise concurrent local edits and partitions. The harness runs on CI with device/emulator targets and records full traces for repro.
Mutation strategies
- Operation-level:
- Randomized create/update/delete/text-char-insert/replace/merge for objects and CRDT ops.
- Parameter mutations: timestamp skew, out-of-order sequence numbers, truncated payloads, malformed fields.
- Composite bursts: rapid local edits to same object with small pauses.
- Heuristics:
- Weighted sampling of real production edit traces.
- Guided mutations to target rare code paths (coverage feedback).
Network fault injection
- Latency: variable per-packet/persistent delay distributions (normal, heavy-tailed).
- Reorder: shuffle message queue windows.
- Drop: probabilistic drop, conditional drop (specific message types).
- Partition: isolate client subset for configurable durations then heal with queued delivery.
- Loss patterns: packet-loss bursts, channel-specific (sync vs push).
Invariants to assert
- Convergence: final state across clients and server equivalent (allowing permitted conflict-resolution nondeterminism).
- Causality: no forever-missing causal dependencies.
- Durability: acknowledged writes eventually visible after reconnection.
- No data corruption: schema validation, checksum matches.
- Semantic invariants: app-level constraints (e.g., unique IDs).
Reproduction & Minimization
- Deterministic seeds: seed RNGs and record all seed/state.
- Event logging: append-only trace of operations, network events, timing.
- Replay harness: deterministic scheduler replays events on emulator/device.
- Delta-debugging: binary search over event subsequence to find minimal failing trace.
- Snapshots/checkpoints: capture client DB and server state at key points to fast-forward replays.
Implementation notes
- Use a harness written in Kotlin/Swift or Node test runner with ADB/xcuitest orchestration.
- Expose hooks in sync engine for deterministic clocks and injectable transport layer.
- Integrate with CI, coverage, and issue triage: auto-create bug reports with trace, minimized repro and device/emulator config.
This approach surfaces correctness bugs from timing, partitions, and concurrent edits while providing deterministic, minimized repros for debugging.
Tell me about a time you had to give difficult feedback to a teammate or partner you worked with closely. What made the conversation hard, how did you frame it, and what happened afterward?
Sample Answer
Situation: I worked closely with a partner who was strong technically but often changed direction late, which was creating churn for the rest of the team.
Task: I needed to give difficult feedback without damaging trust.
Action: I chose a private conversation and used the SBI format, which means Situation, Behavior, Impact. I said, "In yesterday's planning meeting, when the scope changed after we had already aligned with design, it created rework and made the team less confident in the plan." I kept the tone factual, then asked what was driving the change. It turned out they were reacting to pressure from another stakeholder and had not surfaced it earlier. We agreed they would flag uncertainty sooner and bring changes through planning instead of in the middle of execution.
Result: The conversation was uncomfortable, but it improved our working relationship because it was specific and fair. Their behavior became more predictable, and the team trusted them more because expectations were clearer.
Compare LiveData, Kotlin Flow and RxJava for representing streams of asynchronous data in Android. Discuss lifecycle-awareness, hot vs cold streams, backpressure handling, integration with ViewModel and UI threading, and when you'd prefer one over the others.
Sample Answer
Overview
LiveData, Kotlin Flow and RxJava each model async streams but target different needs: LiveData is Android-UI-centric, Flow is Kotlin-coroutine-native, RxJava is feature-rich reactive library.
Lifecycle-awareness
- LiveData: built-in lifecycle-aware — observers auto-start/stop with LifecycleOwner (Activity/Fragment).
- Flow: not lifecycle-aware by default; use lifecycleScope and lifecycle.repeatOnLifecycle to collect safely.
- RxJava: not lifecycle-aware; requires manual disposal or use AutoDispose / Lifecycle-aware extensions.
Hot vs Cold
- LiveData: hot — keeps latest value; observers receive current state.
- Flow: by default cold (Flows start on collect). SharedFlow/StateFlow provide hot behavior (StateFlow ~ LiveData).
- RxJava: supports both; Observable/Flowable can be hot or cold depending on source and Subjects.
Backpressure
- LiveData: no backpressure semantics — suitable for UI events/state.
- Flow: has structured backpressure via suspending emissions and operators (buffer, conflate, collectLatest).
- RxJava: Flowable offers backpressure strategies; Observable lacks built-in backpressure.
ViewModel & UI threading
- LiveData: pairs naturally with ViewModel; postValue/setValue handle main thread concerns.
- Flow: use viewModelScope to emit on Dispatchers.IO and collect on Dispatchers.Main; operators like flowOn, withContext manage threads.
- RxJava: use subscribeOn/observeOn; integrate with ViewModel but manage disposables.
When to prefer
- LiveData: simple UI state, minimal setup, lifecycle safety (small apps).
- Flow/StateFlow: coroutine-native, testable, better for complex async pipelines, prefer when using coroutines.
- RxJava: existing Rx-heavy codebase or need advanced operators, multicasting, backpressure control; otherwise Flow is lighter for new Android projects.
Example: for continuous sensor data with sampling and backpressure -> Flow with buffer/conflate or RxJava Flowable; for UI form state -> LiveData or StateFlow.
Implement from-scratch a simple hash table in Python supporting put(key, value), get(key), and delete(key) using open addressing (linear probing). Support resizing when load factor exceeds a threshold. Explain complexity, deletion special cases (tombstones), and how resizing preserves correctness.
Sample Answer
Direct answer
A hash table built with open addressing stores every entry directly in one flat array; on a
collision it probes forward (here, linearly) to the next slot, uses a tombstone marker (not a bare
None) for deleted entries so later probes don't stop early, and resizes once the load factor
crosses a threshold.
Structured elaboration
Three distinct slot states, not two. A naive implementation might track only "empty" and
"occupied," but deletion needs a third state: tombstone (previously occupied, now deleted). A
probe sequence must treat a tombstone as "keep looking, something later might still be the key I
want" while treating true emptiness as "stop, the key definitely isn't here." Confusing these two is
the single most common open-addressing bug: without tombstones, deleting an entry mid-probe-sequence
can make a later, still-present key silently unreachable.
Insertion. Probe from hash(key) % capacity, remembering the first tombstone slot seen (so a
successful insert reuses it rather than always walking to a brand-new empty slot); stop at the key
itself (update in place) or at a true empty slot (place there, using the remembered tombstone slot
if one was seen along the way).
Resizing. The load factor check should count tombstones toward the trigger (not just live
entries), since a table full of tombstones is just as slow to probe as one full of live entries, and
a resize is exactly what clears all tombstones out (it rebuilds the table from scratch with only the
live entries, since insertion during a resize never needs to place a tombstone).
Worked example
_TOMBSTONE = object()
class OpenAddressingHashMap:
def __init__(self, initial_capacity=8):
self._capacity = initial_capacity
self._used = 0
self._keys = [None] * self._capacity
self._values = [None] * self._capacity
def _probe(self, key):
idx = hash(key) % self._capacity
first_tombstone = None
for _ in range(self._capacity):
slot = self._keys[idx]
if slot is None:
return (first_tombstone if first_tombstone is not None else idx), False
if slot is _TOMBSTONE:
if first_tombstone is None:
first_tombstone = idx
elif slot == key:
return idx, True
idx = (idx + 1) % self._capacity
return first_tombstone, False
def put(self, key, value):
if self._used / self._capacity >= 0.7:
self._resize(self._capacity * 2)
idx, found = self._probe(key)
if not found and self._keys[idx] is None:
self._used += 1
self._keys[idx] = key
self._values[idx] = value
def get(self, key):
idx, found = self._probe(key)
return self._values[idx] if found else None
def delete(self, key):
idx, found = self._probe(key)
if not found:
return False
self._keys[idx] = _TOMBSTONE
self._values[idx] = None
return True
def _resize(self, new_capacity):
old_keys, old_values = self._keys, self._values
self._capacity, self._used = new_capacity, 0
self._keys, self._values = [None]*new_capacity, [None]*new_capacity
for k, v in zip(old_keys, old_values):
if k is not None and k is not _TOMBSTONE:
self.put(k, v)
t = OpenAddressingHashMap(initial_capacity=4)
t.put("a", 1); t.put("b", 2); t.put("c", 3)
print("capacity after 3 inserts:", t._capacity)
print(t.get("a"), t.get("b"), t.get("c"))
t.delete("b")
print("get('b') after delete:", t.get("b"))
t.put("d", 4) # reuses b's tombstone slot
print(t.get("d"), t.get("a"), t.get("c"))
for i in range(20):
t.put(f"k{i}", i)
print("capacity after 20 more inserts:", t._capacity)
print("k17 ->", t.get("k17"))
Running this prints: capacity after 3 inserts: 4 (2/4 = 0.5 was below the 0.7 threshold when "c" was
inserted, so no resize fired yet), then 1 2 3 (all three retrievable), then
get('b') after delete: None (confirms deletion), then 4 1 3 ("d" correctly reused "b"'s freed
slot while "a" and "c" remained untouched), then capacity after 20 more inserts: 32 (several
doublings: 4 -> 8 -> 16 -> 32 as load crossed 0.7 repeatedly), then k17 -> 17 (every key survived
all those resizes correctly).
Trade-offs and pitfalls
The two bugs that actually break this in practice: (1) clearing a deleted slot to None instead of
a tombstone, which silently breaks lookups for any key whose probe sequence passed through that slot;
(2) checking load factor using only live entries (ignoring tombstones), which lets a
delete-heavy workload accumulate tombstones until probing degrades toward O(capacity) even though the
live entry count looks small, exactly why the trigger above uses _used (live entries and
tombstones combined), not a live-only count.
Tell me about a time you noticed and fixed a technical problem, bug, or process gap that was outside your assigned scope, without being asked. Walk me through how you discovered it, the concrete steps you took to fix or improve it end to end, who you kept informed, and the measurable outcome.
Sample Answer
Direct answer
Notice it during normal work rather than a special investigation, scope a fix small enough to do without asking, fix the actual root cause rather than the symptom, and tell the team what changed and why so the fix is visible rather than buried in a commit.
Structured elaboration
- Discovery usually happens incidentally: while doing assigned work, you hit something off, a flaky test everyone re-runs, a script everyone has a manual workaround for, that is outside your actual ticket.
- Scope the fix to what you can safely do inside your own access and time without needing approval, because the change is small, contained, and does not touch anyone else's active work.
- Fix the actual cause: spend the extra time to find why it is happening rather than patching the symptom, re-running until it passes, adding a longer timeout, which just delays the same failure.
- Keep people informed: a short note in the team channel or pull-request description saying what was broken and what changed, so teammates do not independently rediscover the same thing later.
- Measurable outcome: something concrete that changed as a result, fewer reruns needed, less time lost per week, even if the number is modest.
Worked example
A shared integration test suite failed intermittently, and the team's habit was to re-run the whole suite until it passed, costing a few extra minutes each time and adding up across a team running it many times a day. Digging in during downtime between tickets uncovered the real cause: two tests shared a test fixture, a piece of setup data, and ran in a nondeterministic order, so one test occasionally read data the other had not finished writing, a race condition, a bug where the outcome depends on timing that is not guaranteed. Isolating the fixture per test so each test owned its own data, the fix went out as a small pull request with the root cause noted in the description, so reviewers understood it was not just a timeout bump. After the fix, the suite's flaky reruns, which had been happening roughly half a dozen times a day across the team before the fix, dropped from that regular daily occurrence to about once every couple of weeks over the following month.
Trade-offs and pitfalls
A common wrong turn is patching the symptom, retry logic, a longer timeout, because it is faster, which hides the same bug until it resurfaces somewhere less convenient. Another is fixing it silently without telling anyone, so the team never learns what was actually wrong and cannot recognize the same pattern next time. Also watch scope: a fix this small does not need permission, but if it had required touching shared infrastructure other teams depended on, the right move would have been to flag it first rather than just changing it.
You're asked to set up a lightweight mentorship structure for a small team. What would you actually put in place, pairing, cadence, shared resources, and how would you keep it low-overhead?
Sample Answer
Direct answer
A lightweight structure needs three ingredients: a small, predictable time commitment (a fixed cadence, not open-ended availability), a place where knowledge accumulates outside people's heads, and two or three signals you actually look at instead of a heavy program. Keep it low-overhead by reusing rituals the team already has, like code review, rather than inventing new meetings.
Structured elaboration: the components
| Component | What you set up | Why it stays lightweight |
|---|---|---|
| Pairing and cadence | One small recurring block per pair (for example, a single weekly slot), rotating pairs on a short cycle so everyone gets exposure | Bounded time commitment, predictable, no ad hoc scheduling |
| Shared knowledge base | One folder or doc space with a couple of templates (session notes, a troubleshooting or FAQ page), edited through the team's existing review flow | No new tool to learn or separately maintain |
| Kickoff, not a training program | One short session covering what makes a good mentoring conversation and a few question prompts | One-time cost, not ongoing overhead |
| Signals you track | Two or three only, checked occasionally: are sessions actually happening, is the knowledge base getting used, do people feel less stuck | Avoids the program itself becoming the overhead |
Worked example
For a four-person team, a three-week rotation covers every unique pair exactly once: week one pairs A-B and C-D, week two pairs A-C and B-D, week three pairs A-D and B-C, then the cycle repeats. If each pairing block is 45 minutes, the weekly time cost per person is one session, 45 minutes, or 0.75 hours a week, plus roughly 15 to 20 minutes a month writing up notes. That puts the total time cost under an hour a week per person, small enough that it does not meaningfully compete with deliverable time, and it is a claim that can be checked against the actual calendar rather than taken on faith.
Trade-offs & pitfalls
The temptation is always to add more: formal training modules, a matching algorithm, quarterly surveys. A program with more infrastructure than the team has bandwidth to sustain decays within a few weeks. The senior distinction here is that a junior design assumes more structure is always better, while a senior deliberately underbuilds and only adds structure once a specific signal shows it is needed. A second pitfall is shared docs going stale because nobody owns freshness; assign light rotating ownership (whoever paired last updates the relevant page) rather than creating a separate docs-owner role, which is more overhead, not less. A third pitfall is picking the wrong rotation speed: too fast and no pair builds enough context to go deep; too slow and some people never get exposure to others. Match the cycle length to team size so everyone pairs with everyone within one cycle, as in the rotation above.
Implement an LRU cache in Swift with O(1) get(key:) and put(key:value:) operations. Describe the data structures you will use and provide Swift code showing the core logic (dictionary + doubly-linked list). Discuss thread-safety options for use from multiple threads and how to account for item costs such as image byte size.
Sample Answer
Approach (brief)
Use a Dictionary<Key, Node> for O(1) lookups and a doubly-linked list to maintain recency (head = most-recent, tail = least-recent). Each Node stores key, value, cost (e.g., image byte size). Track totalCost and evict from tail until under capacity.
Swift implementation (core logic)
final class LRUCache<Key: Hashable, Value> {
private class Node {
let key: Key
var value: Value
var cost: Int
var prev, next: Node?
init(key: Key, value: Value, cost: Int) {
self.key = key; self.value = value; self.cost = cost
}
}
private var map = [Key: Node]()
private var head: Node?
private var tail: Node?
private var totalCost = 0
private let capacityCost: Int
init(capacityCost: Int) { self.capacityCost = capacityCost }
private func moveToHead(_ node: Node) {
guard head !== node else { return }
// unlink
node.prev?.next = node.next
node.next?.prev = node.prev
if tail === node { tail = node.prev }
// insert at head
node.next = head
node.prev = nil
head?.prev = node
head = node
if tail == nil { tail = head }
}
private func insertAtHead(_ node: Node) {
node.next = head; node.prev = nil
head?.prev = node; head = node
if tail == nil { tail = head }
}
private func removeTail() -> Node? {
guard let node = tail else { return nil }
map[node.key] = nil
totalCost -= node.cost
if head === tail {
head = nil; tail = nil
} else {
tail = node.prev
tail?.next = nil
}
return node
}
func get(_ key: Key) -> Value? {
guard let node = map[key] else { return nil }
moveToHead(node)
return node.value
}
func put(_ key: Key, value: Value, cost: Int = 1) {
if let node = map[key] {
totalCost -= node.cost
node.value = value
node.cost = cost
totalCost += cost
moveToHead(node)
} else {
let node = Node(key: key, value: value, cost: cost)
map[key] = node
insertAtHead(node)
totalCost += cost
}
// Evict while over capacity
while totalCost > capacityCost {
_ = removeTail()
}
}
}
Complexity
- get / put: O(1) time; space O(n).
Thread-safety options
- For simple use, wrap public methods with a serial DispatchQueue (DispatchQueue(label:..., attributes: .concurrent) with .barrier for mutations) or use os_unfair_lock/NSLock for low-latency locking.
- Example: use private let queue = DispatchQueue(label: "lru.cache", attributes: .concurrent); read via queue.sync, writes via queue.async(flags: .barrier) to avoid races and keep reads concurrent.
Accounting for item costs (images)
- Provide cost parameter (bytes) per put; maintain totalCost; evict least-recent until totalCost <= capacityCost. For images, use image.pngData()?.count or image.costForCaching API; consider approximate costs to avoid expensive size computations.
Edge cases & notes
- Handle large single-item cost > capacity (optionally reject or allow by evicting all others).
- Consider weak references if values should be released when not strongly held elsewhere (NSCache alternative).
At the end of a meeting, how do you confirm next steps out loud in the room, and then again in a short written follow-up, so nothing gets lost between the conversation and the written record?
Sample Answer
Direct answer
State the decision and the immediate next steps out loud before the meeting ends, then send a short written follow-up within the hour that restates the same thing, so there's both an in-the-room confirmation and a durable record that matches it.
Structured elaboration
- Confirm verbally before people leave the room (or call). In the last minute or two, say "so to confirm, we've decided X, and the next steps are Y owned by Z by Thursday, does that match everyone's understanding?" This catches a misalignment while everyone who can correct it is still present.
- Watch for silence versus agreement. Nobody objecting isn't the same as everyone actively agreeing; a direct question ("does that match?") is more reliable than just pausing and moving on if no one immediately speaks up.
- Send the written follow-up promptly, ideally within the hour, restating the same decision and action items. The verbal confirmation and the written one should say the same thing; if they don't, that's usually a sign the verbal confirmation was rushed or unclear.
- Keep the written version short and scannable, matching the same content as the verbal confirmation rather than adding new information the room didn't actually agree to.
- Flag anything genuinely still unresolved, in both the verbal check and the written follow-up, rather than letting an unresolved point quietly look settled just because the meeting ended.
Worked example
Verbal, at the end of the meeting: "So to confirm: we're going with the phased rollout, Sam owns the migration plan by next Friday, and we're holding off on the customer announcement until that's done. Does that match what everyone heard?"
Written follow-up sent the same hour: "Recap from today: decided on the phased rollout. Sam: migration plan due next Friday. Customer announcement is on hold until the migration plan is ready. Shout if this doesn't match what you remember."
The two versions state the identical decision and owner, and the written version explicitly invites correction rather than assuming silence means agreement.
Trade-offs and pitfalls
- Skipping the verbal confirmation and only sending a written recap later means any misunderstanding surfaces after people have already left and possibly acted on their own interpretation.
- Skipping the written follow-up and only confirming verbally means anyone who wasn't in the room, or who forgets, has no record to check against.
- A written recap that silently adds detail beyond what was verbally confirmed can create a new source of disagreement; keep the two consistent, and if you realize something needs adding, flag it explicitly as new rather than folding it in unannounced.
Want to create your own tailored preparation guide using our deep research?
Get Started for FreeInterview-Ready Courses
Visual-first, interactive, structured learning paths
Browse Mobile Developer jobs
AI-enriched listings across hundreds of company career pages
Explore Jobs