Interview guides · Amplify
Amplify Software Engineer Interview Guide
Amplify Software Engineer interview prep: six reported rounds, TypeScript/React/Node/MongoDB questions with worked answers, and a 7-day plan.
The 60-second brief
Amplify makes K–12 curriculum and assessment software for English language arts, math and science. Candidates describe a full-stack Software Engineer role: TypeScript on both ends, React in the browser, Node.js (often with Express) on the server, MongoDB for storage, and deployment to a cloud provider such as AWS. Some of the work involves adding large language model features, such as coaching and lesson-planning help for teachers. Engineers work with product managers, designers, data scientists and education specialists.
This guide is for engineers interviewing for a Software Engineer role at Amplify, from mid-level to staff. It goes through the six rounds candidates report and answers the 20 reported questions in five groups: language fundamentals in JavaScript, TypeScript and React; practical coding, mostly data transformation; refactoring and Jest testing; system design on Node/Express, MongoDB and AWS; and behavioral questions about motivation, collaboration, code review and production incidents. System design is described as an area assessed for senior and staff roles, so those candidates should spend more of the week on it.
Candidates report six stages over roughly four to six weeks: Recruiter Phone Screen, Technical Screen, Virtual Onsite Loop, System Design Interview, Technical Round and Behavioral Rounds. Depending on the team, the coding part is a take-home assignment or live coding in a shared editor.
Focus areas
- Technical Interviews (Coding + Concepts)
- Take-Home Assignments
- Problem Solving
Overview
The reported questions are practical web engineering, not puzzles. Candidates are asked to write FizzBuzz in JavaScript, convert data from one format to another, process student assessment inputs, refactor code for new requirements and write Jest tests for edge cases. Practise writing clean TypeScript or JavaScript that handles malformed input and has tests. Complexity still comes up: one reported question asks for several solutions to a problem and a comparison of their time and space costs, so be ready to name the brute-force, sorting and hash-map options for any task.
The language questions cover the stack you would use. Practise explaining how the Node.js event loop runs callbacks, promises and async/await, how you would split state in a React app, and how you would arrange classes and methods for a component. Say what the runtime does before you justify your code: microtasks versus timers, why CPU work blocks every request in a Node process, and why a React context update re-renders every component that uses it.
The design questions all come from classroom software: a real-time dashboard for teachers, an Express server under heavy concurrent load, a MongoDB schema for a content authoring tool, monitoring and deployment on AWS, and low-latency architecture for classrooms. The classroom details make these different from generic design practice. Load spikes when a whole class starts at once. Many students may share one school network address. Connections drop in the middle of an activity. Student data must stay out of logs. Build each design around those facts, using the tools candidates list for the role: Docker, Terraform and Datadog.
The behavioral questions ask why you want to work on K–12 education, how you work with designers and product managers, how you take code review feedback, how you debugged a production problem under a deadline, and how you keep up with new tools, including AI. Prepare one specific, measurable story for each. Before the motivation question, read about Amplify's curriculum and assessment products so your answer can name actual products.
The Amplify interview process
Candidates report the six stages below. Which sessions appear, and whether the coding is a take-home or live, can change with team and level, so confirm the format with your recruiter.
Recruiter Phone Screen
Candidates describe a first call with a recruiter to align on your background, role expectations and mission fit. Treat it as a short pitch: say which of your work matches the stack candidates report (TypeScript, React, Node.js, MongoDB, a cloud provider) and give one concrete reason you want K–12 work. Use the call to learn what comes next. Candidates report that some teams give a take-home assignment and others do live coding in a shared editor, and system design is described as aimed at senior and staff levels, so ask which format and sessions apply to you.
What to demonstrate
- Your background as it relates to the full-stack role candidates describe
- Why you want to build software for K–12 education
How to prepare
- Write a short spoken summary of your career that points to TypeScript/React/Node and production cloud work, with one result you can measure.
- Read about Amplify's ELA, math and science curriculum and its assessment products, and name one you would like to work on and why.
- Ask whether the coding stage is a take-home or live, which language is expected, and whether the onsite includes a system design session at your level.
Technical Screen
Candidates report practical coding plus a detailed discussion of past experience, with a focus on web frameworks and problem solving. Prepare for both parts. For coding, practise small, realistic JavaScript or TypeScript tasks in a plain shared editor: reshaping data, simple functions with input checks, a function with Jest tests. For the experience discussion, pick two projects you can describe at the code level: how the React and Node pieces fit together, which decisions you made, what failed in production and what you changed afterwards.
What to demonstrate
- Working, readable code for a practical task
- Depth on past web-framework work and how you solved problems
- Familiarity with React and Node.js
How to prepare
- Write FizzBuzz, a format conversion and an assessment-scoring script in TypeScript without autocomplete, explaining your reasoning out loud.
- Practise explaining the event loop, the differences between promises and async/await, and React state placement until you can do it without notes.
- Write a one-page outline of each of your two projects: architecture, your decisions, one incident and what you would change now.
Virtual Onsite Loop
Candidates describe the virtual onsite as a series of interviews divided into specialized sessions, and report meeting engineering managers, peers and directors over the course of the loop. The system design, technical and behavioral stages listed after this one are the areas candidates name. Ask your recruiter which sessions your onsite contains and in what order. Then rehearse each type on a different day so that switching between coding, design and behavioral answers in one sitting feels routine.
What to demonstrate
- Collaboration skills, which candidates report for the behavioral rounds
- Technical depth in the full-stack and system design sessions
How to prepare
- Get the session list from your recruiter and match each session to the parts of this guide's 7-day plan that cover it.
- Run one back-to-back mock: a coding task, then a design question, then two behavioral stories, and note where your answers lose detail.
- Practise explaining your reasoning to engineers, managers and directors, since candidates report meeting all three.
- Set up your shared-editor and video environment ahead of time and run a Node script and a Jest test in it.
System Design Interview
Candidates report a session on system design and architecture, and design is described as aimed at senior and staff levels and system-focused rounds. The reported design questions cover a real-time teacher dashboard, scaling Node.js/Express for concurrent traffic, MongoDB schema design for content authoring, monitoring and deployment on AWS, and low-latency architecture for classrooms. Use the same order every time: users and load shape (a whole class starting at once), entities and API, data model, read and write paths, failure handling for unreliable school networks, and how you would observe and deploy the system.
What to demonstrate
- Application structure and data flow
- API and data schema design
- Scalability, reliability and how a production system is operated
How to prepare
- Practise the real-time teacher dashboard with a snapshot API plus a push channel, and explain how a reconnecting client catches up.
- Design a MongoDB model for lessons and reusable content, choosing embedding or referencing for each relationship and saying what each choice costs.
- Outline a deploy-and-monitor pipeline using Docker, Terraform and Datadog, including what you would alert on during school hours.
Technical Round
Candidates describe a deep-dive technical round on full-stack development and testing. This is where to show you can move between a React component, an Express endpoint and the data behind it, and that you test what you write. One example scenario candidates mention for the role is a React component with complex form state that talks to an Express backend. Practise building that from start to finish, with validation in both places and tests at each layer.
What to demonstrate
- Full-stack implementation across React, Node.js/Express and storage
- Automated testing habits and edge-case coverage
- Code quality while requirements change
How to prepare
- Build a small React form with validation and a submitting/error/success state that posts to an Express route, then write Jest tests for the reducer and the route handler.
- Take an old function of your own, pin its current behaviour with tests, refactor it, and explain each step.
- Review Jest mocks, fake timers and table-driven tests (test.each), and be ready to explain where an end-to-end tool such as WebdriverIO fits instead.
Behavioral Rounds
Candidates report behavioral rounds focused on collaboration and alignment with Amplify's core values. The reported prompts ask why you want to build K–12 software, about working with designers and product managers, how you take code review feedback, how you handled a production bug under a deadline, and how you keep up with new practices and AI tools. Answer each with one specific story: the situation in a sentence or two, the decision you made, what it cost or risked, and an outcome you can measure.
What to demonstrate
- Collaboration with non-engineering partners
- How you give and receive code review feedback
- Motivation for education technology
How to prepare
- Write five stories, one per reported prompt, each with your own decision and a measurable result.
- Prepare a disagreement story (with a designer, PM or reviewer) that ends with evidence settling the question.
- Prepare a specific answer on how you use AI tools in your work, including how you check their output.
Where candidates lose points
Writing the transformation or assessment-scoring code for clean input only, with no input checks and no tests.
For every data question, list the malformed cases before coding (missing fields, duplicates, nulls, out-of-order timestamps), collect bad rows into an error report, and finish with a few test.each cases in Jest.
Treating Node.js as multi-threaded in the Express concurrency question, or putting CPU-heavy work in request handlers.
Explain the event loop first, move CPU-heavy work to worker_threads or a job queue, scale with stateless replicas, and name event-loop lag as a metric you would watch.
Designing the MongoDB content schema as normalised relational tables, or embedding arrays that can grow without limit.
List the queries first, then decide embedding versus referencing for each relationship and justify it. Mention the document size limit, versioning of published content, and conditional updates for concurrent edits.
Giving a generic web-scale design that ignores classrooms: synchronised bursts at class start, shared school IP addresses and unreliable networks.
Raise those facts early in every design question, and show how you handle them with CDN caching, scaling ahead of peaks, client-side buffering with idempotent retries, and rate limits that do not lock out a whole school.
Refactoring by rewriting immediately, with no record of the current behaviour.
Write characterisation tests first, make small refactors that keep them passing, add the new requirement in its own step, and measure any performance claim on realistic data.
Questions & practice
Can you walk me through your preferred coding language and explain its core features?
Medium · Technical and Language Fundamentals
Approach
- Pick the language that matches the role. For Amplify that is TypeScript or JavaScript, which candidates list as the stack. Organise the answer as runtime model, type system, concurrency and tooling, folding the common traps into the first two.
- Runtime: one thread running an event loop, first-class functions and closures, prototype-based objects underneath the class syntax, and garbage collection, which means a closure that holds a large object keeps it alive.
- Types: TypeScript checks structure and its types are erased at runtime, so data from outside the program still needs a runtime check. Name strictNullChecks, union types narrowed with type guards, and why
unknownis safer thanany. - Mention the known traps and how you avoid them:
==coercion (use===), howthisbinds in regular versus arrow functions, and floating-point arithmetic (0.1 + 0.2 !== 0.3) wherever scores or money are computed. - Concurrency and tooling: the event loop with promises and async/await for I/O, worker_threads for CPU work, and the tools you would name for a TypeScript project, such as tsc, ESLint, Jest and Nx for a monorepo.
Follow-up
- When would you use an interface rather than a type alias?
- Why does a resolved promise's callback run before a setTimeout(fn, 0) callback?
- How do you check JSON from an API at runtime when TypeScript types disappear after compilation?
How would you implement a simple FizzBuzz function in JavaScript?
Medium · Technical and Language Fundamentals
Approach
- Write it as a pure function that returns an array of strings instead of logging, so it is easy to test:
for (let i = 1; i <= n; i++). Either check divisibility by 15 first, or build the string from the 3 and 5 checks and fall back to the number. - Better version: drive it from a rules table such as
[[3,'Fizz'],[5,'Buzz']]and join the words that match. Adding a rule like 7 → 'Bazz' then means adding a row, not another branch. - Edge cases: decide what happens for n <= 0 (return an empty array) and for non-integer or non-number input (throw a TypeError or round, and say which). It runs in O(n) time and O(n) space for the result.
- Finish with a few Jest tests: 1, 3, 5, 15 and a value of 0 or less, using test.each.
Follow-up
- Add a rule for 7 without touching the existing branches.
- Can you do it without the modulo operator?
- How would you stream the output for a very large n instead of building an array?
What are the key differences between various asynchronous programming patterns in Node.js?
Medium · Technical and Language Fundamentals
Approach
- Go through the patterns in the order they appeared. Error-first callbacks lead to deep nesting and make it easy to call a callback twice. Promises allow chaining and run their callbacks as microtasks. async/await is promise code written to read top to bottom, with errors handled in try/catch.
- Contrast how they combine: awaiting in a loop runs tasks one at a time. With Promise.all the tasks run concurrently once started, it rejects on the first failure, and the other tasks keep running because nothing is cancelled (use AbortController to stop them). Promise.allSettled collects every result; Promise.race and Promise.any return the first to settle or the first to succeed.
- Cover EventEmitter and streams for data that arrives over time, including backpressure (respect
write()returning false, or usepipeline), and worker_threads for CPU-heavy work that would otherwise block the event loop. - Name the common bugs: a missing
await, an unhandled rejection (recent Node versions crash the process by default), and starting thousands of unbounded concurrent requests. Cap concurrency with a small pool or batching.
Follow-up
- How would you run 1,000 HTTP calls with at most 10 in flight at once?
- What is the difference between process.nextTick, setImmediate and a resolved promise?
- How do you convert a callback-style API into one that returns a promise?
Can you explain how you would structure classes and methods for a given software component?
Medium · Technical and Language Fundamentals
Approach
- Start from responsibilities, not class names. List what the component must do, give each responsibility to one unit, and keep I/O (database, HTTP, clock) at the edges so the core logic is pure.
- Prefer composition and interfaces to deep inheritance. For example, an assessment scorer could define a
QuestionScorerinterface with one implementation per question type, chosen from a map, so adding a type does not change existing code. - Pass dependencies in through the constructor (a repository, a clock) so tests can substitute fakes. Keep public methods few and named after behaviour, and keep validation at the boundary.
- Name the trade-off: too much abstraction early makes simple code hard to follow, so add an interface when a second implementation or a test actually needs it.
Follow-up
- How would you unit test this component without a database?
- A new question type needs partial credit. What changes?
- When is inheritance the better choice here?
Write code to translate data formats from one structure to another.
Medium · Coding and Problem Solving
Approach
- Clarify both shapes before writing code: field mappings, required versus optional fields, how dates and time zones are represented, and whether the output groups or flattens records. A common example is flat CSV-style rows turned into nested JSON grouped by an id.
- Write the mapping as a pure function from one record to a result. Group with a Map keyed by id so the whole pass is O(n), instead of calling
array.findinside the loop, which is O(n²). - Check input at runtime and collect errors per record (row number plus reason) rather than throwing on the first bad row. Decide what happens to unknown fields, nulls, empty strings and duplicate ids.
- Test round trips and edge cases: an empty input, one record, a missing nested field, and a date at a time-zone boundary. For input that will not fit in memory, process it as a stream, line by line.
Follow-up
- The source file is 5 GB. How does your design change?
- The source format gains a new version while the old one is still in use. How do you support both?
- How do you show the output is lossless when converting back to the original format?
Provide multiple distinct solutions to a coding problem and compare their time and space complexities.
Medium · Coding and Problem Solving
Approach
- Take one concrete problem, such as finding a pair that sums to a target or detecting duplicates, and give three solutions from simplest to fastest.
- Brute force with nested loops: O(n²) time, O(1) extra space. Sort first, then use two pointers or compare neighbours: O(n log n) time, with space depending on the sort, and the original indices are lost. A hash set or map: O(n) expected time and O(n) space.
- Say when each one wins: the brute force for tiny inputs or tight memory, sorting when the input is already sorted or memory is limited, hashing when speed matters and memory is available. Mention that hash lookups have a bad worst case.
- Write the best solution in full, then test duplicates, negative numbers, an empty array and the case with no answer.
Follow-up
- The input is already sorted. Which solution do you pick now?
- The data arrives as a stream you cannot store. What changes?
- How would you measure which version is actually faster on real data?
Implement a data transformation script to process student assessment inputs.
Medium · Coding and Problem Solving
Approach
- Define the input, for example rows of (studentId, itemId, response, submittedAt), and the output: a score per student and possibly accuracy per item. Confirm the answer-key format and whether partial credit exists.
- Process in a single O(n) pass: parse, normalise responses (trim, lowercase where appropriate), drop or flag rows with unknown items, and keep the latest submission per (student, item) using a Map keyed by that pair, compared on timestamp.
- Score against the key, then aggregate per student in a second Map. Treat 'not attempted' differently from 'incorrect', because a teacher reads those two results differently.
- Edge cases: duplicate rows, out-of-order timestamps, a student missing from the roster, empty responses and malformed numbers. Write rejected rows to an error report, and never log student names or responses.
Follow-up
- A student resubmits after the deadline. How does your script decide which answer counts?
- The file grows to millions of rows from many schools. What changes?
- How would you test this script with fixtures?
Refactor an existing piece of code to improve readability and performance based on new requirements.
Medium · Coding and Problem Solving
Approach
- Before changing anything, record current behaviour with characterisation tests on the public interface, so the refactor is checked against something.
- Name the specific problems: duplicated branches, deep nesting (fix with early returns), unclear names, mixed I/O and logic, and hidden O(n²) work such as
includesorfindinside a loop (fix with a Set or Map lookup). - Refactor in small steps that each keep the tests passing. Then add the new requirement in its own commit, so the change is easy to review.
- Measure before claiming a speed-up: profile or benchmark with realistic data, and say what you did not change, such as the public API and the order of side effects.
Follow-up
- How do you know you did not change behaviour where there were no tests?
- When would you decide not to refactor before adding the feature?
- How would you split this work into pull requests a reviewer can handle?
Write unit tests using testing frameworks like Jest to validate edge cases in your code.
Medium · Coding and Problem Solving
Approach
- Structure each test as arrange, act, assert, with describe/it blocks named after behaviour. Use test.each for the edge-case table: empty input, null or undefined, boundary values, duplicates, unicode strings and a large input.
- Isolate I/O: use jest.fn() for injected dependencies and jest.mock() for modules. Use fake timers or an injected clock for anything time-based, so tests do not depend on the real time.
- For async code, await the call and use
await expect(fn()).rejects.toThrow()for failures. Use expect.assertions(n) when an assertion sits inside a callback. - Test observable behaviour rather than private internals, and treat coverage as a way to find untested branches, not as proof the code is correct. Mention that browser flows belong in an end-to-end tool such as WebdriverIO, not in unit tests.
Follow-up
- How do you test a function that calls Date.now()?
- Where do you draw the line between unit, integration and end-to-end tests here?
- A test fails intermittently in CI. How do you find the cause?
Walk me through how you would set up a server-side application using Node.js and Express to support heavy conc
Medium · System Design and Architecture
Walk me through how you would set up a server-side application using Node.js and Express to support heavy concurrent traffic.
Approach
- Start with the runtime: one event loop per process, so handlers must not block. Move CPU-heavy work (PDF generation, large JSON transforms) to worker_threads or a separate job service, and limit request body size.
- Scale out with stateless instances (one Node process per core, or multiple container replicas) behind a load balancer. Keep sessions and other shared state outside the process.
- Protect dependencies: a sized database connection pool, timeouts and retries with backoff on outbound calls, rate limiting, caching of read-heavy responses, and compression or a CDN for static assets.
- Operate it: health and readiness endpoints, graceful shutdown on SIGTERM (stop accepting requests, drain in-flight ones), structured logs, and metrics for event-loop lag, p99 latency and error rate.
Follow-up
- One endpoint takes 300 ms of CPU per request. What do you do?
- How do you find what is blocking the event loop in production?
- The database pool is exhausted under load. What are your options?
How do you approach database schema design using MongoDB for a content authoring platform?
Medium · System Design and Architecture
Approach
- Design from the access patterns first. Write down the queries: load a lesson with its blocks, list an author's lessons by last edit, find every lesson that uses an asset, and publish a version.
- Embed what is read together and has a fixed upper size, such as a lesson's ordered blocks. Reference what is reused or can grow without limit, such as shared assets, assessment items and edit history. Remember the 16 MB document limit and that rewriting a large document is slow.
- Separate the working copy from published versions. Publishing writes an immutable versioned snapshot, so students never see half-finished edits. Use a multi-document transaction when publishing touches several collections.
- Handle concurrent edits with a version field and a conditional update (only update if the version still matches), and enforce structure with $jsonSchema validation. Add compound indexes such as (authorId, updatedAt), plus a unique index on slug.
Follow-up
- Two authors edit the same lesson at the same time. What happens?
- A shared item is fixed. Should lessons that are already published pick up the fix?
- How would you query all lessons that use a given asset, and what index supports it?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
How would you design the API and data schema for a real-time teacher tooling dashboard?
Medium · System Design and Architecture
Approach
- Scope the requirements: a teacher watches the students in one lesson session progress in near real time (started, answered, stuck). There are tens of students per session, a burst of writes when the class starts, and the teacher may only see their own roster.
- Data model: Class, Student, Session (one run of a lesson) and a progress record per (session, student, activity), updated with an upsert. Keep an append-only event log if replay or analytics is needed.
- API: a REST snapshot endpoint (GET the session's progress), a push channel over WebSocket or SSE that sends changes with a sequence number, and a student write endpoint that takes an idempotency key so retries cannot double-count.
- On reconnect, the client asks for changes after its last sequence number, or reloads the snapshot. Across several server instances, fan out through a pub/sub channel per session. Check that the teacher is allowed to see that session on every subscription.
Follow-up
- The teacher's connection drops for a minute. What does the dashboard show when it returns?
- Thirty students submit at the same moment. Where is the contention?
- Why choose SSE over WebSockets here, or the other way round?
Describe your strategy for monitoring, logging, and deploying production systems in a cloud environment like A
Medium · System Design and Architecture
Describe your strategy for monitoring, logging, and deploying production systems in a cloud environment like AWS.
Approach
- Deploy: a CI pipeline that lints, tests and builds an immutable Docker image; infrastructure defined in Terraform; rolling or canary releases with automatic rollback when error rate or latency crosses a threshold; feature flags to separate deploy from release.
- Logging: structured JSON logs with a request or trace id carried across services, consistent log levels, and no student personal data in logs.
- Metrics and alerts in a tool such as Datadog: request rate, error rate and latency per endpoint, plus saturation (CPU, memory, event-loop lag, database connections). Alert on user-facing symptoms and SLO burn, not on every CPU spike.
- Make the classroom link explicit: schedule risky deploys outside school hours, use dashboards that show impact per school or district, and keep a runbook for the alerts that wake someone up.
Follow-up
- An alert fires during morning classes. What are your first steps?
- How do you roll back a release that included a database migration?
- What goes on a dashboard but should not page anyone?
How do you approach system architecture when scalability and low latency are critical in classroom environment
Medium · System Design and Architecture
How do you approach system architecture when scalability and low latency are critical in classroom environments?
Approach
- Describe the load: traffic comes in synchronised bursts at the start of class periods across time zones, a whole class loads the same lesson at once, and many students may share one school IP address. Rate limiting by IP alone would block a whole school.
- Speed up reads: serve lesson content and assets from a CDN with long cache lifetimes and versioned URLs, cache rendered content, and scale up before predictable peaks instead of relying only on reactive autoscaling.
- Make writes resilient: the client buffers student responses locally and retries with idempotency keys, and the server accepts them quickly and processes them asynchronously through a queue. A brief network drop then does not lose work.
- Set latency targets at p95/p99 for the paths students use most, decide which reads can be slightly stale (content) and which cannot (submission confirmation), and degrade non-essential features first under load.
Follow-up
- A school's network drops in the middle of an assessment. What does the student see, and what is saved?
- How would you rate-limit fairly when hundreds of students share one IP address?
- Which part of this system fails first at ten times the load?
p99 jumped on one listing filter while p50 stayed flat
Medium · query planning · composite index · percentiles
After a release that added an owner_user_id filter to the resource listing, p99 rose from 90 ms to 1.9 s while p50 stayed at 40 ms. Traffic and row counts are unchanged. resource carries the index (tenant_id, status, updated_at DESC, resource_id DESC). The new query filters tenant_id and owner_user_id, orders by updated_at DESC, resource_id DESC, and takes 20 rows. On PostgreSQL, explain the shape of the regression, prove it from a query plan, and give the index you would add.
Approach
- Start from the shape. A flat p50 with a moved p99 means a subset of requests changed cost, not all of them, so the first job is naming the subset. Bucket the endpoint's latency by the tenant's row count; the natural hypothesis is that large tenants are a small share of requests and all of the tail.
- Get the plan for the new query on a large tenant with EXPLAIN (ANALYZE, BUFFERS). Expect an index scan over the tenant's range, a filter discarding most of it, then a Sort feeding the Limit, possibly reporting Sort Method: external merge Disk. Read actual rows on the scan node, not estimated.
- Explain why the existing index cannot serve it. A composite B-tree is seekable only as a left prefix, and with no equality predicate on status the scan cannot treat updated_at as an ordering, because rows in the tenant's range are ordered by status first. Everything matching must be read and sorted before LIMIT 20 can apply, so a tenant with 400,000 rows pays 400,000 rows to return 20.
- Add (tenant_id, owner_user_id, updated_at DESC, resource_id DESC). Equality on the first two columns leaves the index ordered by updated_at within that pair, so the plan becomes an index scan that stops after 20 rows with no Sort node. PostgreSQL can scan a B-tree backwards, so the DESC markers matter only if the two sort columns ever disagree in direction; keeping them explicit documents the order the keyset cursor depends on.
Follow-up
- The endpoint paginates with OFFSET. What does page 500 cost with your index, and what does the keyset version cost?
- How would you have caught this before release, given that a 10,000-row seed database produces the same plan shape at an unnoticeable cost?
Suggested practice checklist
Each day covers one group of reported questions, in the stack candidates list for the role. Coding and testing come first, with a timed take-home rehearsal on Day 2, then full-stack work, design, operations and the behavioral stories.
Day 1 — JavaScript/TypeScript runtime and fundamentals
- Explain out loud the event loop, closures,
thisbinding and TypeScript narrowing, as an answer about your preferred language. - Write FizzBuzz as a pure TypeScript function driven by a rules table, with Jest tests for 1, 3, 5, 15 and n <= 0.
- Write one example each of callbacks, promises and async/await, plus a pool that runs async tasks with a concurrency limit.
Deliverable: A one-page language summary, a tested FizzBuzz module, and an async examples file that includes the concurrency-limited runner.
Day 2 — Practical coding: data transformation and a take-home rehearsal
- Write a converter from flat rows to nested JSON grouped by id, with a per-row error report and a round-trip test.
- Write an assessment-scoring script that keeps the latest submission per student and item and separates 'not attempted' from 'incorrect'.
- Solve one pair or duplicate problem three ways (nested loops, sort, hash map) and write down the time and space cost of each.
- Candidates report that some teams give a take-home, so redo the converter as a timed take-home: a README with setup and assumptions, input checks and tests, submitted as a ready-to-send folder.
Deliverable: Two tested transformation scripts, a written comparison of three solutions with their complexities, and a take-home style converter with README, assumptions and tests.
Day 3 — Code quality: structure, refactoring and Jest
- Design classes for an assessment scorer with a strategy per question type and dependencies passed in, then explain the design out loud.
- Take an old messy function, add characterisation tests, refactor it in small commits, then add a new requirement.
- Write a Jest suite that uses test.each, jest.fn, fake timers and an async rejection test.
Deliverable: A scorer module diagram with code, a refactored function with its commit history, and a Jest suite covering edge cases.
Day 4 — Full stack: React state and Express under load
- Build a React form using useReducer with submitting, error and success states that posts to an Express route, with validation on both sides.
- Write Jest tests for the reducer (each transition) and for the Express route handler (valid input, invalid input, server error).
- Outline how you would scale that Express service: stateless replicas, connection pool, timeouts, graceful shutdown, metrics for event-loop lag.
- Explain where each piece of state in your form app lives and why.
Deliverable: A working React + Express mini app with Jest tests for the reducer and the route handler, a one-page Express scaling outline, and a state-placement explanation.
Day 5 — System design: real-time dashboard and classroom scale
- Design the real-time teacher dashboard: entities, snapshot API, push channel with sequence numbers, idempotent student writes, and pub/sub across instances.
- Design for a whole class starting at once: CDN content, scaling before peaks, client buffering with retries, and rate limits that work with shared IP addresses.
- Answer the follow-ups for both designs out loud, starting with the teacher reconnecting and 30 students submitting at the same moment.
Deliverable: Two design diagrams with written API and data models, plus a list of failure modes and how each design handles them.
Day 6 — Data model and operations: MongoDB, AWS, debugging
- Model a content authoring platform in MongoDB: embed versus reference decisions, published version snapshots, a version field for concurrent edits, and indexes.
- Outline a deploy-and-observe setup on AWS: Docker image, Terraform, canary with rollback, structured logs without student data, Datadog alerts on SLO burn.
- Work through the p99 latency debugging practice question as a check of your production-debugging sequence.
Deliverable: A MongoDB schema document with indexes, a deployment and monitoring outline, and a written debugging walkthrough.
Day 7 — Behavioral stories and a full mock
- Write five STAR stories: why K–12 and Amplify, cross-functional work, code review feedback, a production bug under deadline, and keeping current with AI tools.
- Rehearse each story out loud, check it states your decision and a measurable result, and prepare answers to two follow-ups per story.
- Run one combined mock: a coding task from Day 2, a design question from Day 5, then two stories.
Deliverable: Five written stories with follow-up answers, and notes from the combined mock on what to fix.
Behavioral stories for Amplify
Candidates report behavioral rounds focused on collaboration and alignment with Amplify's core values. The reported prompts cover why you want to work on K–12 software, working with designers and PMs, code review, production incidents and AI tools. For each, prepare one specific story that shows your own decision and a result you can measure, and be ready to name a real Amplify product when asked about motivation.
How do you handle state management in a modern frontend framework like React?
Medium · Technical and Language Fundamentals
Approach
- Start with placement: keep state as close as possible to the component that uses it (useState). Lift it to the nearest common parent when siblings share it. Compute derived values while rendering instead of storing them.
- Use useReducer when a component has several related fields and named transitions, such as a form that can be idle, submitting, failed or done. Each transition is then a pure function you can unit test.
- Treat server data separately from UI state: a fetch/cache layer that handles loading, errors and refetching, instead of copying API responses into global state. Keep URL-worthy state, such as the selected class or filter, in the route.
- Use Context for values that change rarely, such as the current user or theme. Every consumer re-renders when the value changes, so split contexts or bring in a store only when profiling shows a problem. Watch for stale closures in effects with incomplete dependency arrays.
Follow-up
- When would you add a global store, and what would you put in it?
- How would you implement an optimistic update and roll it back if the server rejects it?
- A teacher dashboard re-renders every student row on each update. How do you find the cause and fix it?
What do you know about Amplify, and why do you want to build software for K–12 education?
Medium · Behavioral and Role Fit
Approach
- Show you have done the research: name Amplify's subject areas (ELA, math, science) and specific products from its curriculum and assessment offerings, and say what you noticed about one of them.
- Give a concrete reason for wanting to work on K–12 education, tied to something you did or saw, not a general statement about caring for education.
- Connect it to the role: your TypeScript/React/Node experience, and one engineering problem specific to classrooms you would like to work on, such as reliability on school networks or tools that save teachers time.
- The mistake to avoid is an answer that would fit any edtech company. Mention at least one detail that only applies to Amplify.
Follow-up
- What would you improve in one of Amplify's products?
- What makes building for classrooms harder than building consumer apps?
- What do you expect to find frustrating about working in K–12?
Can you describe a time when you had to work closely with cross-functional team members, such as designers or
Medium · Behavioral and Role Fit
Can you describe a time when you had to work closely with cross-functional team members, such as designers or product managers?
Approach
- Choose a story where you changed the outcome, not one where you implemented a finished spec: for example, an engineering constraint you explained to a designer or PM that led to a change in scope or design.
- Use STAR, but keep situation and task brief. Spend most of the answer on what you did: how you explained the trade-off in non-technical terms, what alternatives you offered, and how the decision was made.
- Give a result you can measure (shipped on time, fewer support tickets, a usability problem fixed) and say what you now do differently with that partner.
- Avoid presenting the other function as the obstacle. Show that you understood what they were trying to achieve.
Follow-up
- Tell me about a time you disagreed with a designer. How was it resolved?
- How did you explain a technical limitation to someone who is not an engineer?
- What would you do differently on that project now?
How do you handle feedback during code reviews and incorporate constructive criticism into your workflow?
Medium · Behavioral and Role Fit
Approach
- Give a real example of a review comment that changed how you work, such as a missed edge case or an unclear abstraction, and what you changed afterwards (a checklist item, a lint rule, a testing habit).
- Explain how you handle disagreement: ask what concern lies behind the comment, settle it with a test, benchmark or short pairing session, and accept the decision once it is made.
- Cover the reviewer side too: you separate blocking comments from optional ones, explain why, suggest alternatives in code, and review promptly so the author is not blocked.
- What sinks this answer: 'I always accept feedback' with no example, or a story where the reviewer turned out to be wrong and nothing was learned.
Follow-up
- Tell me about a review where you disagreed and did not change your code.
- How do you review a 1,500-line pull request?
- Describe feedback you gave that was received badly. What did you change?
Tell me about a time you had to troubleshoot a complex bug in a production environment under tight deadlines.
Medium · Behavioral and Role Fit
Approach
- Structure it as: impact, then mitigation, then diagnosis, then fix, then prevention. Open with who was affected and how badly, then the first thing you did to stop the damage (rollback, feature flag, failover) before you went looking for the root cause.
- Explain the investigation step by step: reproducing the bug, narrowing it with logs, metrics and traces, comparing recent deploys or using git bisect, and testing one hypothesis at a time.
- Describe the fix and the regression test you added, how you kept stakeholders informed while it was open, and how you balanced a quick hotfix against a proper fix under the deadline.
- End with prevention: a monitor, test or process change that would have caught it earlier. A story with no mitigation step, or with no follow-up afterwards, is weak.
Follow-up
- How did you confirm the fix worked in production?
- What would have let you detect this earlier?
- Did you ever regret shipping the quick fix?
How do you stay up to date with modern software engineering practices and new technologies like AI tools?
Medium · Behavioral and Role Fit
Approach
- Name specific habits, not a reading list: a tool or practice you tried recently, how you evaluated it on real work, and whether you adopted it.
- On AI tools, describe actual use: what you use coding assistants for, how you check their output (tests, review, reading the docs), and what you keep out of them, such as secrets or student data.
- If you have built LLM features, which candidates list as a plus for this role, describe one: how you evaluated output quality, handled failures and controlled cost or latency.
- Include one technology you decided not to adopt and why. It shows you evaluate tools rather than follow trends.
Follow-up
- Tell me about a technology you evaluated and rejected.
- How would you measure whether an LLM-based teacher feature is good enough to ship?
- What are the risks of AI-generated code in a shared codebase?