🎤

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)
🧑‍💼
Interviewer
Runtime?
👩‍💻
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