ByteDance's consumer applications include TikTok, CapCut and Lark. This guide's research notes describe the Software Engineer role as work on the systems behind those products, across problem spaces such as microservices, data pipelines, distributed storage, real-time media streaming, ad serving, recommendation infrastructure, trust and safety platforms and machine-learning deployment. The notes mention both backend and frontend services, and work that can range from distributed graph analytics engines to optimising client-side rendering frameworks. What you work on depends on the organisation you join. The notes give Core TikTok Backend, Monetization & Commerce Ads, Infrastructure & Cloud, and Trust & Safety as examples.
The reported questions fall into four groups. Algorithms and data structures: reversing a linked list in groups of k, LRU and LFU caches with O(1) operations, and grid searches that carry extra state. System design: a distributed key-value cache with strictly increasing versions, an auction platform that must prevent bidding races, a global API rate limiter, real-time top-k trending posts, and an RBAC schema. CS fundamentals: what happens when you enter a URL, TCP versus UDP, MySQL clustered and secondary indexes, Redis sorted sets, and thread synchronisation. The fourth group is detailed questioning of your own past projects. The PracHub bank for this role has more questions of the same kinds, including interval merging, course-schedule feasibility, an expiring LRU cache, a distributed rate limiter and a content moderation pipeline.
Candidates describe five stages over roughly four to six weeks. First comes a recruiter screen. Next is an online assessment, which some candidates skip and go straight to a technical phone interview. After the phone interview comes an onsite or virtual loop of three technical rounds, and last a final conversation with a hiring manager or HR partner. The notes say you must pass each technical stage before the next one is scheduled, and that interviewers keep asking follow-ups until they reach the limit of what you know. For every topic, prepare one level deeper than your first answer.
Recruiter Screen
reportedThe research notes describe this as a screening call to check your fit for the role. Use it to learn what you need for preparation: which organisation or team the role sits in, whether you will take the online assessment or go straight to the phone interview, which languages the coding rounds accept, and whether system design is in scope at your level. Raise hard constraints here, such as start date, location, work authorisation or a competing deadline. That costs far less now than after several interviewers have spent time on you.
What to demonstrate
- Whether your background and level match the role you applied for
- Whether your constraints on location, start date, work authorisation and competing timelines fit the process
- How clearly you summarise your recent work, which sets up the résumé questions in later rounds
How to prepare
- Write your questions for the recruiter: team or organisation, whether the online assessment applies to you, allowed languages, and whether design is in scope at your level
- Write each constraint down as a one-line fact before the call so you state it rather than negotiate it live
- Prepare a short summary of your most complex recent project, and only pick one you can defend in detail later
Online Assessment
reportedCandidates report that some take an online assessment and others go straight to a technical phone interview. The research notes do not describe the assessment's format, so prepare for the reported coding questions, whichever stage they appear in: linked-list manipulation such as reversing in groups of k, cache structures with O(1) operations, and grid searches whose state is more than the cell. Get a correct brute force passing first, then improve it without deleting the working version.
What to demonstrate
- Whether your code runs and returns correct output on inputs you were not shown
- Whether you handle degenerate inputs: an empty list, k larger than the list, a single-cell grid, an unreachable target
- Whether the complexity of your submission fits what the input constraints require
How to prepare
- From a blank file in your interview language, implement k-group reversal and an LRU cache. Test each on empty input, one element and a final group shorter than k
- Practise grid searches where the state includes more than position, such as (row, column, fuel remaining), until choosing the state comes without thinking
- Add a fixed check before every submission: loop bounds, null handling and integer overflow
Technical Phone Interview
reportedThe research notes describe this interview as coding plus theory questions, and they list rapid-fire CS fundamentals among what technical screeners ask. Topics include what happens when a URL is entered, TCP versus UDP with congestion and flow control, MySQL clustered versus secondary indexes, B+ trees versus hash indexes, Redis sorted sets and persistence, and thread synchronisation and deadlock. The notes call over-investing in algorithm practice while neglecting fundamentals a common preparation mistake. For coding questions in general, the notes say to expect follow-ups asking you to cut time or space, or to adapt the code for concurrent use, so have an answer ready for both.
What to demonstrate
- Whether you explain networking, database and OS mechanisms accurately and in order, not as a list of keywords
- Whether you write working code and can trace it by hand on a sample input
- Whether you can adapt a solution when asked to optimise it or make it safe under concurrent access
How to prepare
- Write a one-paragraph answer to each reported theory question. Then have someone ask 'why?' twice after each one and note where you run out
- Rehearse the URL-to-page walk end to end: DNS resolution, TCP handshake, TLS negotiation, HTTP request and response, and where each step can fail
- For one structure you have already coded, such as an LRU cache, explain how you would make it thread-safe and what a single global lock costs
Onsite/Virtual Loop
reportedThe notes describe three technical rounds followed by a final round with a hiring manager or HR partner. They do not say which technical round covers which topic, so prepare all four categories the reported questions fall into: coding, system design, CS fundamentals and your past projects. Reported design questions include a distributed key-value cache with timestamped keys and strictly increasing versions, an auction platform that must prevent race conditions under heavy bidding, a global API rate limiter, real-time top-k trending posts over sliding windows, and an RBAC schema. The notes say mid-level and senior candidates face both high-level and low-level design.
What to demonstrate
- Whether you lead a design yourself: clarify scale, choose storage, and cover failure modes and consistency without being prompted
- Whether you handle concurrency concretely, for example how two bids on one item are serialised or how versions stay strictly increasing
- Whether your coding answers run and handle edge cases
- Whether you can defend the choices in your past projects under detailed follow-up
How to prepare
- Work the auction and versioned-cache questions end to end. For each, name the operation that must be atomic and the mechanism that makes it so
- Work the rate limiter with an explicit algorithm choice, such as token bucket or sliding window, and say where its state lives and what happens when that store is unreachable
- Do this guide's cursor pagination worked exercise to practise specifying an API contract exactly
- Prepare two project stories down to the metrics, the bottlenecks and the alternatives you rejected
Final Round
reportedThe notes describe a final interview with a hiring manager or HR partner to judge overall fit and alignment. They also say both engineers and hiring managers question candidates' past work, so prepare the reported résumé questions for this conversation too. Those questions cover your most complex architecture and why you chose its stack, a production outage you triaged, how you tracked stability and latency SLAs, a slow service you optimised, and a disagreement you settled with metrics or benchmarks.
What to demonstrate
- Whether you describe your own contribution specifically, and consistently with what you said in earlier rounds
- Whether you can state the trade-offs you made under deadline and how you measured their impact
- Whether your goals and working style fit the role and the team
How to prepare
- Write one page per project with the numbers you will quote (traffic, latency before and after, team size, what broke) and how each was measured
- Prepare the outage story in order: detection, triage, mitigation, root cause, and the change that stops it happening again
- Prepare questions for the hiring manager about the team's systems and how its work is measured
34 candidate reports. Individual accounts describe a particular role and hiring cycle.
ByteDance Software Engineer Interview Experience — Rejected After a TikTok Trust & Safety System Design Round on a Video Moderation Pipeline
Interviewed in June. Second round: system design interview, 1 hour. Project deep-dive (about 20 minutes). System Design — Video Content Moderation System Question: design a simplified video content moderation system. The interviewer scoped it very clearly: I didn't need to design the video upload part — assume there's already a system that pushes uploaded videos to you. What I needed to design wa…
Read full experienceTikTok Intern Software Engineer Interview Experience — Agent Metrics, Tool Debugging, and Graphs
The interviewer opened with, "Do you speak Chinese?" After I said yes, we spoke Chinese for the entire interview. I gave a brief introduction, then we went straight into questions. Fundamentals / business design Memory and virtual memory: Follow-up: What is virtual memory for? Follow-up: Can virtual memory space (VRAM) be larger than physical memory (RAM)? The reasoning we discussed: Yes. Virtual…
Read full experienceByteDance New Grad Software Engineer Interview Experience — Backend Fundamentals, Databases, and One DFS Problem
First round: technical interview. The interviewer was based in China. The interview leaned toward backend fundamentals, databases, middleware, and web protocols, followed by one algorithm problem. Deep dive into resume projects The interviewer asked about past projects on my resume, focusing on project architecture, the reasons for technical choices, and implementation details from the business l…
Read full experienceByteDance Senior+ Software Engineer Interview Experience — AI QA Workflow Design and an Unfinished Merge Intervals
Good luck to everyone. I hope my bad example can help someone. Location: San Jose Role: Senior SDE, Quality Platform & AI Test Automation Interviewer: The team's QA leader; the interview was in Chinese over video with a shared whiteboard. Background: More than ten years in DevOps and engineering productivity. I have built CI/CD platforms and an AI code-review platform. How the interviewer framed…
Read full experienceByteDance Intern Software Engineer Interview Experience — Four Difficult Online Assessment Questions
There were four questions in total, and they were quite difficult. Question 1 Description Given an array of positive integers numbers, calculate how many of its elements have an even number of digits. Note: The solution did not need to be optimal, but a time complexity no worse than O(numbers.length^2) would fit within the execution time limit. Example For numbers = [12, 134, 111, 1111, 10], the…
Read full experiencePracHub editorial advice for the preparation topics above.
Preparing only for live coding and stalling on the CS theory questions in the technical phone interview
The research notes describe the phone interview as coding plus theory, and they list CS fundamentals questions on TCP congestion and flow control, MySQL clustered versus secondary indexes, Redis sorted-set internals and deadlocks. Write a short answer to each, then practise the second 'why'. For example: a B+ tree serves range queries better than a hash index because its leaves are ordered and linked, so a range is one descent plus a scan. InnoDB handles phantom reads under Repeatable Read with MVCC snapshots for plain reads and next-key locks for locking reads. When you do not know something, say what you do know and reason from there instead of guessing.
Submitting LRU, LFU or k-group reversal code that breaks on the boundary cases the question itself names
The reported questions state their own traps: a final group with fewer than k nodes, and O(1) for every cache operation. For k-group reversal, confirm whether a short final group stays in its original order, then test k = 1, k equal to the list length and k larger than it. For LFU, O(1) needs three things: a key-to-node map, a frequency-to-list map, and a minimum-frequency value that resets to 1 on every insert. A heap makes eviction O(log n) and misses the requirement. Trace each solution by hand on a small input before you say you are done.
Designing the auction platform or versioned cache without saying which operation must be atomic
The reported design questions centre on concurrency: bidding races, strictly increasing versions, thread-safe reads and writes, and top-k over sliding windows. A diagram of a queue next to a cache does not answer them. Name the invariant, for example that a bid is accepted only if it beats the current highest, or that a key's version never goes backwards. Name the one place it is enforced, such as a conditional write, a per-key sequencer, or a partition that serialises one item's bids. Say what happens when a consumer receives a message twice. Then give the failure you designed for and its recovery path.
Coding a grid search before the state and the allowed moves are pinned down
On the zigzag grid question, ask whether a cell may be revisited. Two adjacent cells with different values can alternate up and down forever, so the state graph over (cell, next direction) has cycles and memoised DFS on it is not valid as stated. Settle the constraint first, for example right and down moves only, which makes the graph acyclic. On the fuel-limited grid, run Dijkstra over (row, column, fuel remaining), with a recharge station resetting fuel, for O(mnF log(mnF)). State the complexity and get the interviewer to agree before writing code.
Quoting résumé metrics you cannot explain when the follow-up asks how they were measured
The reported résumé questions ask how you tracked latency SLAs, which tools found a slow query, and why you chose one stack over the alternatives. The notes say interviewers keep asking follow-ups until they reach the limit of your knowledge. Before the loop, write down for every number the time window, the percentile and where the timer started. For every major choice, write down the alternative you rejected and why. A number you cannot defend does more damage than giving no number.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Design and implement a data structure for an LRU (Least Recently Used)…
Design and implement a data structure for an LRU (Least Recently Used) cache and an LFU (Least Frequently Used) cache with $O(1)$ time complexity for basic operations.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Walk one small example through your approach before writing the whole thing.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- Which test case would catch an off-by-one here?
- What is the worst case, and how likely is it on real data?
Given a head pointer to a linked list, reverse the nodes in groups of …
Given a head pointer to a linked list, reverse the nodes in groups of $k$. Handle edge cases where remaining nodes are fewer than $k$.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Walk one small example through your approach before writing the whole thing.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- Which test case would catch an off-by-one here?
- What is the worst case, and how likely is it on real data?
Given a 2D grid containing integers, find the longest zigzag path wher…
Given a 2D grid containing integers, find the longest zigzag path where adjacent cells alternate between strictly increasing and decreasing values.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- State the target complexity and say which constraint rules the naive version out.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- Which test case would catch an off-by-one here?
- How does this change if the input no longer fits in memory?
Given an $m \times n$ grid with movement costs, obstacle cells, and re…
Given an $m \times n$ grid with movement costs, obstacle cells, and recharge stations, calculate the minimum cost to navigate from start to finish with limited fuel using a 3D graph search.
Approach
- State the target complexity and say which constraint rules the naive version out.
- Walk one small example through your approach before writing the whole thing.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
Flag actors exceeding a rate ceiling in any sliding window
An event stream gives you (actor_id, action_kind, ts_ms) for engagement writes, already sorted by ts_ms: up to 200 million events over one day from up to 30 million distinct actors. Return every actor_id that at any point had more than K actions inside a window of W milliseconds, where K is at most 1000 and W at most 60000. The window slides continuously; it is not a fixed bucket. One pass over the stream. State time and space bounds, and say how memory stays bounded given that most of those actors are idle at any instant.
Approach
- Reduce the per-actor state to the minimum that can answer the question: you never need all of an actor's timestamps, only the K-th most recent one. Keep a K-slot ring buffer per actor; on each event, if the buffer is already full, compare ts against the oldest slot, and flag when the difference is within W, because that means K+1 events fall inside one window.
- Say why fixed tumbling buckets of width W are wrong rather than merely approximate: K events at the end of one bucket and K at the start of the next are 2K events inside a single W-wide window and never trip a bucketed counter. The undercount is structural, not a tuning issue.
- Bound memory by eviction, not by capacity: an actor whose most recent event is older than W can never contribute to any future window, so drop its buffer. Maintain a FIFO of (actor_id, ts) appended on every event and pop from the front while the front ts is older than now minus W, discarding an entry whose ts no longer matches that actor's latest event. That lazy-deletion pop is amortized O(1).
- Give the bounds in terms of the right variable: O(n) time overall, and O(A_w * K) space where A_w is the number of distinct actors active within any W-millisecond window, which at W = 60 seconds is orders of magnitude below 30 million. Quoting space as O(actors) instead of O(active actors) is the answer that makes this look infeasible when it is not.
- Fix the boundary convention before coding it. State whether the window is half-open, so that an event exactly W after the K-th previous one does not flag, and keep that convention identical in the eviction test, or the detector disagrees with itself at the edge.
Worked solution 20 min
- Implement the per-actor ring buffer of K timestamps with a write index, and the flag test comparing the incoming ts against the slot about to be overwritten.
- Add the eviction FIFO and the lazy-deletion check, then confirm on paper that an actor which goes quiet for longer than W has its buffer released.
- Construct the adversarial input by hand: K events in the last millisecond of one bucket and K in the first millisecond of the next, and verify your implementation flags while a bucketed one does not.
- State the space bound as O(A_w * K) with a concrete number for W = 60 seconds at the given event rate.
Follow-up
- The stream is now unsorted by up to 5 seconds of clock skew. What breaks first, and what is the minimum buffering that restores a correct answer?
- You are given a fixed memory budget that may not grow with the active actor count. What structure do you reach for, and which direction does its error run: false flags or missed ones?
- The product wants the count of distinct content items acted on in the window rather than the count of actions. What in your per-actor state has to change, and what does that do to the space bound?
Backfill a thread root column on a live table
content_item takes 4,000 inserts a second and serves reads on a 40 ms budget. You must add root_content_id BIGINT, backfill it for 900 million existing rows, add an index on (root_content_id, content_id), and end with root_content_id guaranteed non-null for replies. content_id is time-sortable, so a reply's id always exceeds its parent's. Give the ordered steps with the lock each one takes, the batched backfill statement, and the rule that keeps newly inserted rows correct while the backfill runs. Name the step most likely to cause an outage, and give a verification that would catch a backfill which filled every row with the wrong value.
Approach
- Add the column nullable with no volatile default. On PostgreSQL 11 and later ALTER TABLE ... ADD COLUMN root_content_id BIGINT is a catalogue change with no table rewrite, and a non-volatile default is stored in the catalogue rather than written into every row. It still needs ACCESS EXCLUSIVE for an instant.
- That instant is the outage risk, and the mechanism is the lock queue: the ACCESS EXCLUSIVE request waits behind any open transaction holding a conflicting lock - a long analytics read, an idle-in-transaction session, an anti-wraparound autovacuum - and every request arriving afterwards queues behind it. Set lock_timeout to a couple of seconds and retry in a loop instead of holding the queue, and check pg_stat_activity for long transactions first.
- Dual-write before backfilling. From the moment the column exists, the publishing service sets root_content_id on insert as the parent's root, or the parent's id when the parent is an original. The backfill then owns only rows below a watermark and cannot race the live path.
- Backfill ascending by content_id in bounded batches, committing each - and do not trust the obvious self-join. UPDATE content_item c SET root_content_id = COALESCE(p.root_content_id, p.content_id) FROM content_item p WHERE c.parent_content_id = p.content_id AND c.content_id > $w AND c.content_id <= $w + 50000 AND c.root_content_id IS NULL reads p from the snapshot taken when the statement started, before the statement had written anything. A reply's id does exceed its parent's, but that only guarantees the parent is already filled in when it sits in an earlier, committed batch. When parent and child share one 50,000-id window the parent is still NULL to this statement, COALESCE(NULL, p.content_id) hands the child its parent's id as the thread root, and every level below depth 1 inside that window is silently wrong. The column is non-null afterwards, so the completeness check these migrations usually ship with - count of NULL roots is zero - reports success.
- Guard the join so the statement can only write a root it can prove: AND (p.parent_content_id IS NULL OR p.root_content_id IS NOT NULL). Each write is then either the parent's own id because the parent is an original, or the parent's already-resolved root, and a child whose parent is an unfilled in-batch reply is skipped rather than guessed at. Repeat the statement on the same batch until it reports zero rows: each repetition commits, so the next one's snapshot sees the level below it and the loop climbs one level of depth per pass, bounded by the deepest reply chain whose ids all land inside one window - one pass for most batches. A parent-pointer cycle or a dangling parent_content_id leaves those rows NULL instead of wrong, which is exactly what you want the completeness check to surface. Keep the watermark in its own table so the job restarts cleanly, and throttle on replication lag, since every UPDATE writes a new row version and the WAL is what replicas must keep up with.
- Build the index with CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block, makes two passes over the table, does not block writes, and on failure leaves an INVALID index that must be dropped and rebuilt rather than retried in place.
- Finish the constraint without a full-table ACCESS EXCLUSIVE scan: ADD CONSTRAINT ... CHECK (item_kind = 'original' OR root_content_id IS NOT NULL) NOT VALID takes a brief ACCESS EXCLUSIVE and scans nothing, then VALIDATE CONSTRAINT takes SHARE UPDATE EXCLUSIVE and scans while writes continue. A plain SET NOT NULL is wrong here anyway, because the column is legitimately null for originals.
Worked solution 40 min
- Build a 5M-row table with parent pointers and time-sortable ids, including one 8-deep reply chain whose ids all land inside a single 50,000-id batch, then time ADD COLUMN with nothing else running.
- Repeat it while a second session holds an open transaction reading the table, and record how long every other query blocks with and without lock_timeout.
- Run the unguarded self-join once over that batch and inspect the chain: every reply from depth 2 down carries its parent's id rather than the thread root, while the NULL-root count already reads zero.
- Add the guard, loop the batch until it reports zero rows, and record the pass count against the chain depth. Kill the job mid-batch once and restart from the watermark.
- Run CREATE INDEX CONCURRENTLY under write load, then interrupt a second attempt and inspect the leftover index.
- Add the NOT VALID check, validate it, and time both while inserts continue.
Follow-up
- The backfill is 60 percent done and replication lag reaches 90 seconds. What do you do, and what does that imply about the batch size you chose?
- Verify the backfill is both complete and correct without a full table scan on the primary. Which of the two is harder to prove, and why?
- The new index doubles insert latency. What is the rollback, and what does it cost to redo later?
Model the follow graph for both of its opposite reads
follow_edge holds follower_id, followee_id, created_at_utc, removed_at_utc, edge_state ('active','unfollowed','pending_request','blocked') and is_reciprocal. account holds follower_count and following_count. Two reads matter: which accounts a viewer follows, bounded by that viewer's following count, and who follows an author, unbounded and heavy-tailed. Give the primary key and every secondary index you need, and say why neither read can use the other's index. State exactly what a re-follow does to the existing row, and which of follower_count and is_reciprocal may be trusted for an authorization decision.
Approach
- Primary key (follower_id, followee_id). A B-tree leading on follower_id turns the viewer's followee list into one range scan returning k rows for a following count of k, and the same index cannot answer a followee_id lookup: with the leading column unbound the planner's only options are a full scan of the index or of the heap, on the largest table in the system.
- Add the reverse index (followee_id, follower_id) for the publish path. Make it partial on edge_state = 'active', because soft-deleted rows stay forever and fanout never wants them, with the precondition that the query repeats that predicate literally or the planner will not match the partial index. The cost is a second index entry per edge plus maintenance on every state change.
- A re-follow updates in place, because the primary key forbids a second row: INSERT ... ON CONFLICT (follower_id, followee_id) DO UPDATE SET edge_state = 'active', removed_at_utc = NULL. Decide what created_at_utc means and say it out loud - keeping the first-follow time means one row cannot hold the follow/unfollow history, and if that history is required it belongs in an append-only follow_event table with this row as its current-state projection.
- follower_count is a cache reconciled from this table, so an authorization test phrased as a count reads a number rather than an edge. Whether a viewer may see a followers-only item is an existence check against follow_edge with edge_state = 'active', which the reverse index answers in one lookup.
- is_reciprocal is written by whoever inserts the opposite edge, so it is stale for the window between the two writes and permanently stale if that writer failed. It is a display hint; maintain it in the same transaction as the opposite edge or drop the column and answer reciprocity with a lookup on the second index.
Follow-up
- Blocks live in this table as edge_state = 'blocked'. What breaks when a block and a follow must coexist between the same pair, and would you split blocks out?
- The fanout worker has to page a 40-million-row follower list. What is the cursor, and why not OFFSET?
- Reconciliation finds follower_count off by 12 for one account. Write the query that finds the drift, and say whether the drift ever means the edge table is wrong.
Design an online auction platform utilizing message queues and caching…
Design an online auction platform utilizing message queues and caching layers to guarantee transactional consistency and mitigate race conditions during high-volume bidding.
Approach
- Choose a partition key and say what query it makes expensive.
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
Design a distributed Key-Value cache that supports timestamped keys, s…
Design a distributed Key-Value cache that supports timestamped keys, strictly increasing versioning, and thread-safe concurrent reads/writes.
Approach
- Name the failure you are designing for, then the recovery path.
- Choose a partition key and say what query it makes expensive.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
Explain the detailed step-by-step process of what happens at the OS an…
Explain the detailed step-by-step process of what happens at the OS and network layers when a user enters a URL into a browser, including DNS resolution, TCP handshakes, and SSL/TLS negotiation.
Approach
- Say what you would check first and why it is the highest-information step.
- Clarify what is being asked and what a complete answer contains.
- State your assumptions explicitly before working the problem.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
What is the difference between TCP and UDP? How does TCP handle conges…
What is the difference between TCP and UDP? How does TCP handle congestion control, flow control, and packet retransmission under lossy network conditions?
Approach
- Work from the requirement backwards to the design.
- Say what you would check first and why it is the highest-information step.
- State your assumptions explicitly before working the problem.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Specify a cursor pagination contract for a head-growing feed
Two callers read a viewer's home feed: an infinite scroller in the mobile app, and a nightly sync job that wants only what is new since its last run. The store is timeline_entry(owner_id, content_id, author_id, inserted_at_utc, sort_key, source), PRIMARY KEY (owner_id, content_id), INDEX (owner_id, sort_key DESC). New entries land at the head continuously, and the read path drops items the viewer may not see. Design GET /feed: parameters, cursor encoding and opacity, the response envelope, the end-of-feed signal, and what a client does when its cursor is rejected.
Approach
- Fix the ordering and the query first: WHERE owner_id = ? AND sort_key < :cursor ORDER BY sort_key DESC LIMIT n+1, served directly by (owner_id, sort_key DESC) so the page is a seek, not a sort. sort_key must be unique within owner_id — a timestamp collides at high insert rates and the boundary row is then either repeated or dropped, which is why it is the time-sortable content id.
- Fetch n+1 rows to decide has_more without a COUNT, and derive next_cursor from the last returned row rather than from the requested limit.
- Make the cursor opaque and self-describing: base64 of {version, sort_key, fingerprint of the filter parameters}, ideally signed. Opacity buys the freedom to change the encoding later; the fingerprint stops a cursor being replayed against a different filter, which silently skips or repeats rows. A mismatch is a typed 400, not a 500.
- Refuse to serve both callers with one direction. Paging older is a stable backward seek; getting what is new is a poll with since=sort_key, because the head moves between calls. One endpoint with an explicit direction parameter is fine; one endpoint that quietly changes meaning is a bug factory.
- Write the end-of-feed rule explicitly: the end is next_cursor = null, never 'fewer than limit items returned'. Visibility filtering runs after the seek, so short pages are normal and a client that stops on one loses the rest of the feed.
- State the guarantee and its limits: no duplicates and no skips among rows that existed when paging began; items inserted at the head afterwards are not returned by paging older; items deleted mid-scroll vanish without a signal; and a cursor pointing below a capped timeline's floor returns a typed error that resets the client to the head.
Worked solution 30 min
- Write the keyset query and, beside it, the index it must use, and mark which column order makes it a seek.
- Write the cursor's decoded contents as a JSON object and state which field defends against which failure.
- Draw a timeline of page 1, fifty insertions at the head, then page 2, and mark which rows each response contains.
- Write the full response envelope, including next_cursor, and the exact end-of-feed condition.
- Write the two error cases — stale cursor and filter mismatch — with their status and the client's recovery.
Follow-up
- The timeline is capped at a few hundred entries and the viewer scrolls past the floor — what does the contract say happens?
- How would you page a thread, where the order is by reply_path rather than by recency?
- Two devices share one account: can a cursor minted on one be used on the other, and what does that imply about what you put in it?
An hour of push notifications vanishes during a clock change
Push notification deliveries fell to near zero for exactly one hour last Sunday, then recovered with no deploy, no error rate change and no provider incident. Envelopes were still created during the window; the delivery worker counted them as duplicate suppressions. The dedup key is built from recipient_id, notif_kind, source_id and a bucket string formatted from the host's wall clock as YYYYMMDDHH24. Six months earlier an hourly aggregation job alerted because a day produced 23 buckets. Give the ordered checklist, the root cause, and the fix.
Approach
- Separate not-sent from suppressed before anything else. Envelopes were created and the worker incremented its duplicate-suppression counter, so nothing failed to be produced and nothing failed to be delivered; the system deliberately declined to send. That rules out the provider, the network and capacity in one step, and it means the bug lives in the dedup key rather than in the transport.
- Pin the window in UTC and convert it to the host's local zone. A gap of exactly one hour, on a Sunday, with no deploy, that lands on a 01:00 to 02:00 local boundary is a clock transition and almost nothing else; exact-hour boundaries do not arise from load or from rollouts.
- Read the key construction. The bucket is a wall-clock string, so at the autumn transition the local hour 01 occurs twice: 01:59:59 is followed by 01:00:00 again. Every key minted in the repeated hour is byte-identical to one already recorded in the first pass, the dedup store answers already-delivered, and an hour of notifications is suppressed with the worker reporting success. The spring alert is the same defect seen from the other side, where local hour 02 never occurs and the day yields 23 buckets instead of 24.
- Fix the key, not the formatter: derive the bucket from created_at_utc as an integer epoch division, floor(epoch_seconds / 3600), which is monotonic and never repeats. Formatting for display can stay local; the identity must not be derived from a representation that is not injective over time.
- Sweep for the same shape elsewhere before closing. Audit every column named with a utc suffix for writers that call a bare now() on a session whose timezone is not UTC, since the schema stores naive timestamps and the column name is a convention rather than a constraint. Check the notification coalescing key, the reconciliation job's window boundaries, and any retention job that deletes by a local-day boundary, because a repeated hour there deletes twice and a missing hour deletes nothing.
Follow-up
- The fix changes the key format, so keys minted before and after the deploy do not collide. What does the first hour after the deploy do, and is that acceptable for this notification kind?
- How would you have detected this in the six months between the two transitions, given that neither an error rate nor a latency metric moved?
- A user sets their own delivery quiet hours in their local zone. Which part of that computation is allowed to use a local clock, and which part is not?
For someone fluent in a dynamic language who has shipped real work but has never had to say what the runtime is doing underneath. The week is built on measuring and deliberately breaking things, because the questions that expose this background are the ones where the interviewer asks why a second time.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Recruiter screen and a coding baseline
- Write down your constraints (start date, location, work authorisation, competing deadlines) and your questions for the recruiter: team or organisation, whether the online assessment applies, allowed languages, and whether design is in scope at your level.
- Choose your interview language and, from a blank file, implement k-group linked-list reversal. Test k = 1, k equal to the list length, k larger than it, and a final group shorter than k.
- List every edge case you missed on the first run. That list becomes your checklist for the rest of the week.
Deliverable: A one-page recruiter-call sheet, plus a tested k-group reversal with your personal edge-case checklist.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Cache structures with O(1) operations
- Implement an LRU cache with a hash map and a doubly linked list. Then implement an LFU cache with a key-to-node map, a frequency-to-list map and a minimum-frequency value, matching the reported question.
- Write tests that catch eviction bugs: fill to capacity, insert one more item, and check which key was evicted. For LFU, also check the tie-break between keys with equal frequency.
- Add expiry to the LRU, as in the bank's expiring LRU question, and state what happens to an expired key that is never read again.
- Explain how you would make the LRU thread-safe and what a single global lock costs.
Deliverable: Working LRU and LFU implementations with eviction tests, plus a written note on thread safety.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Graphs, grids and sliding windows
- Solve the fuel-limited grid question with Dijkstra over (row, column, fuel remaining), where a recharge station resets fuel. State the O(mnF log(mnF)) bound.
- Start the zigzag grid question with the clarifying questions about revisits and allowed moves, then solve a version whose state graph is acyclic.
- Solve course-schedule feasibility with a topological sort, and merge overlapping intervals, both from the bank.
- Do this guide's sliding-window worked exercise on flagging actors who exceed a rate ceiling, and check the case where a burst straddles a bucket boundary.
Deliverable: Five solved problems, each with its state definition, its complexity and the edge case that nearly broke it.
Practice prompt ↗Practice prompt ↗04CS fundamentals for the phone interview
- Write the URL-to-page walk: DNS resolution, TCP three-way handshake, TLS negotiation, HTTP request and response, and where each step can fail.
- Write short answers on TCP versus UDP, congestion control versus flow control, and retransmission when packets are lost.
- Write short answers on MySQL clustered versus secondary indexes, B+ tree versus hash indexes for range queries, isolation levels and MVCC, and composite and covering indexes.
- Write short answers on Redis sorted sets (a skip list plus a hash table for large sets), how Redis persists data without blocking its main loop, and on deadlock and memory visibility between threads.
- Do this guide's SQL worked exercise on backfilling a live table, to connect index and lock theory to a real migration.
Deliverable: A page of fundamentals answers, with each weak point from a 'why? why?' drill with a partner marked for review.
Practice prompt ↗Practice prompt ↗Worked solution ↗05System design: concurrency and consistency
- Design the reported auction platform. Name the invariant for accepting a bid, where it is enforced, and how a message the queue delivers twice is made harmless.
- Design the reported versioned key-value cache. Explain how versions stay strictly increasing per key, how concurrent reads and writes stay safe, and how keys are partitioned.
- Design the global rate limiter: algorithm choice, where the counters live, and what happens when the counter store is unreachable.
- Do this guide's cursor pagination worked exercise to practise specifying an API contract exactly.
Deliverable: Three design write-ups, each with its invariant, the failure it is built to survive, and the recovery path, plus the pagination contract.
Practice prompt ↗Practice prompt ↗06Résumé deep dive and the final round
- Write one page per project with the numbers you will quote, how each was measured, and the alternative you rejected for each major choice.
- Answer the reported résumé questions in this guide's behavioral section out loud, and record yourself.
- Work the debugging drill on vanished push notifications, and practise telling it as an ordered investigation, the same shape as the outage question.
- Prepare questions for the hiring manager or HR partner about the team's systems and how its work is measured.
Deliverable: Project sheets, recorded answers to the résumé questions, and a list of questions for the final round.
Practice prompt ↗Practice prompt ↗07Mock loop across all four categories
- Run a mock phone interview: fundamentals questions first, then an unseen bank coding problem that you trace by hand before running.
- Run a mock design round on the reported question about top-k trending posts over sliding windows, with global and regional rankings.
- Run a mock résumé deep dive where the interviewer keeps asking follow-ups until you reach the limit of what you know, and note where that was.
- Update your edge-case checklist and fundamentals notes with what the mocks exposed.
Deliverable: Notes from three mocks, with your top three gaps and what you will do about each before the interview.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The research notes say both engineers and hiring managers question candidates in detail about their past work. Be ready to show which parts you drove and how they work. Pick two or three projects. Fix the numbers you will quote and how each was measured, and have the rejected alternative ready for every major choice.
Unblock an engineer seeing duplicate items on page two
An engineer on your team reports that users see the same items twice when they load page two of the feed. Their query is ORDER BY sort_key DESC LIMIT 40 OFFSET 40, and their proposed fix is to dedupe by content_id in the client. They are blocked, frustrated, and have been on it a day. Unblock them without taking the keyboard: get them to a reproduction, explain why the duplicates appear, why client-side dedup is the wrong layer, and what you would have them build instead.
Approach
- Get them to a deterministic reproduction before explaining anything. Insert k items at the head between the two requests and watch exactly k already-seen items reappear on page two. A reproduction they ran themselves teaches the mechanism; an explanation teaches them that you were right.
- Describe the cause as counting rather than as a bug. OFFSET defines a window by counting from the start of a result set that is mutating at the head, so k insertions above the window push k rows down into page two. Then show the half they have not seen: deletions shift the other way and skip rows entirely, with no signal to the client that anything was missed.
- Use the skip case to show why client dedup is the wrong layer. It suppresses the visible repeat and can do nothing about the invisible omission, because nothing ever told the client an item existed. Add the performance argument second: the database still produces and discards OFFSET rows, so page n costs more than page one even when no duplicate appears.
- Hand over the replacement with its precondition attached, because the precondition is where this bug comes back. Seek on the previous page's last key: WHERE owner_id = :owner AND sort_key < :cursor ORDER BY sort_key DESC LIMIT 40, served by the index on (owner_id, sort_key DESC). The key must be unique within the owner, or the boundary row is either repeated or dropped, which is why the sort key is the time-sortable content id rather than a timestamp that collides at high insert rates.
- Leave them the work and the proof: they write the query, the test that inserts at the head between page requests, and the plan check confirming an index scan rather than a sort. Then ask them to explain the skip case back to you, which is the only cheap way to find out whether the mentoring landed.
Follow-up
- What must the cursor encode once a page merges pushed timeline entries with items pulled from above-threshold authors?
- The product wants a 'new items above' indicator. How do you show what arrived above the cursor without breaking the page sequence?
- What test would have caught this before release, and why did the existing tests pass?
Argue against filtering blocks at write time instead of read time
Your tech lead proposes removing the per-read block check: the fanout worker would skip any follower the author has blocked, so the feed never has to look. It removes a lookup from the highest-QPS path in the system. You are asked to review the design document. Write the argument you would make: what the proposal genuinely buys, the single case that falsifies it, the second falsifier that is independent of the first, what you would concede, and the alternative you would put in its place with its own cost stated.
Approach
- Concede the gain first and size it, because an objection that will not state what the proposal buys reads as obstruction. A block lookup sits on every feed read at essentially full platform read QPS, so removing it is worth real capacity; say roughly how much before you argue.
- Falsify with one case rather than five: the block is created after the item was already fanned out. Write-time filtering cannot see an edge that does not exist yet, so by construction there is a window where the blocked viewer holds a materialized entry pointing at the blocker's item. Blocks are created reactively, so this is the common case, not the corner case.
- Give a second falsifier independent of timing: the feed is one of several surfaces. Thread, profile, search, notification and a raw deep link all reach the item without touching the timeline, so write-time filtering secures one path while the guarantee is the conjunction of all of them.
- Price the retrofit honestly instead of asserting it is impossible. Making write-time filtering sufficient means scanning the blocked viewer's timeline at block creation and removing matching entries, which is bounded by the timeline cap and therefore tractable per block, but it must complete before the viewer's next read and it does nothing at all for the other surfaces.
- Put a costed alternative in its place: keep enforcement on the read path and make it cheap. Fetch the viewer's block set once per request rather than per item, filter against the denormalized timeline_entry.author_id so blocked authors are dropped without hydrating anything, and cache the set per viewer with write-through invalidation on block creation plus a short TTL as a backstop. State the staleness bound out loud, since a stale block cache is a correctness failure and not a stale count.
- Close with the concession that makes the proposal survive in a useful form: write-time filtering is acceptable as an optimization layered on top of the read filter, where it reduces the rows the filter must drop and is allowed to be wrong.
Follow-up
- A page holds 40 items from 30 authors. Where exactly does the block check run so it is one lookup per request rather than 30?
- Your lead says the exposure window is a few seconds and therefore acceptable. What evidence would change your mind, and what evidence would change theirs?
- How do you prove the guarantee holds on every surface, and what test fails if someone adds a seventh surface next quarter?
Choose which feed debt ships and which blocks the launch
Two weeks to launch, three known defects. First, like counts are maintained from an at-least-once stream with no dedup and no reconciliation job, so they drift upward on redelivery. Second, the per-viewer block set is cached with no invalidation on block creation, so a new block can take up to an hour to take effect. Third, hydration issues one lookup per item, so a 40-item page costs 40 sequential round trips instead of one batched read, and its latency grows linearly with page size. Decide what ships and what blocks the launch, how each is written down, and the rollback for anything you ship.
Approach
- Sort by which guarantee each defect gives up, not by how long each takes to fix. The first gives up numeric exactness, which this system already declares approximate between reconciliation runs. The second gives up a safety property the product states to users in absolute terms. The third gives up latency headroom.
- Block the block cache and say why in one line: a stale block set is a correctness failure with a named victim on a guarantee the product makes without qualification, and the fix is small, being write-through invalidation on block creation plus a short TTL as a backstop. A cheap fix to a safety defect is not debt, it is work you have not done yet.
- Ship the counter drift with two conditions written into the launch note: a stated bound on acceptable drift, and the rule that no authorization or eligibility decision may read the denormalized count. Schedule the reconciliation job from the engagement table with an owner and a date, and add a drift alarm so the bound is measured rather than assumed.
- Ship the hydration cost behind a measured limit rather than a hope. The defect is serial round trips, so the page's added latency is the page size times the per-lookup RTT until someone batches the fetch; cap the page size, measure p99 with realistic fan-in instead of a warm single-author page, and make the rollback a page-size config change rather than a deploy, so the person paged at 3am can act without a build.
- Write each item where the next on-call will read it, with an owner, a date and a trigger that fires without a human remembering, such as the drift alarm or a p99 threshold. Debt with a trigger gets paid; debt in a retrospective document does not.
- Say plainly what you told the people who wanted all three shipped: the deadline can buy scope, and it cannot buy a guarantee the product already promised.
Follow-up
- Product argues a one-hour block delay is fine because blocks are rare. What is your answer, and does the rarity argument change anything?
- Reconciliation runs and finds 4% drift rather than the 0.1% you assumed. What do you do about the numbers already shown to users?
- Which of the three do you fix first after launch, and why is it not simply the slowest one?
- 01
Walk through the most complex system architecture you designed in a recent role. What were the bottleneck constraints, and why did you choose your tech stack over the alternatives?
- 02
Describe a critical production outage or edge-case failure in one of your projects. How did you triage, investigate and mitigate it?
- 03
How did you set up, track and validate stability and latency SLA metrics for the backend services you managed?
- 04
Describe a time you optimised a slow query or a bottlenecked service. Which tools and profiling techniques did you use?
- 05
Describe an architectural disagreement with a senior teammate or manager. How did you validate your approach with metrics or benchmarks?
- 06
If you have migrated a service from a monolith to microservices, what was the data migration strategy, and how did you ensure zero downtime?
Is this an official ByteDance interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at ByteDance. The rounds and questions reflect what candidates have reported, not a process ByteDance has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult are the live coding questions?
Candidate reports in this guide's research describe the coding questions as medium to hard. They emphasise algorithmic efficiency, matrix traversal, dynamic programming and string manipulation, and your code is expected to run during the session. Practise writing complete, runnable solutions and your own test inputs, not pseudocode.
PracHub interview research ↗How long does the process take from screen to offer?
The research notes give roughly four to six weeks for the five stages, and elsewhere three to six weeks from screen to offer. Feedback timing varies with team matching, time zones and holidays. If you have a competing deadline, ask the recruiter for the expected timeline at the first call.
PracHub interview research ↗Does every candidate take the online assessment?
No. Candidates report either taking an online assessment or going straight to a technical phone interview. Ask your recruiter which applies to you. Prepare the same coding material either way, since the phone interview also includes live coding.
PracHub Software Engineer practice ↗Is Mandarin required for Software Engineer roles?
According to the research notes, no. English is the working language in international offices such as the US, UK, Singapore and Canada. Some teams work with Mandarin-speaking engineering hubs, and Mandarin is listed as a nice-to-have for certain cross-regional teams. If a particular team matters to you, ask the recruiter.
PracHub interview research ↗What coding environment is used in live interviews?
Candidates describe video interviews with a shared web-based code editor, such as a Lark code pad. You can usually choose your language and run code against your own test inputs. Practise writing and running your own test cases, and tracing variables by hand, since the notes say candidates are sometimes asked to trace before running.
PracHub interview research ↗What should I study first?
The research notes advise starting CS theory and system design early instead of focusing only on live coding. Cover all four categories: algorithms and data structures, system design, CS fundamentals (networking, database internals, operating systems, Redis), and deep dives into your own projects. Prepare in the language you know best, plus the middleware you have actually used, such as Redis, Kafka or a relational database.
PracHub Software Engineer practice ↗Can I reapply if I do not pass?
The research notes say candidates can reapply after a waiting period, typically six months. Confirm the current policy with your recruiter. Spend the time on the specific gaps the loop exposed, whether coding speed, CS theory or design depth.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24