Phase 9: Career, Portfolio & Interview Prep · 45 min · Case interview frameworks · Python
The Problem
"Our company runs a mid-size e-commerce platform. We're considering adding an AI shopping assistant that helps users find products through conversation instead of search and filters. Walk me through how you'd approach this." That's the entire prompt — no follow-up structure, no sub-questions, just 35 minutes and a blank whiteboard. A candidate who treats this like the shorter product-sense prompts from Lesson 2 and rushes through a five-minute answer, then sits there expecting more questions, misreads the format: a case interview is long on purpose, because it's testing whether you can sustain structured thinking across a full extended conversation with an interviewer who will redirect you, add constraints mid-stream, and push back on your assumptions — the actual texture of a real strategy conversation with a VP, not a scripted Q&A.
The skill this round tests is different in kind from the shorter product-sense round, not just longer: can you build a structure, use it as scaffolding rather than a script, and adapt when the interviewer changes the ground under you (a new constraint, a follow-up that pokes a hole in your assumption, a request to go deeper on one part and skip another). This is also the round most likely to blend product sense, technical fluency, and prioritization into one continuous scenario, since 35 minutes is enough room for all three.
A structure for the long case
The five-step framework from Lesson 2 still applies, but at case-interview scale each step gets real depth and — critically — checkpoints where you should expect the interviewer to jump in:
- Clarify and scope (3-5 minutes) — for a prompt this open, spend real time here: what's the business goal (revenue, retention, reduced support load), what's the current search/filter experience's actual pain point, is there a specific user segment or product category this should start with. A case prompt this broad is often deliberately underspecified to see if you'll ask, not just because the interviewer forgot detail.
- Frame 2-3 possible directions, then commit to one (5-7 minutes) — for the shopping-assistant prompt: is this "AI-only search replacement," "AI as one path alongside existing search," or "AI as a post-search refinement layer"? State the options, state which you'd pursue first and why, and be ready to defend the choice when the interviewer asks "why not the others."
- Design the chosen direction in depth (10-15 minutes) — this is where the case interview differs most from the shorter product-sense round: the interviewer will likely interject with constraints ("what if the catalog has 2 million SKUs and inventory changes hourly?", "what if this needs to work for a user who's never used the assistant before?") and you need to adapt your design live, not restart from scratch.
- Address the technical/eval layer (5-8 minutes) — how would you know the assistant is actually helping vs. just being a novelty (Phase 5's material, live): a metric like conversion rate or time-to-purchase for assistant sessions vs. traditional search, a golden set of realistic shopping queries to eval before launch, a plan for what happens when the assistant recommends something out of stock or misunderstands intent.
- Prioritize a rollout and name risks (5-7 minutes) — a staged rollout (Phase 8's crawl/walk/run instinct) and at least one Phase 7 responsible-AI consideration specific to this feature (a shopping assistant that can be manipulated into recommending a specific product inappropriately, or one that makes a purchase-adjacent recommendation a vulnerable user shouldn't be nudged toward).
Treat an interviewer's interruption as a gift, not a derailment
When an interviewer jumps in mid-case with "what if inventory changes hourly?", the instinct under pressure is to feel thrown off. Reframe it: they're handing you a real constraint that a live product conversation would actually surface, and reacting to it well — "good point, that changes my design here specifically: I'd..." — is a stronger signal than a smooth uninterrupted monologue would have been. Interviewers who never interject are often being polite, not impressed; interjections usually mean they're engaged enough to stress-test your thinking.
A worked case walkthrough (abbreviated)
Using the shopping-assistant prompt: after clarifying that the goal is increasing conversion for users who abandon after multiple failed searches (a real, specific pain point, not "AI is trendy"), a strong candidate frames three directions — full search replacement, parallel option, post-search refinement — and commits to the parallel option first ("lowest risk, tests demand without breaking an experience that already converts fine for users who know what they want"). When the interviewer interjects with the 2-million-SKU, hourly-inventory constraint, a strong answer adapts specifically: "that means I can't rely on the assistant's training data alone for product facts — this needs to be a retrieval-augmented setup pulling live inventory, not a model that might recommend something out of stock, which changes my architecture recommendation from Phase 1's build-vs-buy framing toward something with a live catalog lookup." For the eval layer: "I'd build a golden set of 50-100 realistic shopping queries across categories, track conversion and time-to-purchase for assistant sessions against a control group, and specifically eval for out-of-stock recommendation rate as a guardrail metric, not just conversion." For rollout: "shadow mode first to catch bad recommendations before they're user-facing, then a small opt-in cohort, watching the guardrail metric before wider rollout" — and for risk: "the main responsible-AI risk I'd flag is the assistant being steered by a user into recommending products in a way that looks like an endorsement it shouldn't be giving — I'd want a guardrail decision here similar to what we covered for prompt injection and misuse."
Fifteen minutes into a case interview, after the candidate commits to a specific direction, the interviewer says: "Interesting — but what if I told you user research shows most users actually prefer using filters and don't trust conversational AI for purchases?" What is the strongest way to respond?
This is the exact scenario the lesson's callout addresses: an interjection with new information is a chance to demonstrate real-time adaptation, not a signal to defend a position rigidly or collapse entirely. The strongest response takes the new data seriously, asks a clarifying question to understand its scope (does this apply to all users or a specific segment/use case?), and shows how it would specifically change the recommendation — for instance, narrowing rollout to a segment or use case where the trust concern is less costly — rather than either stubbornly ignoring the pushback or abandoning the whole direction without adapting it.
A candidate spends 20 of their 35 minutes on the "clarify and scope" step, asking increasingly detailed questions, and only reaches the design step with 8 minutes left. What does this reveal, based on the case-interview structure in this lesson?
Case-scale steps have real time targets for a reason: clarify/scope is meant to take 3-5 minutes, enough to establish the real goal and a key constraint, not to exhaustively map every possible detail. Spending more than half the total time there means the design, eval, and prioritization steps — the parts of the case that most reveal product judgment and technical fluency — get compressed or skipped, which is a worse outcome than a slightly less thorough scoping phase. Time management across the five steps is itself part of what's being evaluated, the same instinct tested in the shorter product-sense round.
What separates a strong case answer from a competent one
A competent answer covers the five steps in order and reaches a defensible recommendation. A strong answer does that and demonstrates real product judgment in the specific choices made along the way: naming the constraint that would actually change the recommendation (not a generic list of "risks to consider"), tying every design choice back to the specific goal established in step 1 rather than drifting into a generic feature description, and treating interviewer pushback as new information to incorporate rather than an attack to survive. The gap between competent and strong is rarely about knowing more frameworks — it's about applying the frameworks to the actual specifics of the prompt in front of you, live.
Build
Build it
The code artifact lesson_09_04_case_study_walkthrough.py implements a case-interview structure tracker and adaptation scorer:
CaseStep — one of the five case-scale steps with time allocation and contentInterjection — a mid-case interviewer redirect or constraint, and how the candidate adaptedCaseWalkthrough — the full case tracked across steps and interjectionsscore_adaptation() — checks whether interjections were incorporated (not ignored or over-reacted to)render_case_review() — a markdown case review report- A worked example: the shopping-assistant case from above, including the inventory-constraint and user-research interjections
"""
lesson_09_04_case_study_walkthrough.py
A case-interview structure tracker and adaptation scorer. Extends the
five-step product-sense framework to case-interview scale, tracks
mid-case interviewer interjections (new constraints or pushback) and
whether the candidate incorporated them, and produces a case review
report.
Reference: IGotAnOffer (https://igotanoffer.com/en/advice/ai-product-manager-interview).
Usage:
python lesson_09_04_case_study_walkthrough.py
No external dependencies, no network calls, no API keys required.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
class CaseStepName(Enum):
CLARIFY_SCOPE = "Clarify and scope"
FRAME_DIRECTIONS = "Frame directions, commit to one"
DESIGN_IN_DEPTH = "Design the chosen direction in depth"
EVAL_LAYER = "Address the technical/eval layer"
PRIORITIZE_RISKS = "Prioritize rollout and name risks"
@dataclass
class CaseStep:
name: CaseStepName
content: str
minutes_spent: float
@dataclass
class Interjection:
at_step: CaseStepName
interviewer_said: str
candidate_response: str
incorporated_new_info: bool # did the response actually change based on the interjection
@dataclass
class CaseWalkthrough:
prompt: str
steps: list[CaseStep] = field(default_factory=list)
interjections: list[Interjection] = field(default_factory=list)
def total_minutes(self) -> float:
return sum(s.minutes_spent for s in self.steps)
def score_adaptation(walkthrough: CaseWalkthrough) -> tuple[int, list[str]]:
"""Scores out of 5: structure coverage plus how well interjections were handled."""
score = 5
issues: list[str] = []
covered = {s.name for s in walkthrough.steps}
missing = [s for s in CaseStepName if s not in covered]
if missing:
score -= len(missing)
issues.append(f"Missing step(s): {', '.join(s.value for s in missing)}")
if not walkthrough.interjections:
issues.append("No interjections recorded — a real case interview almost always includes at least one; practice with a partner who will push back")
else:
ignored = [i for i in walkthrough.interjections if not i.incorporated_new_info]
if ignored:
score -= len(ignored)
for i in ignored:
issues.append(
f"Interjection at {i.at_step.value} was not incorporated into the response: \"{i.interviewer_said}\""
)
return max(score, 0), issues
def render_case_review(walkthrough: CaseWalkthrough) -> str:
score, issues = score_adaptation(walkthrough)
lines = [
"# Case Study Walkthrough Review",
"",
f"**Prompt:** {walkthrough.prompt}",
f"**Total time:** {walkthrough.total_minutes():.1f} minutes",
"",
"## Steps",
"",
]
for s in walkthrough.steps:
lines.append(f"- **{s.name.value}** ({s.minutes_spent:.1f} min): {s.content}")
lines += ["", "## Interjections and adaptation", ""]
if walkthrough.interjections:
for i in walkthrough.interjections:
status = "Incorporated" if i.incorporated_new_info else "NOT incorporated"
lines.append(f"- At {i.at_step.value}: interviewer said \"{i.interviewer_said}\" — {status}")
lines.append(f" Response: {i.candidate_response}")
else:
lines.append("None recorded.")
lines += ["", f"## Score: {score}/5", ""]
if issues:
lines.append("Issues to address:")
lines += [f"- {i}" for i in issues]
else:
lines.append("Strong structural coverage and adaptation — focus next practice on depth of content.")
return "\n".join(lines)
# ── Worked example ───────────────────────────────────────────────────────────
# The shopping-assistant case from the lesson's worked walkthrough.
def build_example() -> CaseWalkthrough:
walkthrough = CaseWalkthrough(
prompt="Our e-commerce platform is considering an AI shopping assistant for conversational product discovery. Walk me through how you'd approach this.",
)
walkthrough.steps = [
CaseStep(CaseStepName.CLARIFY_SCOPE, "Clarified goal: increase conversion for users who abandon after multiple failed searches", 4.0),
CaseStep(CaseStepName.FRAME_DIRECTIONS, "Framed 3 directions (full replacement, parallel option, post-search refinement); committed to parallel option as lowest-risk demand test", 6.0),
CaseStep(CaseStepName.DESIGN_IN_DEPTH, "Designed the parallel-assistant flow, then adapted to a retrieval-augmented architecture pulling live inventory after the SKU/inventory interjection", 13.0),
CaseStep(CaseStepName.EVAL_LAYER, "Proposed a 50-100 query golden set, conversion/time-to-purchase vs control, out-of-stock recommendation rate as a guardrail metric", 6.0),
CaseStep(CaseStepName.PRIORITIZE_RISKS, "Shadow mode then opt-in cohort rollout; flagged manipulation-into-inappropriate-recommendation as the key responsible-AI risk", 6.0),
]
walkthrough.interjections = [
Interjection(
at_step=CaseStepName.DESIGN_IN_DEPTH,
interviewer_said="What if the catalog has 2 million SKUs and inventory changes hourly?",
candidate_response="Shifted the architecture recommendation to a retrieval-augmented setup pulling live inventory, rather than relying on the model's training data for product facts, to avoid recommending out-of-stock items.",
incorporated_new_info=True,
),
Interjection(
at_step=CaseStepName.FRAME_DIRECTIONS,
interviewer_said="What if user research shows most users prefer filters and don't trust conversational AI for purchases?",
candidate_response="Asked whether this held across all segments; proposed narrowing initial rollout to a segment/use case where the trust cost is lower rather than abandoning the direction outright.",
incorporated_new_info=True,
),
]
return walkthrough
if __name__ == "__main__":
walkthrough = build_example()
print(render_case_review(walkthrough))
print()
print(
"Takeaway: both interjections changed something concrete about the "
"design (architecture choice, rollout scope) rather than being "
"acknowledged and then ignored — that's what a 5/5 adaptation score "
"actually requires."
)