In a live round you are given existing code to review and then asked to add a feature to it. How do you review it quickly and what do you fix first?
Predict first
Which version of the screen is freed when its scope ends?
final class Screen {
var onTap: (() -> Void)?
var taps = 0
func configureBad() { onTap = { self.taps += 1 } } // the screen owns the closure; the closure owns the screen
func configureGood() { onTap = { [weak self] in self?.taps += 1 } }
deinit { print("Screen freed") }
}
do {
let s = Screen(); s.configureBad(); s.onTap?()
print("bad version taps:", s.taps)
}
print("after bad scope")
do {
let s = Screen(); s.configureGood(); s.onTap?()
print("good version taps:", s.taps)
}
print("after good scope")
- bad version taps: 1 / after bad scope / good version taps: 1 / Screen freed / after good scope
- Screen freed appears after both scopes
- Screen freed appears after the bad scope only
Mental model. A building inspector looks for the leaks and the loose wiring first and reports in order of danger; they do not repaint the walls before checking the roof.
Reported for Uber and Microsoft (anonymous, unverified). The skill is reading before writing. Spend the first minutes asking what the code does and what you are meant to add, then skim from the entry point down (who creates what, who owns what), and name issues in priority order rather than listing everything: crashes and data loss (force unwraps, unchecked indices, ignored errors), threading (UI updates off the main actor, shared mutable state), memory (retain cycles, strong delegate references, unbounded caches), then structure and naming. Say what you would change and what you will leave for later, because the aim is to add the feature without a rewrite.
The classic find is a retain cycle in a stored closure: the check shows a screen that keeps onTap and a closure that captures self strongly. The bad version never prints Screen freed, the [weak self] version does. Make small, safe edits: fix the defect that would hurt the feature, add the feature with the smallest change that fits the existing structure, and add a test or a quick manual check for it. Keep talking: state assumptions, ask before deleting anything, and keep the code compiling after every change. Do not start renaming and reformatting everything, which burns the time and hides your real change.
The strong capture keeps the screen alive forever, so only the [weak self] version runs deinit.
Common traps
- Rewriting the code in your own style. The task is to extend it safely, and a rewrite risks breaking behaviour you cannot see.
- Listing twenty nitpicks with no priority. Say which two matter for this change.
They might follow up with
- When is it right to refactor first? When the feature cannot be added cleanly without it, and you can do it in a tiny, tested step.
- How do you find a retain cycle in a real app? The Memory Graph Debugger, or a
deinitlog in the suspect classes.
Related questions
- 29.3.1 You get 60 to 90 minutes to build a small app live (a photo search, a movie list from JSON, a recipe app, a…
- 29.3.2 Machine-coding round: build a generic networking layer in about 45 minutes. What do you build, and how do you…
- 29.2.7 Given function entry and exit events with timestamps, print the time spent in each function when it exits.…
← Machine-coding round: build a generic networking layer in about 45…