🎤
A full mock coding interview, narrated
This is exactly how a real 45-minute software-engineering interview flows — an open-ended OOP design question, then a dynamic-programming algorithm, then behavioral questions and your own. Read both sides of the conversation, with coaching notes (💡) on why each move works. Free to read.
⏱️ ~45 minutes🧩 OOP design⚡ Dynamic programming🗣️ Behavioral🆓 Free
Phase 0 — Intro & warm-up First 2–3 minutes: build rapport and confirm the tooling.
🧑💼
Interviewer
Welcome! We'll use a shared editor today. Can you run a quick
print("hello world") so we know it works?
👩💻
Candidate
Sure — that prints fine. I'm comfortable. We'll spend about 20 minutes on a design problem and 20 on an algorithm?
🧑💼
Interviewer
Exactly. Let's start with design.
💡 Why this works: Confirm the tools, relax, and restate the structure so you know how to budget your time. Calm beats clever in the first minute.
Phase 1 — OOP design (≈20 min) "Design an online cloud reading app — like a Kindle, but for indie short stories."
🧑💼
Interviewer
Design the core code for an online reading app. Requirements: a user has a
library of books they can add to / remove from; they can set one book
active; the app
remembers where they left off in each book; and it
shows one page at a time of the active book. It's open-ended — focus on structure.
👩💻
Candidate
Let me make sure I understand. A user manages a collection of books, marks one as currently-reading, and the app shows a single page and remembers their spot per book. How is a book represented — just text, or pages?
🧑💼
Interviewer
Up to you. The important parts are the book's title and its text content.
💡 Why this works: Clarify before coding. Restating the problem in your own words and asking one or two pointed questions shows you understand scope — and stops you building the wrong thing.
👩💻
Candidate
I see two clear responsibilities, so two classes: a
Book (its title, its pages, and the last page this reader saw) and a
Library (the collection of books plus which one is active). I'll store pages as a
list of strings — ordered, simple to index — and
last_page as an integer index (watch the off-by-one: index 0 is page 1). The library will be a
dict of id → Book for O(1) lookup, plus an
active_id. I'll use a generated id, not the title, since two books can share a title.
🧑💼
Interviewer
Good — assume titles aren't unique, so the id idea is sensible. Let's see it in code.
class Book:
"""One story. content = list of page-strings; remembers the reader's spot."""
def __init__(self, book_id, title, content):
self.id = book_id
self.title = title
self.content = content # list[str], one entry per page
self.last_page = 0 # index into content
def display_page(self):
return self.content[self.last_page]
def turn_page(self):
if self.last_page < len(self.content) - 1:
self.last_page += 1
return self.display_page()
class Library:
"""A reader's collection of books + which one is open right now."""
def __init__(self):
self.collection = {} # id -> Book (dict = O(1) lookup)
self.active_id = None
self._next_id = 0 # simple unique-id generator
def add_book(self, title, content):
book = Book(self._next_id, title, content)
self.collection[book.id] = book
self._next_id += 1
return book.id
def remove_book(self, book_id):
self.collection.pop(book_id, None)
if self.active_id == book_id:
self.active_id = None
def set_active(self, book_id):
if book_id in self.collection:
self.active_id = book_id
def display_page(self):
if self.active_id is None: return None
return self.collection[self.active_id].display_page()
def turn_page(self):
if self.active_id is None: return None
return self.collection[self.active_id].turn_page()
💡 Why this works: Separation of concerns. Page logic lives in Book; the library only knows ids. If the storage format changes later, only Book changes — the Library API (display_page, turn_page) stays the same. Narrating why a dict (O(1) lookup) and why an id (title collisions) is exactly the reasoning interviewers grade.
🧑💼
Interviewer
Follow-up: an older reader wants a bigger font, which changes how much text fits on a page. How would you adapt this — no need to code it, just talk it through.
👩💻
Candidate
Pages can't be fixed anymore. I'd store the content as
one long string and add a
font_size, then compute
chars_per_page from it. Page
n becomes
content[n*cpp : (n+1)*cpp]. Because
display_page/
turn_page are already separate methods, only their internals change — nothing outside
Book breaks. I'd even pull that into a small
Display class so the book stays about
data and the display handles
layout.
🧑💼
Interviewer
And if many users share the same books — everyone reads the same Harry Potter?
👩💻
Candidate
Then the book's
content is shared but each reader's
state (active book, last page) is personal. I'd store each book
once — e.g. a SQL table keyed by a global id — and keep per-user reading state separately (a
UserState, or a row per user-per-book). When adding, if that title+content already exists, reuse its id instead of duplicating.
💡 Why this works: The follow-ups test whether your design flexes. Because responsibilities were separated, each change is local. Naming the seam ("Display class", "UserState", "store the book once") shows senior-level thinking even without writing code.
Phase 2 — Algorithm (≈20 min) "Detect the two stories most likely to be plagiarized — the longest shared subsequence of text."
🧑💼
Interviewer
Independent authors upload short stories, so we want to flag plagiarism. Define the two books most likely copied as the pair sharing the
longest common subsequence of characters — same order, but gaps allowed. Design an algorithm; we care how many characters that shared section is.
👩💻
Candidate
Quick check: a
subsequence keeps order but can skip characters — so "lean" is a subsequence of "learning"? Right. The brute force would be enumerating every subsequence of both and comparing — that's exponential, way too slow. Even a pointer-by-pointer compare from every start is O(n·m) per pair at best and misses the optimal alignment. The overlapping work screams
dynamic programming.
🧑💼
Interviewer
Agreed it's DP. Define the sub-problem and a one-line recurrence.
👩💻
Candidate
Let
lcs(i, j) = the longest common subsequence of
a[i:] and
b[j:].
Base case: if
i or
j runs off its string, there's nothing left → 0.
Recurrence: if the current characters match, take both and add one; otherwise the best of skipping one side or the other.
from functools import lru_cache
def longest_common_subsequence(a, b):
@lru_cache(maxsize=None) # memoize overlapping sub-problems
def lcs(i, j):
if i == len(a) or j == len(b):
return 0 # base: ran off a string
if a[i] == b[j]:
return 1 + lcs(i + 1, j + 1) # match -> advance both
return max(lcs(i + 1, j), lcs(i, j + 1)) # else skip one side
return lcs(0, 0)
👩💻
Candidate
There are
n·m distinct
(i, j) states and each is O(1) thanks to memoization, so
O(n·m) time and space. To find the worst pair in a library, run it on every unique pair of books and keep the max.
def most_likely_plagiarism(books): # books: list[(title, text)]
best = (0, None, None)
for i in range(len(books)):
for j in range(i + 1, len(books)):
score = longest_common_subsequence(books[i][1], books[j][1])
if score > best[0]:
best = (score, books[i][0], books[j][0])
return best # (shared_chars, titleA, titleB)
💡 Why this works: The winning sequence is always: (1) state the brute force and its complexity, (2) spot the overlapping sub-problems, (3) define the sub-problem + one-line recurrence + base case, (4) memoize, (5) give the final O(n·m). Talking through that arc matters more than the final code.
Phase 3 — Behavioral (≈10 min) Answer in STAR: Situation → Task → Action → Result.
🧑💼
Interviewer
Tell me about a difficult team project.
👩💻
Candidate
Situation/Task: On a 4-person capstone we fell behind because two of us disagreed on the database design.
Action: I set up a 30-minute call, had each person whiteboard their approach, and we listed the trade-offs in a shared doc; we chose the simpler schema and split the rest into clear tickets.
Result: We shipped on time and the grader called out our clean data model. The lesson: surfacing a disagreement early — instead of avoiding it — saves the whole team time.
💡 Why this works: STAR keeps you concise and concrete, and you end on a measurable result + a lesson. Pick stories about your actions, stay blameless, and keep it ~90 seconds.
Phase 4 — Your questions for them Always have 2–3 ready; asking none signals low interest.
👩💻
Candidate
"What does success look like in the first 90 days?" · "How does the team handle code review and testing?" · "What's the biggest technical challenge the team is facing right now?"
🧑💼
Interviewer
Great questions — let's wrap there.
📋 What an interviewer scores afterward: Did you clarify before coding? Was the structure clean and did you separate concerns? Did you justify data-structure choices out loud? For the algorithm: brute force → DP → recurrence → memoize → complexity. For behavioral: specific, concise, result-driven. Common "make it even better" notes: break out more classes (a Display class), make ids more robust, and use subclassing for shared-vs-personal state.
← Practice the problems (190+ with solutions) · All cheat sheets