Interview guides · Classdojo
Classdojo Software Engineer Interview Guide
Classdojo Software Engineer interview guide: the 3 reported rounds, coding, system design and behavioral questions, with answer outlines and a 7-day plan.
The 60-second brief
Classdojo is an education technology company whose products carry communication between teachers, students and parents. Candidates describe the Software Engineer role as hands-on product engineering: building, testing and maintaining applications, writing and reviewing code, debugging existing systems, helping shape the design of new features, and working beside product managers, designers and other engineers. The requirements candidates list name JavaScript, Python or Ruby, web development frameworks, and familiarity with cloud services, SQL and NoSQL databases as a plus.
This guide is for engineers preparing for the Classdojo Software Engineer loop. It covers the three rounds candidates report, the reported coding, system design, debugging and behavioral questions with answer outlines you can adapt, and a one-week schedule. It assumes you can already program; the aim is to turn that skill into clear, structured answers.
Candidates report three rounds over roughly three to five weeks: a Preliminary Screening, Technical Interviews that cover coding and system design, and a Behavioral Evaluation.
Focus areas
- System design
- Coding interviews
- JavaScript
Overview
The reported question set is broad rather than deep in any one area. It mixes classic coding problems (reversing a linked list, binary search, sorting and searching an array, dynamic programming), system design prompts (a real-time messaging system, API design for a new service, microservices versus a monolith), discussion questions about quality, patterns, REST versus GraphQL and version control, and a block of behavioral prompts. A good plan gives each of those five groups some time instead of putting everything into algorithm practice.
The product connects teachers, students and parents, and the listed stack is JavaScript, Python or Ruby with web frameworks. That makes the real-time messaging question and the API design question natural places to practise: think about who sends and receives messages, how delivery works when a client is offline, how a retried request avoids duplicate messages, and what a REST or GraphQL contract would look like for a feed of messages. Treat these as design exercises you can reason through from first principles, not as claims about how Classdojo built its own systems.
Candidates also report troubleshooting prompts: optimising a slow application, working through a system outage, and describing a hard bug you fixed. Prepare a repeatable sequence for each (measure first, narrow the cause, fix, then prevent recurrence) and keep one or two real incidents from your own work ready to illustrate it. Several prompts ask for your own experience, so the strongest preparation is a short list of projects you can describe in detail, including your decisions, the trade-offs and the results.
Finally, the technical rounds are reported to involve coding on a whiteboard or in a coding environment, so practise both: writing code without autocomplete, then saying the time and space complexity out loud. Whichever language you choose, make sure you can write it fluently and explain its behaviour; JavaScript is the language most visible in the reported topics.
The Classdojo interview process
Candidates describe three stages. The descriptions below say what is reported for each and how to prepare for that format.
Preliminary Screening
Candidates report that the process opens with a screening conversation led by a recruiter, and the reported topics refer to a recruiter plus tech screen flow. Treat it as the first filter: you are introducing yourself and your fit for a Software Engineer role. Expect to explain your background, the languages you work in and why this role interests you, and to ask your own questions about the process.
What to demonstrate
- Overall fit for the Software Engineer role as a recruiter sees it
- A clear, short account of your background and the languages you use
- Interest in the role and in a product used by teachers, students and parents
How to prepare
- Write a short spoken summary of your recent work that names your main language (JavaScript, Python or Ruby are the ones listed) and one project you can discuss in detail.
- Read about the Classdojo product and prepare two or three questions of your own about the team, the stack and the next steps.
- Keep a concise answer ready for why you want to work in education technology, based on your genuine reasons rather than a rehearsed slogan.
- Rehearse a brief answer on your experience with version control, since that discussion question is among the reported ones.
Technical Interviews
Candidates report interviews with engineering leads that focus on coding ability and system design, with problems solved on a whiteboard or in a coding environment. The reported coding questions are classic ones: reversing a linked list, implementing binary search, a sorting-and-searching problem on an array, a dynamic programming approach and the time and space complexity of your solution. The reported design questions include a real-time messaging system, a high-traffic system you built, data storage and retrieval, API design for a new service and microservices versus a monolith. Candidates also report discussion questions on REST versus GraphQL, code quality, design patterns, debugging, optimising a slow application and troubleshooting an outage.
What to demonstrate
- Writing correct code unaided, then stating its time and space complexity
- Designing a system or API from requirements, with the trade-offs named
- Debugging and performance reasoning on a slow application or an outage
- Explaining past technical work and the design choices behind it
How to prepare
- Write linked-list reversal (iterative with previous, current and next pointers) and binary search (including the boundary conditions) by hand until they come out right without running them.
- Practise one array problem that combines sorting and searching, and one dynamic programming problem written as recursion with memoisation and then as a table.
- Outline a real-time messaging design from scratch: connection layer, message store, delivery to offline users, ordering and retries. Then write an API contract for a small service, once as REST resources and once as a GraphQL schema.
- Prepare a debugging sequence for a slow application and for an outage, plus one real bug from your own work told in order: symptom, hypothesis, evidence, fix, prevention.
Behavioral Evaluation
The last reported stage is a Behavioral Evaluation, described by candidates as an assessment of behavioral responses to judge fit within the team. The reported prompts cover working with a difficult team member, prioritising across several projects, mentoring a junior developer, handling failure or setbacks, and what motivates you. Prepare stories that show your own decisions and results, because a prompt such as mentoring or setbacks is answered with a specific episode from your career.
What to demonstrate
- How you describe working with teammates, including disagreements
- Whether your stories show your own decisions and measurable results
- How you describe setbacks, mentoring and prioritisation in real situations
How to prepare
- Write five stories in situation, action, result form: a conflict with a colleague, a prioritisation trade-off, mentoring a junior engineer, a failure you owned, and a project you are proud of.
- For each story, note the number or outcome that shows the result, and one thing you would change now.
- Rehearse the stories aloud so each runs to a short, structured answer, with the reasoning given more space than the background.
- Prepare a sincere, specific answer on what motivates you, with a past example that proves it.
Where candidates lose points
Writing linked-list reversal or binary search from memory with an off-by-one error and never testing it
After writing, trace a list of zero, one and two nodes, or an array where the target is first, last or missing. For binary search, decide up front whether the bounds are inclusive and keep that rule in every branch.
Giving the complexity of a solution as a bare label, or skipping the time and space discussion
Say the cost of each step and the total, including the extra space such as recursion depth or a memo table. Compare it with the brute-force version and explain what you traded.
Answering the real-time messaging or API design question with a list of technologies and no structure
Start by fixing scope and the users, then walk through the request path: clients, connection handling, the message store, delivery to offline users, ordering and retries. Name one trade-off at each layer and say what fails first as load grows.
Treating REST versus GraphQL or microservices versus a monolith as a contest with a winner
Give the decision criteria: client data needs, over- and under-fetching, caching, team size, deployment independence and operational cost. Then say which you would choose for a stated situation and why.
Behavioral stories that say "we" throughout, have no result, or blame the other person
For every story, state your own decision, the action you took and a concrete outcome. Describe setbacks and conflicts in terms of behaviours and process, and end each with what changed afterwards.
Questions & practice
How would you approach a project with ambiguous requirements?
Medium · Problem-Solving / Case Studies
Approach
- Start by listing the unknowns, then separate those that shape the architecture (data model, scale, integrations) from details that are cheap to change later.
- Go to the people involved: what problem the work solves, who uses it, and how success will be measured. Write a one-page spec with goals, non-goals and stated assumptions, and get agreement on it.
- Build the smallest version that tests the riskiest assumption, such as a prototype or a feature-flagged release to a small group. Keep early decisions reversible: clear interfaces, no premature abstraction.
- Iterate in short checkpoints. When the scope or an assumption changes, state the trade-off (time, scope, quality) and update the spec.
- Anchor it in one real story: the ambiguity, the question that resolved it, and what shipped as a result.
Follow-up
- Two stakeholders want contradictory things. How do you resolve it?
- How do you decide you have clarified enough and should start building?
- Tell me about an assumption that turned out wrong and what it cost.
Write a function to reverse a linked list.
Medium · Coding / Algorithms
Approach
- Iterative solution: prev = null, curr = head. In the loop, save next = curr.next, set curr.next = prev, then move prev = curr and curr = next. Return prev. O(n) time, O(1) extra space.
- Recursive solution: reverse the rest of the list, then set head.next.next = head and head.next = null, and return the new head from the base case. O(n) time but O(n) call-stack space, which can overflow on very long lists.
- Edge cases: empty list, a single node, and two nodes. Make sure the old head's next becomes null, otherwise the result contains a cycle.
- Test by walking the returned list and comparing the values to the reversed input. Mention the variants: reverse between positions m and n, reverse in groups of k, and swapping prev and next pointers in a doubly linked list.
Follow-up
- Reverse only the nodes from position m to position n in one pass.
- Reverse the list in groups of k nodes.
- Check whether a linked list is a palindrome using O(1) extra space.
How would you implement a binary search algorithm?
Medium · Coding / Algorithms
Approach
- State the precondition: the array is sorted, or more generally the predicate is monotone. Closed-interval version: lo = 0, hi = n - 1, while lo <= hi, compute mid, compare, then set lo = mid + 1 or hi = mid - 1. Return -1 when not found.
- Compute mid as lo + (hi - lo) / 2 (integer division) to avoid overflow in fixed-width integers (Java, C++). In JavaScript, use Math.floor((lo + hi) / 2) or (lo + hi) >>> 1. O(log n) time, O(1) space when iterative.
- Lower-bound variant: half-open [lo, hi) with while lo < hi. If a[mid] < x then lo = mid + 1, else hi = mid. It returns the first index with a[i] >= x, which handles duplicates and first or last occurrence.
- Binary search on the answer: when feasibility is monotone in a value (minimum capacity to ship within D days, integer square root), search over the value range instead of indices.
- Edge cases: empty array, one element, target below the minimum or above the maximum, duplicates. Infinite loops come from setting lo = mid when mid rounds down.
Follow-up
- Search for a target in a rotated sorted array.
- Find the first and last positions of a target in a sorted array with duplicates.
- How would you search a sorted array whose length you do not know?
Solve a problem involving sorting and searching in an array.
Medium · Coding / Algorithms
Approach
- Clarify the input: whether it is already sorted, its size, duplicates, the value range, negative numbers, and whether you may modify it in place.
- Pick the pattern. Sort then use two pointers (pair sum on sorted input is O(n) after an O(n log n) sort, 3-sum is O(n^2)). Sort then binary search. Use a hash map for O(n) average lookups on unsorted data, at the cost of O(n) memory. Sort intervals by start, then merge.
- Know the sorts and what decides between them. Comparison sorting needs Ω(n log n) comparisons. Merge sort is stable and uses O(n) extra space. Quicksort averages O(n log n), is O(n^2) in the worst case and sorts in place. Heapsort uses O(1) extra space and is not stable. Counting or radix sort runs in O(n + k) for bounded integers.
- Language traps: JavaScript's default Array.prototype.sort compares elements as strings, so pass (a, b) => a - b for numbers (the sort has been stable since ES2019). Python's sorted uses Timsort, which is stable and fast on nearly sorted input.
- For the kth largest element, use quickselect (O(n) average, O(n^2) worst) or a min-heap of size k (O(n log k)).
Follow-up
- Find the kth largest element without fully sorting the array.
- Merge two sorted arrays when the first has room at its end for the second.
- Count the pairs whose difference equals k.
Discuss your approach to dynamic programming problems.
Medium · Coding / Algorithms
Approach
- Recognize the shape: optimal substructure plus overlapping subproblems. Typical signals are counting ways, minimum or maximum cost, or yes/no feasibility over prefixes, intervals or subsets.
- Follow a fixed sequence: define the state in words (dp[i] = best answer for the first i items), write the recurrence, set base cases, choose an evaluation order so dependencies are computed first, and say where the final answer sits.
- Write top-down memoization first (recursion plus a cache), then convert to bottom-up tabulation. Reduce space when a row depends only on the previous one: climbing stairs needs O(1), and 0/1 knapsack fits in one array if capacity is iterated downward so each item is used at most once.
- Complexity is the number of states times the cost per transition: LCS O(mn), coin change O(n * amount), LIS O(n^2), or O(n log n) with tails plus binary search.
- To return the actual solution, not just its value, store the choice made at each state and walk back from the answer.
Follow-up
- Why must capacity be iterated downward in the one-array 0/1 knapsack?
- Convert your memoized solution to bottom-up and reduce its space.
- How would you recover the actual subsequence, not just its length?
Can you explain the time and space complexity of your solution?
Medium · Coding / Algorithms
Approach
- Time: count the dominant operation as a function of input size and drop constants and lower-order terms. Two nested loops over the same input are O(n^2), halving the range each step is O(log n), and a comparison sort is O(n log n).
- Space: separate auxiliary space from the input. Recursion depth counts, so a recursive tree traversal uses O(h) stack, and a hash map of all elements uses O(n).
- Use amortized and average cases precisely. Appending to a dynamic array is O(1) amortized because capacity doubles. Hash map operations are O(1) on average and O(n) in the worst case under heavy collisions.
- Watch for hidden costs: repeated string concatenation in a loop can be O(n^2), Python list slicing copies O(k) elements, JavaScript array shift is typically O(n), and membership in a list is O(n) versus O(1) average in a set.
- For recursion, use states times work per state when memoized, and the Master theorem for divide and conquer: T(n) = 2T(n/2) + O(n) gives O(n log n).
Follow-up
- What is the time complexity of naive recursive Fibonacci, and what does memoization change?
- Why is appending to a dynamic array O(1) amortized?
- Can you reduce the space your solution uses, and what does that cost in time?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Explain the differences between REST and GraphQL.
Medium · Technical / Domain Questions
Approach
- Start with the model. REST exposes resources at URLs, uses HTTP verbs and status codes, and each endpoint returns a shape the server defines. GraphQL exposes one typed schema, usually at a single POST endpoint, and the client chooses the fields it wants through queries, mutations and subscriptions.
- Name the problem GraphQL addresses. Fixed REST responses can return more data than a screen needs (over-fetching) or force several round trips (under-fetching). A GraphQL query can fetch an entity and its related data in one request.
- State what GraphQL costs. Plain HTTP and CDN caching of GET responses stops working, and persisted queries sent over GET bring some of it back. Per-field resolvers cause N+1 database calls unless you batch them (DataLoader). You need depth and cost limits on queries. Errors often arrive in an errors array with HTTP 200, so monitoring has to read the response body.
- Compare how each evolves. REST usually versions through the path (/v2) or headers. GraphQL adds fields and marks old ones with @deprecated, so clients that never request a field are unaffected.
- Close with a decision rule. Use REST for simple, cacheable CRUD and public APIs. Use GraphQL when web and mobile clients need different slices of a graph of related entities.
Follow-up
- How would you stop a client from sending a deeply nested query that overloads the database?
- A list of posts resolves each author with a separate query. How do you fix that N+1 problem?
- How would you get HTTP caching back for a GraphQL API?
How do you ensure code quality and maintainability?
Medium · Technical / Domain Questions
Approach
- Start with automated gates in CI that run on every pull request: a formatter and linter for the language in use (Prettier and ESLint for JavaScript, Black or Ruff for Python, RuboCop for Ruby), a type checker where one exists (TypeScript, mypy), and the test suite. Style then never comes up in review.
- Describe a test pyramid. Many fast unit tests on pure logic, fewer integration tests at the database and HTTP boundaries, and a few end-to-end tests on the main user flows. Tests assert behaviour rather than implementation details, and coverage is a signal, not a target.
- Make code review concrete: small pull requests, tests included with every change, and reviewers spending their attention on naming, error handling and design because tooling already covers formatting.
- Explain how you design for change: small modules with clear interfaces, dependencies injected so they can be faked in tests, dead code deleted, and comments that explain why rather than what. Refactor in small steps behind passing tests.
- Say how you would know quality is improving, for example escaped defects, change failure rate, or time to review and merge.
Follow-up
- A legacy module with no tests needs a change tomorrow. What do you do first?
- How would you get a team that resists linting rules to adopt a shared standard?
- When would you accept lower test coverage?
Describe a challenging bug you encountered and how you resolved it.
Medium · Technical / Domain Questions
Approach
- Pick a bug whose cause was not obvious, such as a race condition, a stale cache, a timezone or encoding error, or a memory leak. A typo makes a weak story. Open with the symptom in numbers: who was affected, how often, and since when.
- Walk through the debugging sequence in order. How you reproduced it, or why you could not. How you narrowed it with logs, metrics or traces. How you bisected commits (git bisect) or inputs. Which hypothesis you tested with a single controlled change.
- State the root cause precisely, for example: two requests read the same row and both wrote back without a lock or version check. Explain why your first theory was wrong.
- Cover the fix and the prevention: a regression test that fails without the fix, monitoring or an alert that would catch a recurrence, and how you confirmed the fix in production, not only on your machine.
- Keep the focus on your own reasoning and actions. Avoid blaming another team or skipping how you verified the result.
Follow-up
- How did you confirm the fix worked in production?
- What would have caught this bug before it shipped?
- If you had never reproduced it, what would you have done next?
What design patterns do you commonly use, and why?
Medium · Technical / Domain Questions
Approach
- Name three or four patterns you have actually shipped, each with its context. Strategy: one notification interface with email, SMS and push implementations. Adapter: a wrapper around a third-party SDK so the vendor can be swapped and mocked in tests. Observer or pub/sub: event emitters or domain events. Decorator or middleware: an auth and logging chain in an HTTP framework.
- For each, say what problem it solved and what it cost: extra indirection, more files, and a harder-to-follow call path. Say you add a pattern when a second variant appears, not before.
- Show you know the language. In JavaScript and Python, functions are first-class, so Strategy is often just passing a function. Module scope already gives a single instance, but singletons hide global state and make tests harder to isolate.
- Mention composition over inheritance. Deep class hierarchies are hard to change, while small objects wired together through interfaces can be recombined.
Follow-up
- Tell me about a time a pattern made the code worse.
- How do you test code that depends on a singleton?
- What is the difference between Adapter and Facade?
How would you design a real-time messaging system?
Medium · System Design / Architecture
Approach
- Fix the requirements first: one-to-one and group chat, message history, delivery and read receipts, presence, and push notifications for offline users. State scale numbers as your own assumptions. Make ordering per conversation, not global.
- Connections: clients hold persistent WebSocket connections to a gateway tier, with heartbeats to detect dead sockets and long polling as a fallback. A connection registry (user id to gateway node, for example in Redis) tells the system where each online user is connected.
- Send path: the client sends the message with a client-generated id. The chat service deduplicates on that id, persists the message with a per-conversation sequence number, and publishes it on pub/sub (Redis pub/sub or Kafka). Gateways push it to connected recipients. Offline recipients get a push notification and, on reconnect, fetch everything after their last seen sequence number.
- Storage: key messages by (conversation_id, sequence). A wide-column store such as Cassandra suits very high write volume, and partitioned Postgres is simpler at moderate scale. Large groups favour fan-out on read over fan-out on write.
- Guarantees: at-least-once delivery with client acks and id-based deduplication, so effectively exactly-once to the user. Add rate limiting and access checks on who may message whom.
Follow-up
- How does the design change for a group with 10,000 members?
- A gateway node holding many connections crashes. What happens to those users and their in-flight messages?
- How would you implement read receipts without writing a row per message per member?
Describe a system you designed that had to handle significant traffic.
Medium · System Design / Architecture
Approach
- Open with numbers: peak requests per second, data volume, read/write ratio, and the latency target (p95 or p99) before and after your work. Without numbers, significant traffic is just a claim.
- Sketch the architecture briefly: load balancer, stateless application tier that scales horizontally, cache in front of the database (cache-aside with TTLs), read replicas, and a queue for work that does not need to finish within the request.
- Name the bottleneck you found and how you measured it: profiling, the slow query log, EXPLAIN plans, or saturation metrics. Then name the one design change that removed it and the measured result.
- State the trade-offs you accepted: stale reads from cache or replica lag, extra cost, more operational complexity. Explain how you limited their impact, for example reading your own writes from the primary.
- Finish with how you load tested the design, what failed under load, and what you would change now.
Follow-up
- After your fix, what became the next bottleneck?
- How did you prevent a cache stampede when a hot key expired?
- How did you load test the system, and how realistic was the traffic?
What considerations do you take into account for data storage and retrieval?
Medium · System Design / Architecture
Approach
- Start from access patterns before naming a database: read/write ratio, query shapes (lookup by key, range scan, full-text search, aggregation), data size and growth, consistency needs, and latency targets.
- Match the store to the pattern. Relational (Postgres, MySQL) for transactions, joins and constraints. Key-value or document stores (DynamoDB, MongoDB) for high-volume access by a known key. A search index (Elasticsearch) for text. Object storage (S3) for files. Redis for hot reads and counters.
- Indexing: B-tree indexes serve equality and range queries. In a composite index, column order matters because only a leftmost prefix can be used. Covering indexes avoid table lookups. Every index slows writes and costs storage, so check queries with EXPLAIN.
- Scaling: read replicas (and their replication lag), partitioning or sharding by a key (watch for hot keys and cross-shard queries), and caching with an explicit invalidation plan (cache-aside, TTL, delete on write).
- Lifecycle and safety: backups and point-in-time recovery, retention and deletion rules, encryption at rest, and access control for personal data such as records about students and parents.
Follow-up
- When would you denormalize, and how do you keep the copies consistent?
- How do you choose a shard key, and what do you do about a hot key?
- How would you change the schema of a large, busy table without downtime?
Explain how you would approach designing an API for a new service.
Medium · System Design / Architecture
Approach
- Start from the consumers and their use cases, then model resources and operations. Choose the style deliberately: REST for broad or public clients, GraphQL for varied client data needs, gRPC for internal service-to-service calls.
- Write the contract before the code, for example as an OpenAPI spec, and review it with consumers. Cover resource naming, methods, and specific status codes (201, 400, 401/403, 404, 409, 429) with one consistent error body.
- Collections need pagination, filtering and sorting. Prefer cursor pagination for large or changing data, because offset pagination slows down on deep pages and skips or repeats rows when data changes. POST that creates things takes an idempotency key so retries are safe. PUT and DELETE are idempotent by definition.
- Security and limits: authentication (OAuth 2.0 or tokens), authorization checks on each resource, input validation, and rate limiting.
- Plan for evolution and operations: additive changes stay backward-compatible, breaking changes go to a new version with a deprecation window, and every request carries an id that shows up in logs, metrics and traces.
Follow-up
- Why choose cursor pagination over offset pagination here?
- How would you make a create request safe to retry after a network timeout?
- How would you roll out a breaking change to an API with existing clients?
Discuss the trade-offs between microservices and monolithic architectures.
Medium · System Design / Architecture
Approach
- Monolith: one deployable, in-process calls, one database with real transactions, and simple local setup and debugging. Costs: every change ships together, the whole app scales as one unit, and coupling grows with the codebase and the team.
- Microservices: each service deploys and scales on its own, teams own clear pieces, and failures stay contained. Costs: network latency and partial failures, no cross-service transactions (you need sagas or the outbox pattern), eventual consistency, distributed tracing, and much more operational work.
- Decision drivers: how many teams work on the code, whether domain boundaries are stable and well understood, and whether components have very different scaling or reliability needs. Conway's law applies: service boundaries tend to follow team boundaries.
- Middle path: a modular monolith with enforced module boundaries. Extract a service only when a boundary has proven stable, using the strangler fig pattern to route traffic over gradually.
- Data ownership: each service owns its database. Services sharing one database recreate the coupling microservices were meant to remove.
Follow-up
- How do you keep data consistent across two services without a distributed transaction?
- Which part of a monolith would you extract first, and why?
- How do you debug a slow request that crosses five services?
How would you approach optimizing a slow-running application?
Medium · Problem-Solving / Case Studies
Approach
- Define slow precisely: which page or endpoint, whether it is p50 or p95/p99, since when, and for which users. Record a baseline measurement before changing anything.
- Locate the time. Use tracing or an APM tool to split a request between application code, database, network and external calls. On the frontend, use Lighthouse, Core Web Vitals and bundle analysis to find render-blocking scripts and oversized bundles.
- Check the usual causes in order: N+1 queries, missing indexes (confirm with EXPLAIN ANALYZE), unbounded result sets, synchronous calls to slow dependencies, repeated work that could be cached, poor algorithmic complexity in a hot loop, and memory pressure or garbage collection pauses.
- Change one thing at a time and re-measure against the baseline. Typical fixes: add an index, batch queries, paginate, cache with a clear invalidation rule, or move work to a background queue.
- Prevent regressions with performance budgets in CI and alerts on p95 latency.
Follow-up
- A query is fast in staging and slow in production. What could explain that?
- How would you find a memory leak in a Node.js service?
- When can adding a cache make things worse?
If given a dataset, how would you analyze it to find actionable insights?
Medium · Problem-Solving / Case Studies
Approach
- Clarify the decision the analysis has to inform and the metric that matters. Then learn the data's grain (what one row is), its time range and how it was collected.
- Check data quality before any conclusions: nulls, duplicates, outliers, timezone handling, test or bot accounts, and whether row counts match a known total.
- Explore with SQL or pandas group-bys: distributions, trends over time, and segments such as cohort by signup week, platform or region. Many insights are a difference between segments, not an overall average.
- Keep correlation and causation apart. Name likely confounders, watch for Simpson's paradox when segment mix shifts, and recommend an A/B test when the claim has to be causal.
- Present each insight as finding, size and recommended action, with one clear chart and how you would confirm the effect after the change ships.
Follow-up
- A key metric dropped 10% week over week. How do you find out why?
- How would you check that a difference between two segments is not just noise?
- What do you do when the data needed to answer the question is missing?
Explain a complex technical problem you solved and the impact it had.
Medium · Problem-Solving / Case Studies
Approach
- Pick a problem where the difficulty was technical, such as concurrency, scale, a risky data migration or performance. Open with a one-sentence summary, then add detail in layers so the listener can follow.
- State the constraints and the options you weighed: at least two real alternatives and the reason you chose one, framed as a trade-off rather than a preference.
- Make your own part explicit. Say I for your decisions and code, and credit the team for theirs.
- Quantify the impact (latency, error rate, cost, hours saved, user-facing metrics) and say how you separated your change's effect from other changes shipped at the same time.
- End with what you would do differently and what the system or team does now because of the work.
Follow-up
- Which alternative came closest, and why did you reject it?
- How did you measure the impact, and how confident are you in that number?
- If you rebuilt it today, which part would you design differently?
Describe your process for troubleshooting a system outage.
Medium · Problem-Solving / Case Studies
Approach
- Triage first: confirm the impact (error rate, which users, which features), open an incident, and split roles. One person coordinates and communicates while others investigate. Post status updates on a regular cadence.
- Ask what changed: recent deploys, config or feature-flag changes, infrastructure events, upstream dependency status, traffic spikes. Line up the onset time on dashboards against those events.
- Mitigate before finding the root cause: roll back, turn off the flag, fail over, scale out, or shed load. Restoring service comes before understanding it.
- Then diagnose layer by layer (DNS, load balancer, application, database, third parties) using logs, traces, and RED metrics (rate, errors, duration) for services or USE metrics (utilization, saturation, errors) for resources. Separate the trigger from the underlying cause.
- Close with a blameless postmortem: a timeline, the root cause and contributing factors, action items with owners, and the alert that would have caught the problem earlier.
Follow-up
- The rollback did not help. What do you check next?
- How do you tell whether the database is the cause of an outage or a victim of it?
- What would you put on an on-call dashboard for this service?
Suggested practice checklist
The week follows the three reported rounds: coding fundamentals first, then system design and troubleshooting, then discussion questions, then behavioral stories. Each day ends with something written that you can reuse in the interview.
Day 1 — Map the loop and inventory your material
- Write out the three reported rounds (Preliminary Screening, Technical Interviews, Behavioral Evaluation) and, under each, the question types you are least ready for.
- List four or five projects from your own work that you can describe in depth, with the language, your role and a measurable result for each.
- Write a short spoken introduction for the recruiter screen that names your main language and your most relevant project.
Deliverable: A one-page loop map, a project inventory and a written recruiter-screen introduction.
Day 2 — Coding: linked list, binary search, sorting and searching
- Write linked-list reversal both iteratively and recursively; state the time and space cost of each and test on empty, one-node and two-node lists.
- Write binary search, then a variant that finds the first position of a value; test on duplicates, missing targets and single-element arrays.
- Solve one array problem that needs sorting and then searching, and state its complexity, including the sort.
Deliverable: Working, hand-written code for the linked-list, binary search and sort-and-search problems, each with a complexity statement and a list of test cases.
Day 3 — Dynamic programming and JavaScript fundamentals
- Take one dynamic programming problem and solve it three ways: plain recursion, recursion with memoisation, and a bottom-up table. Write the state definition and recurrence before any code.
- Review the JavaScript behaviours that come up in coding discussions: closures, how this is bound, promises with async and await, and the event loop.
- Write one short JavaScript snippet for each topic and explain what it prints and why.
Deliverable: A dynamic programming solution in three forms with its state and recurrence written out, plus four short JavaScript snippets with explanations.
Day 4 — System design: messaging and API design
- Outline a real-time messaging system: clients and connections, message store, delivery to offline recipients, ordering, retries and what happens when a component fails.
- Design the API for a small new service twice, as REST resources and as a GraphQL schema, and list where each fits better.
- Write the data storage choices for the messaging design: which store holds messages, what the key is and which queries it makes expensive.
Deliverable: A messaging design outline, two API contracts for the same service and a short storage decision note.
Day 5 — Scale, architecture trade-offs and troubleshooting
- Write a comparison of microservices and a monolith with the criteria you would use and the situation in which you would pick each.
- Prepare the steps for a slow application (measure, profile, find the bottleneck, fix, verify) and for an outage (what changed, bisect, mitigate, then find the root cause).
- Describe one high-traffic system from your own work, covering the load, the bottleneck you found and the change you made.
Deliverable: A trade-off table, two written troubleshooting runbooks and a one-page account of a high-traffic system from your own work.
Day 6 — Technical discussion answers out loud
- Answer aloud: the differences between REST and GraphQL, how you keep code quality and maintainability high, and which design patterns you use and why.
- Write and tell your hard-bug story and your complex-technical-problem story in order: symptom, evidence, fix, impact, prevention.
- Answer aloud how you would analyse a dataset for actionable insights, and how you would handle a project with ambiguous requirements.
- Rehearse your version control answer: branching, merge versus rebase, and recovering from a mistake.
Deliverable: Recorded spoken answers for each topic and two written bug and problem stories, each with its outcome.
Day 7 — Behavioral stories and a full rehearsal
- Write your difficult-teammate, prioritisation, mentoring, setback and motivation answers as short structured stories, each with a result.
- Say each one aloud and cut anything that is background rather than your own decision.
- Run a mixed rehearsal: one coding problem written by hand, one design outline and two behavioral answers in a row.
Deliverable: Five behavioral stories, each with a named result, and a completed mixed rehearsal.
Behavioral stories for the Classdojo loop
The final stage is reported as a Behavioral Evaluation of how you respond in team situations. The reported prompts cover difficult colleagues, prioritisation, mentoring, setbacks and motivation, and the technical discussion also asks about your version control experience and your approach to ambiguous requirements. Prepare a handful of real stories that you can adapt to several of these, each showing your own decision and a concrete result.
- How would you approach a project with ambiguous requirements?
Can you discuss your experience with version control systems?
Medium · Technical / Domain Questions
Approach
- Describe your actual workflow in concrete terms: how you branch (short-lived feature branches off main, or trunk-based with feature flags), how you name and size commits, and how pull requests are reviewed and merged. Concrete habits beat a list of commands.
- Explain merge versus rebase versus squash with a rule for each: rebase a local, unpublished branch to keep history linear; never rewrite history others have pulled; use merge commits when preserving the branch context matters; squash on merge when the branch history is noisy.
- Show you can recover from mistakes: git reflog to find lost commits, revert (not reset) to undo a change already on a shared branch, cherry-pick to carry a hotfix to a release branch, and git bisect to find the commit that introduced a regression, which takes about log2(n) test steps for n commits.
- Close with one real episode, such as a painful merge conflict or a bad deploy, and the process change that followed, for example required CI checks, branch protection or a code owners file. Resolve conflicts by understanding both intents, then rerun the tests.
Follow-up
- You force-pushed over a teammate's commits on a shared branch. How do you recover them?
- A regression appeared somewhere in the last 200 commits. How do you find the offending change?
- When would you choose merge over rebase on your team's main branch?
Describe a time when you had to work with a difficult team member.
Medium · Behavioral / Leadership
Approach
- Choose a real situation with a clear working problem, such as missed review turnarounds, dismissive code review comments or unreliable handoffs, and describe the behaviour and its effect on the project instead of labelling the person.
- Show the order of your actions: you first spoke to the person directly and privately, asked about their constraints or view, and listened. Say what you learned that changed your own understanding.
- Describe the concrete agreement that came out of it, such as review within a day, written decisions in the pull request, or a split of ownership, and what happened next. If you involved a manager, say when and why, and that you brought facts rather than complaints.
- End with the measurable result and what you took away, for example the working relationship improved and the delayed work shipped. The story sinks if you were a bystander, if you cast the other person as the villain, or if there is no outcome.
Follow-up
- What if the direct conversation had changed nothing?
- How did you keep the project moving while the tension was unresolved?
- What would you do differently if you met the same situation again?
How do you prioritize tasks when managing multiple projects?
Medium · Behavioral / Leadership
Approach
- Use a real example with competing demands, for instance a production bug, a feature with a deadline and a request from a stakeholder arriving in the same week. Name what each was worth and the cost of delay.
- State your method in plain words: rank by user or business impact and urgency, check dependencies and what blocks others, estimate the effort, then commit to an order. The frameworks matter less than showing a real decision.
- Show that you make trade-offs visible: you told the product manager or stakeholders what would slip and agreed it with them, rather than silently absorbing everything or missing a date.
- Cover re-planning: you review the list when something new lands, and you say no or renegotiate scope when the load is too high. End with what shipped and what you deliberately deferred. A weak answer says you do everything, or lists a framework with no episode.
Follow-up
- Two stakeholders each insist their request is the most urgent. What do you do?
- Tell me about a priority call you got wrong and how you fixed it.
- How do you handle an urgent production issue in the middle of planned work?
Share an experience where you had to mentor a junior developer.
Medium · Behavioral / Leadership
Approach
- Pick a mentoring relationship where you had a clear role, such as onboarding a new hire or guiding a junior engineer through a first feature. Open by saying what the person needed to be able to do by the end.
- Describe your method specifically: scoping a small first task, pairing at the start, then stepping back; code review comments that explain why a change helps; and regular short check-ins in which they set the agenda.
- Handle the hard part honestly: a moment when you were tempted to take over the task, or when the person struggled or disagreed with your feedback, and what you did. Letting them finish the work, even slowly, is part of the point.
- Show the result in observable terms: their pull requests needing fewer revisions, a feature they owned alone, or their moving on to reviewing others' code. Also say what you learned as the mentor. The common mistake is a story where you did the work for them.
Follow-up
- What did you do when your mentee kept making the same mistake?
- How did you balance mentoring with your own delivery commitments?
- How do you give critical feedback to a junior engineer without discouraging them?
How do you handle failure or setbacks in your work?
Medium · Behavioral / Leadership
Approach
- Choose a real setback that you owned, such as a release that caused an incident, a missed estimate or a design that did not scale. State the impact plainly and early, with no spin.
- Describe your immediate response: contain the damage, communicate quickly to those affected, and fix or roll back before examining causes.
- Then give the analysis: you looked for causes in process and system (missing test, no alert, unclear ownership) rather than blaming individuals, and found the real root cause.
- Finish with the specific changes you made, such as a regression test, a deploy check, an alert or a checklist, and evidence that the problem did not recur. The story fails if the failure is a disguised strength, if you blame others, or if you cannot name what changed afterwards.
Follow-up
- What would you do differently if you faced the same situation today?
- How did you tell your manager and your teammates about the problem?
- Describe a smaller setback where you caught the problem early, and how.
What motivates you to perform well in your role?
Medium · Behavioral / Leadership
Approach
- Give two or three honest drivers rooted in your own history, for example solving hard debugging problems, shipping features that people use, or helping teammates grow, and tie each to something you actually did.
- Back the answer with one short story that shows the driver in action, such as staying to polish a rough user flow after the deadline because you had watched users struggle with it.
- Connect it to this role without making claims about the company: you can say that building software used by teachers, students and parents appeals to you and why, so long as the reason is yours and specific.
- Add what helps you stay motivated, such as clear ownership, quick feedback from users and a team that reviews code carefully, and say how you act when motivation dips. Generic answers ("I love learning") with no example are the weak version.
Follow-up
- What kind of work or situation drains your motivation, and how do you deal with it?
- Why education technology rather than another domain?
- What would you need from your team to do your best work?