Interview guides · Speechify
Speechify Software Engineer Interview Questions & Guide 2026
Software engineer interview prep for Speechify: original coding, system design and debugging practice, each with a worked solution.
The 60-second brief
Speechify turns written content into spoken audio across reading apps and a text-to-speech API.
The Platform team builds the APIs for subscriptions, payments, authentication, usage tracking and text-to-speech customers.
Several technical interviews + a real-codebase take-home with engineer feedback and revision; a fixed round count is not published.
Focus areas
- Codebase changes
- Retry-safe APIs
- Feedback & revision
Overview
For this Platform opening, prepare around ownership of an existing service. A useful practice project starts with a small backend that already has routes, persistence and tests. Trace one request from the API boundary to its durable state, identify what could charge an account twice, and make a focused change that another engineer can verify. This is a better match to the published codebase assessment than spending every practice session solving isolated algorithm puzzles.
Use the role’s TypeScript and Node.js requirements to choose your main implementation language. The compact Python and SQL examples below help you check a contract quickly; reproduce one of them in your own backend stack before the interview. Keep API behavior, database changes and delivery side effects separate in your explanation. For an audio-generation flow, distinguish accepting a request, scheduling work, producing the audio and recording billable usage.
Treat the revision stage as a second engineering deliverable. Save a baseline, write a brief explanation of the first change, and practise reviewing someone else’s criticism against tests and actual behavior. Your second version should make the reasoning easier to follow, including any feedback you chose not to implement. The point of the exercises is to produce evidence you can discuss: a failing test, a safe fix, a measured result and a clear handoff.
The Platform interview loop
Official badges identify the published Platform process; Typical cards are preparation checkpoints. The employer describes several technical interviews and a two-stage assessment, not five fixed interview rounds.
Scope the backend role
Begin preparation with the service you would own: account state, billing, usage or the public audio API. Connect one example from your experience to that surface, so the conversation starts with an engineering problem rather than a list of technologies.
What to demonstrate
- Explain an invariant in account or payment state, the failure that would violate it, and the user impact of that failure.
- Show where your responsibility started and ended in a service you inherited; separate your decisions from the team’s work.
How to prepare
- Prepare a short request-flow diagram with API, durable state and external dependencies. Mark retry boundaries and what is safe to repeat.
- Choose one production change you can explain from first observation through verification. Bring a concrete test, metric or operational check that supported the decision.
Technical conversations
Speechify lists multiple technical interviews for the Platform role. Practise connecting backend design choices to an observable result instead of reciting an architecture. The posting’s Node.js and cloud requirements give you a useful starting point for selecting examples.
What to demonstrate
- Reason about failures across an API, database and worker without assuming that a timed-out request did nothing.
- Make a tradeoff explicit: which correctness guarantee is essential now, what can wait, and how a teammate would recognize a regression.
How to prepare
- Rehearse the retry-safe job exercise using an authenticated account, an idempotency key and a transaction boundary. Explain both duplicate submission and crash recovery.
- Walk through an unfamiliar TypeScript endpoint aloud. Identify validation, authorization, persistent changes and response behavior, then choose the smallest test that would expose a defect.
Change a real codebase
The take-home uses an existing codebase. Make your first submission easy to run and review: understand the current behavior, implement the requested change and leave a short path from the requirement to the evidence. Avoid turning a contained task into an unrelated rewrite.
What to demonstrate
- Navigate existing conventions and preserve behavior outside the requested change, including error paths and already-supported clients.
- Demonstrate correctness with a test that fails before the fix, plus checks for the boundaries introduced by your implementation.
How to prepare
- Practise opening a small service cold: run it, follow one request, inspect the tests and record assumptions before editing. Keep setup notes reproducible.
- Include a concise change note covering the contract, the important test and one alternative you rejected. List commands that reproduce the result and any remaining limitation the reviewer needs to understand.
Respond to engineer feedback
The assessment includes feedback from a Platform engineer and time to revise your submission. Keep the original result available so you can explain what changed. Treat feedback as evidence to investigate, then make a second version whose behavior and reasoning are both stronger.
What to demonstrate
- Distinguish a correctness issue from a readability preference, and explain your priority without becoming defensive about the first version.
- Close the loop on each review point: show the changed behavior, supporting evidence or reason for retaining an existing choice.
How to prepare
- Ask a peer to review your practice change. Convert each substantive comment into a test, a smaller design decision or a precise explanation.
- Keep a short response log with the comment, your action and the verification. Re-run the original regression checks as well as the new ones; a revision should not silently break a previously correct path.
Explain the final result
Use a final rehearsal to connect your implementation and revision into one clear account. The posting asks candidates to explain their reasoning and allows everyday tools during the assessment. Make it clear which outputs you checked yourself and how you checked them.
What to demonstrate
- Explain the final behavior from the user’s request to the stored outcome, including the failure path that informed your design.
- Demonstrate ownership of tool-assisted work: identify a suggestion you changed or rejected and the system evidence behind that judgment.
How to prepare
- Prepare a five-minute walkthrough of the diff, one representative test and the before-and-after behavior. Keep the longer code tour available only where it answers a question.
- Reproduce the result from your own setup notes, then practise a failure injection such as a repeated request or interrupted worker. End with the next improvement you would make and why it is lower priority.
Where candidates lose points
Starting the codebase task without a working baseline
Run the service and its existing checks before changing it. Trace the endpoint or job handler the task touches and save a representative request with its current result. For a backend that generates audio or records usage, write down what counts as one accepted request and one completed job. If setup fails, isolate that failure and record a reproducible command. This gives the reviewer a way to distinguish your change from an unrelated environment issue, and gives you something concrete to compare after feedback.
Showing only a successful request
A successful audio response does not show that retry behavior or billing state is safe. Add a boundary case that can fail: repeat the same request, reuse its key with different input, or interrupt the worker after persistence. State which operation is atomic and which side effect can happen later. Keep the test focused on the contract rather than a private function name. In your walkthrough, show the assertion that protects against duplicate work and describe the recovery behavior you intentionally left for a later change.
Turning feedback into an unexplained rewrite
The second submission should help the engineer see how you used their feedback. Group comments by correctness, maintainability and scope, then change the smallest relevant part. Preserve the original regression checks, add evidence for the new behavior and explain any suggestion you declined. If a review reveals a deeper design problem, name the invariant that forced the larger change. A list of edits alone is weak evidence; a short before-and-after explanation makes the improvement inspectable.
Trusting generated code without owning the result
Use your normal tools, then verify their output against the service itself. Read a database row, inspect the actual request or reproduce the concurrency failure instead of relying on a tool’s summary. Prepare one example where you narrowed an agent’s scope or rejected an attractive implementation because it violated a contract. In the final walkthrough, you should be able to explain the important lines, the relevant test and what would still fail under a different workload.
Optimizing generation before locating the delay
For an audio API, a slow first response can come from admission, queue wait, generation or delivery. Instrument those boundaries before changing an algorithm. Compare a slow request with a normal one using the same kind of input and follow the trace through the worker, rather than treating one average duration as the whole user experience. In a codebase assessment, a small timing probe and a reproducible request can be more useful than an ambitious optimization. Explain which observation would disprove your hypothesis and what you would measure after the fix.
Questions & practice
Build a bounded audio cache
Medium · Caching · Data structures
Design an in-memory LRU cache with get and put. A read must refresh recency; a write past capacity must evict the least recently used key.
Approach
- Use a hash map plus a doubly linked list, or an ordered-map abstraction.
- Move a key to the most recent position on both reads and writes.
- Define zero capacity, overwrites and missing-key behavior before coding.
Worked solution (35 min)
Support get(key) and put(key, value) with a fixed item capacity. Return None for a missing key. Store only non-null values.
- Keep keys in least-to-most-recent order.
- Refresh order after every successful read or write.
- Evict the first key when capacity is exceeded.
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
if capacity < 0:
raise ValueError("negative capacity")
self.capacity = capacity
self.items = OrderedDict()
def get(self, key):
if key not in self.items:
return None
self.items.move_to_end(key)
return self.items[key]
def put(self, key, value):
if self.capacity == 0:
return
self.items[key] = value
self.items.move_to_end(key)
if len(self.items) > self.capacity:
self.items.popitem(last=False)
cache = LRUCache(2)
cache.put("a", 1)
cache.put("b", 2)
assert cache.get("a") == 1
cache.put("c", 3)
assert cache.get("b") is None
Expected result: After reading a and inserting c, b is evicted. Operations are expected O(1); storage is O(capacity).
Verify it yourself
- Zero capacity stores nothing.
- Overwriting a key does not increase the size.
- A missing read does not change recency.
Follow-up
- What changes when values have byte sizes and a time-to-live?
- How would concurrent callers preserve recency and capacity?
Split text into bounded audio requests
Medium · Streaming · Boundary tests
Split an ordered sequence of sentences into requests of at most N characters without splitting a sentence. Reject an oversized sentence and preserve punctuation.
Approach
- Track the current character count, including separators. Flush before adding a sentence that would exceed the limit.
- Test empty input, an exact boundary and an oversized first sentence.
Follow-up
- How would streaming input change your memory bound?
Apply usage events once
Medium · Idempotency · State
Implement an in-memory usage accumulator keyed by account. Replayed event IDs must not add usage twice; reusing an event ID with different units must fail.
Approach
- Store the account, units and event ID together. Check an existing event before changing the total.
- Define behavior for negative units and account-scoped event IDs; test replay and conflicting payloads.
Follow-up
- How would you make event registration and balance update atomic in a database?
Count completed audio jobs
Medium · SQL · Aggregation · Time boundaries
Given jobs(id, account_id, status, created_at), count completed jobs per account in the UTC interval [2026-09-01, 2026-09-02). Timestamps are stored in a consistent ISO text format.
Approach
- Use a half-open time interval to avoid double-counting midnight.
- Filter status before grouping.
- Keep the same tie-breaking order for reproducible results.
Worked solution (30 min)
- Use a half-open time interval to avoid double-counting midnight.
- Filter status before grouping.
- Keep the same tie-breaking order for reproducible results.
SELECT account_id, COUNT(*) AS completed_jobs
FROM jobs
WHERE status = 'completed'
AND created_at >= '2026-09-01T00:00:00Z'
AND created_at < '2026-09-02T00:00:00Z'
GROUP BY account_id
ORDER BY completed_jobs DESC, account_id;
Expected result: For account a with two completed jobs and b with one, return (a, 2), (b, 1). Accounts without completions are omitted.
Verify it yourself
- Exclude a job at the upper boundary.
- Exclude failed and pending jobs.
- Discuss timestamp types and indexes for the production database.
Follow-up
- How would you include accounts with zero completed jobs?
- What changes when accounts use different reporting time zones?
Reconcile completed jobs with usage records
Medium · Joins · Data integrity
Given jobs(id, account_id, status) and usage(job_id, units), return completed jobs with either no usage row or more than one usage row.
Approach
- Group usage by job_id before joining so duplicate records remain visible.
- Left-join completed jobs and filter for a count other than one.
Follow-up
- How would you reconcile records arriving after the reporting cutoff?
Make a retried request safe
Medium · Reliability · APIs
A client retries an audio-generation request after a timeout. How do you prevent duplicate jobs while still allowing safe retries?
Approach
- Accept an idempotency key scoped to the authenticated account.
- Atomically record a request fingerprint and job ID before dispatching work.
- Return the recorded job for an identical retry; reject key reuse with different input.
Worked solution (45 min)
Design a job API that accepts text, generates audio asynchronously, and lets a client recover after losing the response.
- API: accept an account-scoped idempotency key and return a job ID.
- Database: persist job state and an outbox event in one transaction.
- Dispatcher: retry publishing outbox events; workers must tolerate duplicates.
- Worker: write output to object storage, then record a durable completed state.
- Client: poll the job endpoint or subscribe to a completion event.
Expected result: A retry with identical input returns the same job. A duplicated queue message does not create a second charge or overwrite a completed result.
Verify it yourself
- Define pending, running, completed and failed states.
- Recover from worker death between upload and status update.
- Separate latency goals from throughput goals.
Follow-up
- How do you recover if persistence succeeds but queue publication fails?
Design resumable audio delivery
Hard · Object storage · APIs
Design delivery for generated audio when a mobile client disconnects. Cover authorization, partial downloads, retries and expired access.
Approach
- Separate job status from an immutable audio object; scope access to the authenticated account.
- Define resumption, cache behavior and object retention, then explain a failure during download.
Follow-up
- What changes if an object must be revoked immediately?
Investigate a slow audio pipeline
Hard · Observability · System design
Time to first audio increased after a release, but median generation time did not. Plan the investigation.
Approach
- Break the path into request, queue, generation and delivery spans.
- Compare percentiles by region, payload length and worker version.
- Test whether queue wait or connection setup explains the regression before changing the model.
Follow-up
- Which signal would make you roll back?
- How would you detect the same problem before a full rollout?
Find a retry-related duplicate
Medium · Concurrency · Reliability
Two workers process the same job. Both see pending and generate audio. Explain why a read-then-write state check is insufficient.
Approach
- Reproduce two reads before either worker writes.
- Replace the check with an atomic conditional claim.
- Check affected row count; only the successful claimant continues.
- Use a lease and an idempotent finalization step for crash recovery.
Worked solution (25 min)
- Reproduce two reads before either worker writes.
- Replace the check with an atomic conditional claim.
- Check affected row count; only the successful claimant continues.
- Use a lease and an idempotent finalization step for crash recovery.
UPDATE jobs
SET status = 'running', worker_id = :worker_id
WHERE id = :job_id AND status = 'pending';
-- Continue only when affected_rows == 1.
-- Add a lease + recovery policy for worker crashes.
Expected result: For two concurrent initial claims, exactly one succeeds. A lease-recovery design is still needed for crashes.
Verify it yourself
- Do not mistake an atomic claim for end-to-end exactly-once processing.
- Verify duplicate completion and billing paths.
- Test crash recovery separately from the initial claim.
Follow-up
- How would you reclaim a job after the successful worker crashes?
- How would you keep completion and billing idempotent?
Suggested practice checklist
Use 14 focused sessions to practise the codebase change, feedback revision and supporting skills. Time estimates describe study effort.
Day 1 — Map the role
- Read the posting for your exact team.
Deliverable: A one-page role and constraints map
Day 2 — Read an unfamiliar codebase
- Find entry points and test commands.
Deliverable: A request-flow sketch
Day 3 — Refresh cache fundamentals
- Solve the bounded-cache prompt.
Deliverable: A complexity and edge-case checklist
Day 4 — Implement and test
- Build the LRU exercise.
Deliverable: A working cache with meaningful checks
Day 5 — Practice SQL boundaries
- Run the job-count query.
Deliverable: A verified result table
Day 6 — Make retries safe
- Answer the retry prompt.
Deliverable: An idempotency contract
Day 7 — Review week one
- Revisit the weakest exercise.
Deliverable: Three concrete gaps to address
Day 8 — Design the job pipeline
- Trace data through the asynchronous design.
Deliverable: A design with explicit state transitions
Day 9 — Debug concurrency
- Reproduce the two-worker race.
Deliverable: A reproducible failure and bounded fix
Day 10 — Measure the slow path
- Work through the latency investigation.
Deliverable: A trace-based investigation plan
Day 11 — Prepare ownership stories
- Outline a correctness tradeoff.
Deliverable: Two specific behavioral stories
Day 12 — Run a timed simulation
- Choose an unfamiliar practical task.
Deliverable: A reviewed change with a short walkthrough
Day 13 — Revise after feedback
- Ask for critique or review against your rubric.
Deliverable: A before-and-after decision note
Day 14 — Final readiness check
- Repeat the hardest explanation.
Deliverable: A concise interview-day checklist
Show how you own the outcome
Practise stories about correctness, feedback and reducing manual operational work.
- Use a real incident, a review disagreement and an automation decision from your experience.
Explain a correctness tradeoff
Easy · Behavioral · Reliability
Describe a change where you reduced scope to ship safely. Explain the invariant you protected and the evidence you gathered.
Approach
- State the user impact and the constraint.
- Explain the narrower implementation and what you postponed.
- Show a check or measurement that supported the release decision.
Follow-up
- What changed after feedback?
Disagree constructively in a review
Easy · Feedback · Judgment
Describe a time you kept a design choice after feedback. What evidence changed the discussion?
Approach
- Explain the reviewer’s concern fairly, then show the test or constraint behind your decision.
- State what you changed anyway and how you checked the outcome.
Follow-up
- What evidence would have made you reverse your decision?
Make an owned service need less manual work
Easy · Ownership · Operations
Describe recurring operational work you automated. Explain the human decision you retained and the failure signal you added.
Approach
- Quantify the repeated task using records you actually have.
- Explain automation boundaries, escalation and how ownership passed to someone else.
Follow-up
- How did you know the automation stayed correct after you moved on?