cswork / libraryOpen practice workspace

Articles · The Trade Desk

The Trade Desk SWE Intern Interview Guide 2027: Coding Assessment, Technical Rounds, and Timeline

Prepare for The Trade Desk SWE intern interviews with coding assessment context, a tested versioned recipe store, rollback, OOD and a seven-day plan.

A staged coding problem can become difficult without changing its topic. Adding users changes identity; adding rollback changes what “current” means; adding concurrent writers changes where a version check must happen. Preparing for The Trade Desk SWE intern interviews means being able to explain those transitions and keep the code correct as requirements expand.

This guide retains coding-assessment preparation, live technical practice, object-oriented design, project discussion and a seven-day plan. Its main addition is a complete original versioned-store exercise with a trace, runnable code and checked follow-ups. It is practice material, not a prediction of a private 2027 assessment.

Start with Design a Versioned Recipe Store with Search and Rollback.

What is confirmed for the 2027 recruiting cycle?

Check the active opening and your recruiter materials before treating an older internship description as current. On October 8, 2026, the official 2027 North America posting linked by the previous guide returned “Page not found.” That does not prove the internship was canceled or that every opening is closed. It means that URL no longer supports current eligibility, office, hybrid-policy or deadline claims.

The current careers site describes advertising technology for the open internet and emphasizes curiosity and ownership. Connect your interest to a real project or engineering problem. Do not turn company context into a purported interview rubric or force ad auctions into every coding answer.

Ask recruiting which assessments remain, the permitted language and tools, the design scope, availability requirements and expected next-step window. The old guide's candidate-reported stage sequence is not an official 2027 promise. Prepare for timed coding and a live technical discussion while using your invitation for scheduling and rules.

Prepare for the assessment actually assigned

If your invitation specifies CodeSignal's standard General Coding Assessment, its documentation describes four questions in 70 minutes. Its rules also note that employers can configure a different duration. This platform description does not establish that The Trade Desk assigns the standard GCA to every 2027 intern candidate.

Read the assessment's own rules. Standard GCA guidance prohibits AI assistance, including AI syntax searches, and requires submitting work to save it before leaving a task. Custom formats may have different instructions; confirm those rather than generalizing from a friend's invitation.

For practice, scan the set, solve the clearest task completely and reserve time for boundary checks. A 70-minute four-task rehearsal is one useful format, not an employer cutoff or timer guarantee. Record whether lost time came from selecting a model, language syntax, implementation or missed edge cases. Repair that cause before repeating another set.

Original staged exercise: a versioned recipe store

Build one in-memory class. Stage one adds create, read, update and delete. Stage two adds ordered listing and name search. Stage three adds owners, immutable history and rollback. A final follow-up introduces two writers that read the same revision.

These are the chosen mock rules. Another prompt may choose differently, so clarify them before coding:

DecisionContract used here
IdentityTrimmed, case-sensitive owner and name; both nonempty
BodyText, including an empty string; no nested mutable payload
VersionsStart at 1; each successful write appends, even unchanged text
DeleteAppend a tombstone; hide from live reads and search; preserve history
RollbackCopy a live historical body to a new version
Deleted namesReserved; restore by rollback rather than recreate
SearchOwner-scoped, case-insensitive name substring; case-sensitive sorted names
Stale writesExpected head must match inside the locked mutation

The invariant is simple: each identity's history is append-only, revision equals its one-based position, and only its last entry defines live state. Returning immutable version objects in a tuple prevents callers from editing earlier records through the public methods. Passing an owner is a namespace decision; it is not authentication.

Walk through five versions before implementing

Ana creates Soup with “tomato” at revision 1. Ben independently creates Soup with “lentil.” Ana updates with expected revision 1 and gets revision 2, “tomato + basil.” Rolling back to 1 with expected 2 appends revision 3, “tomato.” Revision 2 remains available.

Ana deletes with expected 3, producing revision 4 with a tombstone. Live get fails and Ana's search excludes Soup, but history still contains four records. Rolling back to 2 with expected 4 restores the recipe as revision 5. A later update using expected 4 conflicts and adds nothing. Ben's history remains at revision 1 throughout.

Runnable implementation

The lock serializes each public operation's access to shared history in this process. In particular, checking the expected revision and appending its successor happen together. Reading first and checking outside the lock would allow both competing writers to pass.

from dataclasses import dataclass
from threading import Lock

class Conflict(Exception):
    pass

@dataclass(frozen=True)
class Version:
    revision: int
    body: str | None

class RecipeStore:
    def __init__(self):
        self._data = {}
        self._lock = Lock()

    @staticmethod
    def _key(owner, name):
        if not all(isinstance(x, str) and x.strip() for x in (owner, name)):
            raise ValueError("owner and name must be nonempty strings")
        return owner.strip(), name.strip()

    @staticmethod
    def _number(value, minimum):
        if type(value) is not int or value < minimum:
            raise ValueError("invalid revision")

    @staticmethod
    def _append(history, expected, body):
        if expected != len(history):
            raise Conflict("read the latest revision before writing")
        version = Version(len(history) + 1, body)
        history.append(version)
        return version

    def create(self, owner, name, body):
        key = self._key(owner, name)
        if not isinstance(body, str):
            raise ValueError("body must be text")
        with self._lock:
            if key in self._data:
                raise Conflict("identity already exists, including deleted recipes")
            self._data[key] = [Version(1, body)]
            return self._data[key][-1]

    def history(self, owner, name):
        key = self._key(owner, name)
        with self._lock:
            return tuple(self._data[key])

    def get(self, owner, name):
        latest = self.history(owner, name)[-1]
        if latest.body is None:
            raise KeyError("recipe is deleted")
        return latest

    def put(self, owner, name, body, expected):
        key = self._key(owner, name)
        self._number(expected, 1)
        if not isinstance(body, str):
            raise ValueError("body must be text")
        with self._lock:
            history = self._data[key]
            if history[-1].body is None:
                raise KeyError("use rollback to restore a deleted recipe")
            return self._append(history, expected, body)

    def delete(self, owner, name, expected):
        key = self._key(owner, name)
        self._number(expected, 1)
        with self._lock:
            history = self._data[key]
            if history[-1].body is None:
                raise KeyError("recipe is already deleted")
            return self._append(history, expected, None)

    def rollback(self, owner, name, target, expected):
        key = self._key(owner, name)
        self._number(target, 1)
        self._number(expected, 1)
        with self._lock:
            history = self._data[key]
            if target > len(history) or history[target - 1].body is None:
                raise ValueError("target must be an existing live version")
            return self._append(history, expected, history[target - 1].body)

    def search(self, owner, query):
        owner = self._key(owner, "placeholder")[0]
        if not isinstance(query, str):
            raise ValueError("query must be text")
        with self._lock:
            return tuple(sorted((name, history[-1])
                for (user, name), history in self._data.items()
                if user == owner and history[-1].body is not None
                and query.casefold() in name.casefold()))

get captures a consistent immutable latest version through history; another writer can advance the store immediately afterward. That is a point-in-time read, not a promise that its returned revision stays current. The caller must supply that revision when writing and handle a conflict.

Missing identities raise KeyError. Invalid inputs raise ValueError; an existing identity or stale expectation raises Conflict. Updating or deleting an already deleted head raises KeyError. Rollback rejects a tombstone target. These exception choices are part of this exercise, not a required employer API.

Check behavior, not only method names

Use concrete assertions:

s = RecipeStore()
assert s.create("ana", "Soup", "tomato") == Version(1, "tomato")
assert s.put("ana", "Soup", "tomato + basil", 1).revision == 2
assert s.rollback("ana", "Soup", 1, 2) == Version(3, "tomato")
assert s.delete("ana", "Soup", 3) == Version(4, None)
assert s.search("ana", "soup") == ()
assert s.rollback("ana", "Soup", 2, 4) == Version(5, "tomato + basil")
assert s.history("ana", "Soup")[1].body == "tomato + basil"

The local verification executed 1,462 checks. Fixed cases cover separate owners, whitespace handling, case-sensitive identity, case-insensitive search, preserved snapshots, unchanged-body writes, tombstones, invalid revisions and rejected writes leaving history unchanged. A separate list-based model checks 500 generated operations and every resulting body and revision sequence.

Another 100 controlled races release two threads after both read revision 1. Each trial verifies exactly one appended revision 2, one conflict, two history entries and both threads terminating. This tests local locking behavior; it does not establish database isolation, network retries, durability or a throughput benchmark.

Explain complexity with the implemented data structures

Dictionary identity lookup is expected constant time after key processing. Appending a version is amortized constant time, apart from string processing and lock contention. Rollback indexes the target directly and shares its immutable text rather than copying the whole history.

Returning history takes O(V) time and references for V versions. The current get uses that method, so it also costs O(V); an optimization would read the last element directly under the lock. Name search scans all R stored identities, performs string matching and sorts K matches. Its cost includes scanned name lengths and O(K log K) result ordering; it is not constant-time search.

Memory grows with all retained versions and text. There is no retention limit. A single global lock keeps the implementation small but blocks unrelated recipes during long scans. Those are acceptable teaching choices only while the problem's size and latency permit them. State the bottleneck before suggesting an index or a more granular lock.

Follow-up one: fast search without stale results

An index introduces another invariant: every searchable result must correspond to the current live head. On update, delete or restore, remove the old searchable terms and add the new ones in the same logical mutation. Otherwise a deleted recipe can remain visible or a restored recipe can disappear from search.

Here search matches names, not body text. Since names do not change, a name index can retain identity membership while filtering tombstones at read time. But substring matching needs a different plan from exact matching; a dictionary of full names does not solve arbitrary substrings. Ask about query patterns and result limits before choosing prefixes, n-grams or an external search service.

If indexing is asynchronous, disclose the freshness window and recheck returned identities against current state. An index hit is a candidate, not authority to return stale private data. Real authorization belongs at the service boundary, beyond this owner-parameter mock.

Follow-up two: persistence and retried writes

For a persistent service, store logical identity, immutable version rows and a current revision. Within a transaction, conditionally advance the head only when it equals the expected revision, and insert the new version. Define uniqueness on identity and revision. A local Python lock does not coordinate separate servers or survive a process crash.

A lost response creates a separate issue. Retrying a successful update with its old expected revision conflicts, even though the first attempt may have committed. This class is not retry-idempotent. An HTTP design can attach a request ID to a fixed semantic payload and atomically store the resulting receipt with the version. Return that receipt on a matching retry; reject the same ID with different content.

Explain receipt retention and what happens after it expires. Do not silently rerun a stale rollback against a newer head: that could undo someone else's work. The client should reread, show the changed state and obtain a fresh decision. None of these database or HTTP mechanisms is implemented or proven by the local class tests.

Live coding, projects and a seven-day plan

In a live round, narrate one requirement change at a time. If the interviewer changes rollback to moving a pointer, stop and revise the invariant: head revision may no longer equal history length. Update the tests and expected-write semantics before patching code. Clean adaptation is more useful than defending a rule the prompt never required.

Prepare one project where you can explain a decision you owned, the alternative, a failure, the evidence and your correction. Use actual measurements only when you collected them. For “Why The Trade Desk?”, connect a specific interest in engineering or advertising technology to your experience, then describe what you hope to learn.

The schedule below is an original practice plan; its timers do not describe employer rounds.

DayWorkConcrete output
1Confirm invitation; run a coding baselineRules and two largest gaps
2Practice arrays, maps and stringsTested solutions and error log
3Add staged store operationsAPI, invariant and five-version trace
4Implement and test OODRejected-operation and search cases
5Add concurrency and persistence follow-upsAtomic boundary and retry explanation
6Run 45-minute coding and 45-minute design mocksRecorded reasoning and remaining bugs
7Repair weak points; rehearse a real projectRetested failures and logistics

Ask the recruiter for an expected next-step window. An expired public listing and a slow response do not establish your application's status. If the stated window passes, send a concise follow-up and keep other applications moving.

Frequently asked questions

Is CodeSignal guaranteed for The Trade Desk's 2027 internship?

No current official material reviewed here establishes that universal format. If your invitation names the standard GCA, use its platform guidance; otherwise follow the assigned assessment.

Does rollback make an older revision current?

In this original mock it appends a new version with an older live body. A different prompt may move a pointer. Clarify the requirement; the two contracts require different tests.

Why retain deleted recipe names?

It preserves a single identity and history and avoids revision reuse. This is a chosen exercise rule, not a universal product requirement. Restore uses a new revision.

Do the concurrency tests prove a production service is safe?

No. They check a local locked class under controlled competing writes. Cross-process consistency, crash recovery, authentication and retried HTTP requests require separate designs and verification.

Do I need senior-level distributed system design?

Confirm the expected scope. First make class operations, ownership, invariants and tests precise. Extend toward persistence and scale when the interviewer changes those requirements.

Sources and scope

Reviewed October 8, 2026. Recruiting details can change.