Google Code Comprehension Interview: A Practical Tutorial

By interviewDB Editorial Team

Published

Quick summary

Summarize this blog with AI

In a code comprehension interview, your job is to understand an unfamiliar program, explain why it behaves as it does, and make a change you can defend. An AI assistant can help, but you still need to decide whether its explanation and proposed code are correct.

This tutorial gives you a repeatable workflow, an original Python debugging exercise, examples of useful AI prompts, and a seven-day practice plan. You will practice tracing data, proving a bug with a small input, reviewing an incomplete AI suggestion, and testing a focused repair.

Prerequisites: basic Python functions, lists, dictionaries, and assertions. Allow about 30 minutes for the worked exercise. The method also applies to other languages.

What is confirmed about Google's interview pilot?

Business Insider reported on May 7, 2026 that Google planned a limited US pilot for junior and mid-level engineering roles, using Gemini for code comprehension tasks. Google confirmed the plans. Candidates would read, debug, and optimize existing code, with prompting and output validation assessed. AI use was planned for the second half of 2026. The report does not establish a fixed code length, duration, or editor. Read the original reporting.

Confirm the rules for your own interview. Google's general virtual-interview guide still prohibits AI assistance. Use an assistant only when your specific round explicitly permits it, and follow the provided tool and confidentiality rules. Google's candidate guide.

The exercises, timing, and self-assessment below are our preparation recommendations, not Google's official questions or scoring rubric. A 200–500-line practice project and a 60-minute mock are useful training choices; they are not verified specifications for every interview. Continue preparing algorithms and data structures for the rounds in your confirmed interview schedule.

1. Map the relevant execution path

Start with the task description, existing tests, and the entry point that handles the reported behavior. Follow one representative input through the functions it reaches. Identify what each stage receives, returns, and changes.

Input → validation → filtering → ordering → pagination → output

Look for side effects: a function may change its input, update shared state, write to a database, or call another service. Check where errors are raised, handled, or silently discarded.

Explain your current understanding in a few sentences:

This function returns a page of open tickets for one team. I'm checking whether pagination applies before or after filtering, and whether the function changes the caller's list.

This gives the interviewer something concrete to confirm or correct. You do not need to understand every helper before investigating the relevant path. Read more when the evidence points elsewhere.

2. Define correct behavior before changing code

A suspicious line becomes a confirmed bug when you can show that it violates a requirement. Write down the rules that a correct implementation must satisfy. For the ticket-listing exercise, the contract is:

  • Return only open tickets belonging to the requested team.
  • Order results by creation time, newest first.
  • Preserve original input order when creation times are equal.
  • Apply offset and limit to the matching results.
  • Leave the caller's list and ticket fields unchanged.
  • Reject negative offsets and nonpositive limits with ValueError.

For this exercise, assume the required ticket fields exist, creation times are comparable integers, and offset and limit are integers. Those are explicit input assumptions, not a complete validation policy for a public API. Returning independent copies of ticket dictionaries is not required.

In a real interview, clarify material ambiguity before treating your preferred behavior as the specification. For example, a zero limit might intentionally mean an empty page in another API.

3. Reproduce the bug with a concrete input

This is an original teaching exercise, not an official Google interview question. It is deliberately small so you can inspect every operation before applying the method to a larger codebase.

Save the following as tickets.py. Predict its output before running it with python3 tickets.py.

def list_open_tickets(tickets, team, offset=0, limit=20):
    tickets.sort(key=lambda t: t["created_at"], reverse=True)
    page = tickets[offset:offset + limit]
    return [
        t for t in page
        if t["team"] == team and t["status"] == "open"
    ]


def sample_tickets():
    return [
        {"id": "A", "team": "alpha", "status": "open", "created_at": 10},
        {"id": "B", "team": "beta", "status": "open", "created_at": 30},
        {"id": "C", "team": "alpha", "status": "closed", "created_at": 20},
        {"id": "D", "team": "alpha", "status": "open", "created_at": 5},
    ]


if __name__ == "__main__":
    tickets = sample_tickets()
    page = list_open_tickets(tickets, "alpha", limit=1)
    print("Page:", [t["id"] for t in page])
    print("Input after call:", [t["id"] for t in tickets])

The expected first page contains ticket A. The expected input order remains A, B, C, D. The actual output is:

Page: []
Input after call: ['B', 'C', 'A', 'D']

Trace the failure rather than jumping directly to a rewrite:

  1. Sorting produces B, C, A, D.
  2. Taking the first item produces B.
  3. Filtering for team alpha removes B.
  4. The function returns an empty page, although matching tickets exist.

The root cause is pagination before filtering: unrelated tickets consume positions in the page. A second defect is input mutation. Python's list.sort() changes a list in place, while sorted() creates a new list. Python's sorting documentation.

You can shrink the reproduction to tickets A and B with limit=1; it still exposes both defects. Reducing the input helps you distinguish the cause from incidental data.

The first page is empty because we select from all tickets before filtering. I also confirmed that sorting changes the input order. I'll address those separately and test both.

4. Make the smallest complete repair

Replace only list_open_tickets in tickets.py with this implementation. Keep the fixture and runnable example.

def list_open_tickets(tickets, team, offset=0, limit=20):
    if offset < 0 or limit <= 0:
        raise ValueError("offset must be >= 0 and limit must be > 0")

    matching = [
        t for t in tickets
        if t["team"] == team and t["status"] == "open"
    ]
    matching.sort(key=lambda t: t["created_at"], reverse=True)
    return matching[offset:offset + limit]

The function now filters, sorts the matching tickets, and selects the page. Sorting in place is safe here because matching is a new list owned by the function. It does not reorder the caller's list.

Python's stable sort preserves the input order of equal timestamps, including with reverse=True. That satisfies this exercise's tie rule. Python's sorting documentation.

Running the example again should print:

Page: ['A']
Input after call: ['A', 'B', 'C', 'D']

The result still contains references to the original ticket dictionaries. The function does not modify them, but a caller could later do so through the returned references. Deep copying would address a different contract and is unnecessary for the requirements given here.

5. Test the contract, including failure paths

A fixture containing only open tickets from one team would miss the pagination defect. Mixed teams and statuses make the regression meaningful.

Save the following as test_tickets.py beside tickets.py. Run python3 -m unittest -v test_tickets. The eight tests should pass with the repaired function. Running the same tests against the original function exposes the missing behavior.

from copy import deepcopy
import unittest

from tickets import list_open_tickets, sample_tickets


class TicketTests(unittest.TestCase):
    def setUp(self):
        self.tickets = sample_tickets()

    def page_ids(self, **kwargs):
        page = list_open_tickets(self.tickets, "alpha", **kwargs)
        return [t["id"] for t in page]

    def test_first_page_filters_before_slicing(self):
        self.assertEqual(self.page_ids(limit=1), ["A"])

    def test_offset_counts_matching_tickets(self):
        self.assertEqual(self.page_ids(offset=1, limit=1), ["D"])

    def test_input_is_unchanged(self):
        before = deepcopy(self.tickets)
        self.page_ids()
        self.assertEqual(self.tickets, before)

    def test_large_page_excludes_other_teams_and_closed_tickets(self):
        self.assertEqual(self.page_ids(limit=99), ["A", "D"])

    def test_empty_input_and_no_matching_team(self):
        self.assertEqual(list_open_tickets([], "alpha"), [])
        self.assertEqual(list_open_tickets(self.tickets, "missing"), [])

    def test_offset_at_or_beyond_end(self):
        self.assertEqual(self.page_ids(offset=2), [])
        self.assertEqual(self.page_ids(offset=99), [])

    def test_equal_timestamps_preserve_input_order(self):
        self.tickets[3]["created_at"] = 10
        self.assertEqual(self.page_ids(), ["A", "D"])

    def test_invalid_ranges(self):
        for offset, limit in [(-1, 20), (0, 0), (0, -1)]:
            with self.subTest(offset=offset, limit=limit):
                with self.assertRaises(ValueError):
                    list_open_tickets(
                        self.tickets, "alpha", offset=offset, limit=limit
                    )

Explain what each test proves. The second-page test establishes that offsets count matching tickets. The deep comparison detects changes to both list order and ticket values. The equal-timestamp test checks an explicit ordering rule.

Passing tests provide evidence for the covered cases. They do not prove correctness for every possible input. If execution is unavailable in the interview, show the same reasoning with a hand trace and state what you would run.

6. Use AI for specific questions and verify its answers

When AI is permitted, start with enough independent understanding to evaluate the answer. This is a practice recommendation, not a claim that Google requires a particular sequence of tool use.

A useful debugging prompt supplies the intended behavior, observed failure, relevant code, and constraints:

This function should filter by team and open status, sort newest first, and then paginate. It must preserve input order. With this fixture, the first page incorrectly returns an empty list. Identify the operation responsible and propose the smallest patch. Explain which requirements the patch satisfies.

For test design, ask for expected outcomes rather than a large, unexplained test suite:

Suggest cases that would fail if pagination happened before filtering. Include mixed teams, closed tickets, nonzero offsets, and equal timestamps. State expected results before writing tests.

For review, invite challenges to your own assumptions:

Review this patch against the stated contract. Identify remaining violations, changed behavior, and assumptions that the tests do not cover. Point to the relevant code for each claim.

Check that referenced functions exist, proposed APIs fit the environment, and suggested tests assert the required behavior. Read the diff before running it. Keep the task's boundaries intact: a rewrite may introduce unrelated changes that are harder to verify.

7. Catch a plausible but incomplete AI fix

Suppose an assistant proposes replacing the original in-place sort with a new sorted list, while leaving pagination before filtering:

ordered = sorted(tickets, key=lambda t: t["created_at"], reverse=True)
page = ordered[offset:offset + limit]

This fixes the input mutation. It does not fix the empty first page. The original fixture still returns no tickets for team alpha with limit=1.

Creating a new list resolves the mutation issue. The original counterexample still fails because filtering happens after slicing. We also need to filter before pagination.

That response shows how to assess AI output: accept the valid part, identify the remaining defect, and support the conclusion with a reproducible case. The assistant's confidence is not evidence.

8. Discuss performance, security, and tradeoffs

Let n be the total number of tickets and m the number that match. The repaired function takes O(n + m log m) time in the worst case and O(m) additional space. It filters all tickets once and sorts only the matches.

For a large production dataset, discuss moving filtering, ordering, and pagination into the data-access layer. First establish the required ordering: preserving a list's original tie order is meaningful in this exercise, while a database query needs an explicit, deterministic tie rule. Offset pagination can also shift between requests when records are inserted or deleted. Those are follow-up design considerations, not reasons to expand the initial repair without agreement.

A team argument is a filter, not an authorization check. A real service must establish that the caller may access the requested team's data. Also identify where request types, field validity, and maximum page size are checked. Avoid claiming that the small exercise function is a complete production endpoint.

A 60-minute mock interview plan

Use this as a suggested practice schedule and adapt it to the actual task and allotted time.

Suggested timing and evidence for a practice session
TimeActivityEvidence to produce
0–5 minutesClarify behavior and permitted toolsA short list of requirements and assumptions
5–15 minutesTrace the relevant execution pathA code map and one concrete input
15–25 minutesReproduce and isolate the defectA failing test or execution trace
25–40 minutesImplement a focused repair; use AI where usefulA patch you can explain line by line
40–52 minutesTest normal, boundary, and failure casesResults tied to the requirements
52–60 minutesReview the change and discuss tradeoffsAn explanation of evidence and remaining limits

If you get stuck, reduce the input or examine one assumption. State what you know, what remains uncertain, and what observation would distinguish your hypotheses. If several issues exist, prioritize the requested behavior and serious correctness or access-control failures before cosmetic cleanup.

A seven-day preparation plan

Use unfamiliar code for each new exercise. Repeating a memorized bug tests recall more than comprehension.

  1. Day 1 — Read: explain a module's entry point, data flow, and side effects without changing it.
  2. Day 2 — Trace: predict the output for three inputs, including a boundary case, then compare with execution.
  3. Day 3 — Debug: reproduce one defect, add a failing regression test, and make a focused repair.
  4. Day 4 — Review AI output: evaluate a suggested patch. Demonstrate any remaining defect or unsupported claim.
  5. Day 5 — Examine boundaries: investigate mutation, invalid input, error handling, and access controls.
  6. Day 6 — Run a mock: spend 60 minutes on a small unfamiliar project with several functions and existing tests.
  7. Day 7 — Review: identify the weakest step from the mock and repeat it with different code.

For larger practice, choose a 200–500-line command-line tool, a small API service, or a data-processing module. Have a partner select a documented bug without showing you the solution. Include a follow-up requirement, such as a new filter, only after the original behavior is understood and verified. Keep algorithm and data-structure practice alongside these sessions.

Check your readiness with evidence

This is a practice checklist, not an employer's scoring system. After a mock, check whether you can:

  • Explain the relevant behavior and side effects without reading an AI answer.
  • Separate confirmed requirements from assumptions.
  • Show an input that fails before the repair and passes afterward.
  • Explain why the change fixes the cause and what it leaves unchanged.
  • Evaluate an AI suggestion using code, tests, or a counterexample.
  • Describe complexity, untested cases, and remaining design questions accurately.

Finish with a concise engineering explanation:

The empty page came from slicing before filtering. I moved filtering ahead of pagination and preserved the caller's list order. The reproduction now returns the expected ticket, and the tests cover subsequent pages, empty results, equal timestamps, and invalid ranges. Returned ticket objects remain shared, consistent with the stated contract.