← Back to blog Pinterest VO Debrief: BQ, Coding & System Design
Pinterest

Pinterest VO Debrief: BQ, Coding & System Design

2026-08-10

My Pinterest onsite ran four rounds: a behavioral round, two coding problems, and two system design questions. The pace was packed, and it ended in an offer. This debrief lays out the real questions, my approach, and the code for each round. If you're prepping for a Pinterest onsite too, I hope this note helps you zero in on what each round is really testing. If you're looking for VO interview assist or VO live support, the contact details at the end are for you.

Environment and Format Overview

All four rounds ran over video. Coding used a shared live editor; system design was drawn and explained on a whiteboard tool. The overall feel: the behavioral round cares a lot about whether you can explain a tradeoff with data, the coding rounds lean toward "step-by-step evolution plus edge-case clarification," and the system design rounds care most about whether you confirm requirements before you start designing.

Round Format Focus
Round 1 · BQ Behavioral + probing STAR structure, simplifying a complex system, UX vs. business-metric tradeoffs
Round 2 · Coding Live coding + explanation Piece reachability on a board, three-step evolution, direction map + obstacle HashSet
Round 3 · System Design Whiteboard + explanation Streaming join + group-by dashboard, bulk catalog upload architecture
Round 4 · Coding Live coding + explanation Itinerary reconstruction from shuffled tickets, Map + Set to find the start

Pacing tip: in the coding rounds, clarify thoroughly before you write; in system design, never jump straight to picking a database. Confirm the read/write pattern first, then decide on the design.


Round 1: BQ Behavioral

Questions

This round had two questions:

  1. Tell me about a time you made a complex system simple.
  2. When user experience and business metrics conflict, how do you make the tradeoff?

My Answer (STAR)

For the first question I used my experience optimizing a recommendation-feed dedup service.

For the second question I emphasized: when UX and business metrics conflict, first quantify the impact on both sides, then consider whether the decision is short-term or long-term. For example, a change might lift clicks in the short term but hurt long-term retention. I would use layered A/B metrics to put "short-term gain" and "long-term experience cost" side by side, then decide the tradeoff point together with product, rather than fixating on a single metric.

Takeaway

The biggest plus in a behavioral round is grounding the abstract "I optimized a system" in concrete numbers. Prepare before/after comparison charts so you have something to lean on when they probe the tradeoffs.


Round 2: Coding -- Piece Reachability

The interviewer had a strong accent, but communication was completely fine. The problem evolved in three steps.

Step 1: Queen Only

Given a start position and board size, a Queen can move along up/down, left/right, and the four diagonals, eight directions in total. Add every in-bounds position it can reach.

Step 2: Add Obstacles

Use a HashSet of obstacle coordinates. When scanning a direction, stop immediately on hitting an obstacle: the Queen can neither land on it nor pass through it.

Step 3: Support Multiple Piece Types

Support different pieces via a piece_type parameter: a Queen uses 8 directions, a Bishop only the 4 diagonals, and a Rook only up/down/left/right. The bounds check, obstacle check, and direction scan all share one code path; only the direction map differs.

Python Solution

from typing import List, Set, Tuple

# Direction map per piece type
DIRECTIONS = {
    "queen": [(-1, 0), (1, 0), (0, -1), (0, 1),
              (-1, -1), (-1, 1), (1, -1), (1, 1)],
    "bishop": [(-1, -1), (-1, 1), (1, -1), (1, 1)],
    "rook": [(-1, 0), (1, 0), (0, -1), (0, 1)],
}


def reachable_positions(
    start: Tuple[int, int],
    n: int,
    piece_type: str,
    obstacles: Set[Tuple[int, int]],
) -> List[Tuple[int, int]]:
    """Compute all positions the piece can reach from start on an n x n board.

    Step along each direction for the piece until we go out of bounds
    or hit an obstacle.
    """
    directions = DIRECTIONS[piece_type]
    result: List[Tuple[int, int]] = []
    r0, c0 = start

    for dr, dc in directions:
        r, c = r0 + dr, c0 + dc
        # Keep walking this direction until out of bounds or an obstacle
        while 0 <= r < n and 0 <= c < n:
            if (r, c) in obstacles:
                break  # cannot land on or pass through an obstacle
            result.append((r, c))
            r += dr
            c += dc

    return result

Time complexity: O(n + b), where n is the board side length (each direction walks at most n steps, and the number of directions is constant) and b is the obstacle count (cost of building the HashSet). Space complexity: O(b) for the obstacle set.

Takeaway

The key to the three-step evolution is letting the obstacle check and direction scan share one path: adding obstacles only adds a break inside the while, and adding piece types only swaps the direction map, leaving the core logic untouched. The interviewer valued this "add requirements without tearing down the structure" style.


Round 3: System Design -- Two Design Questions

Design 1: Ad-Event Aggregation Dashboard

Two existing services emit ad events (adId, servingId) and user events (servingId, action type click / impression, region). Build a dashboard that aggregates by ad, action type, and region.

At its core this is a streaming join + group-by aggregation: ad events build a servingId -> adId mapping, user events enrich via servingId to attach the adId, then aggregate by (adId, action, region).

Architecture choices:

Points to raise proactively: how to set the TTL for the servingId mapping, how to handle out-of-order / late events with watermarks, and whether the aggregation uses tumbling or sliding windows.

Design 2: Bulk Catalog Upload

Merchants bulk-upload catalogs (price, inventory, etc.), with up to 500k products per job.

My initial idea was to have the client / SDK split the data into batches of 100 rows and push them up, but I quickly realized this pushes reliability and job management onto the client, which is not ideal.

A better approach:

  1. The merchant uploads the large file directly to object storage, and the server returns a job ID.
  2. A message queue asynchronously drives parsing, validation, sharding, and bulk writes.
  3. The client polls progress and failure details by job ID, and only needs to retry the failed parts.

This way the client only handles the upload and polling, the heavy lifting happens asynchronously on the server, and both reliability and observability improve.

Lesson learned: don't pick a write-optimized database before confirming the read pattern. First confirm the catalog's read frequency, query patterns, update ratio, and consistency needs, then decide how to configure the database / indexes / cache.


Round 4: Coding -- Itinerary Reconstruction

Given a set of shuffled tickets, each with an origin and a destination, all forming exactly one complete route, reconstruct the correct itinerary order.

Approach

Use an origin -> destination Map and a Set of all destinations. The origin that does not appear in the destination set is the start of the whole route. Then follow the map station by station.

Python Solution

from typing import Dict, List, Set, Tuple


def reconstruct_itinerary(
    tickets: List[Tuple[str, str]]
) -> List[str]:
    """Reconstruct the full itinerary from shuffled tickets.

    Build an origin -> destination map, find the unique start,
    then follow the map station by station to build the route.
    """
    next_stop: Dict[str, str] = {}
    destinations: Set[str] = set()

    for origin, dest in tickets:
        next_stop[origin] = dest
        destinations.add(dest)

    # Find the start: the origin that never appears as a destination
    start = None
    for origin, _ in tickets:
        if origin not in destinations:
            start = origin
            break

    # Follow the map station by station to build the itinerary
    itinerary = [start]
    current = start
    while current in next_stop:
        current = next_stop[current]
        itinerary.append(current)

    return itinerary

Time complexity: O(n), where n is the number of tickets (one pass to build the map, one pass to walk it). Space complexity: O(n) for the map and destination set.

Follow-up: Edge Cases

The interviewer probed several abnormal inputs:

My handling was to clarify with the interviewer first: does the input guarantee a single, valid, unique chain? Once the convention is confirmed, decide accordingly. If it's not guaranteed, detect the anomaly (for example, number of starts != 1, or reconstructed station count != ticket count + 1) and, by the agreed convention, return an error, return empty, or report the specific anomaly, rather than silently emitting a wrong route.


Prep Notes


FAQ

Q1: How many rounds is a Pinterest VO, and what does it cover?

Mine was four rounds: one behavioral round, two coding rounds, and one system design round (with two design questions). The behavioral round tests tradeoff articulation, coding leans toward step-by-step evolution, and system design checks whether you confirm requirements first.

Q2: Why do the coding problems favor a "three-step" format?

They want to see whether you can keep your structure from collapsing as requirements grow. The piece problem went from Queen to obstacles to multiple piece types; the good approach shares one direction-scan path, so adding requirements only swaps the direction map and adds a break.

Q3: For the ad-event aggregation dashboard, why Flink instead of batch processing?

The dashboard needs near-real-time multi-dimensional aggregation, and events flow in continuously. Flink handles the streaming join and windowed aggregation, paired with Kafka for ingestion, Redis for the short-lived mapping, and an OLAP store for the aggregated results, keeping the whole pipeline real-time.

Q4: For bulk catalog upload, why not have the client split into batches?

Client-side batching pushes retries, progress management, and failure recovery onto the client, which hurts reliability. A better approach is to upload the large file to object storage to get a job ID, then have the server asynchronously parse, validate, shard, and bulk-write via a message queue, with the client only polling progress and failure details.

Q5: What about duplicate origins or cycles in itinerary reconstruction?

Confirm with the interviewer first whether the input guarantees a single valid chain. If it's not guaranteed, detect the anomaly: number of starts other than 1, or a mismatch between reconstructed station count and ticket count, and by the agreed convention return an error, return empty, or report the anomaly, rather than silently emitting a wrong route.


Prepping for a Pinterest VO? We know the rhythm of the four rounds -- data-driven tradeoffs in BQ, step-by-step evolution in coding, requirement clarification in system design -- and offer VO interview assist and VO live support from mock runs to the real thing, helping you polish every round.

Add WeChat Coding0201 now to get a one-on-one custom prep plan.

Contact