Moveworks builds products that use artificial intelligence to automate IT support and other business processes. The Software Engineer role centres on designing and implementing the systems behind those products. The listed responsibilities are building software that meets user needs, defining project requirements with cross-functional teams, taking part in code reviews, and troubleshooting and resolving system issues.
The must-have skills listed for the role are proficiency in Python, Java or JavaScript, experience with cloud services such as AWS or Azure, and a strong grasp of data structures and algorithms. Familiarity with machine learning concepts, Agile methods and frontend frameworks such as React or Angular are listed as nice-to-haves. The reported questions split the same way. On the algorithms side there are the longest substring without repeating characters and binary search. On the design side there are serving millions of requests per second, building a real-time data processing pipeline and keeping data consistent in a distributed system.
Candidates describe three stages: a recruiter screen, a set of technical interviews covering coding and system design, and a cultural fit assessment. The loop section below takes each stage in turn. The question list pairs the reported prompts with original practice questions, and the seven-day plan maps onto the same stages.
Recruiter Screen
reportedHalf of this call is the part candidates treat as small talk: start date, notice period, work authorisation and its timing, location and time zone, on-call, and the number. Those are what kill offers late, after several engineers have each spent a day. Surfacing a hard constraint now costs you nothing and occasionally buys you something, since a loop compressed to fit a competing deadline can usually only be arranged if it is asked for early. The common failure is deflecting the compensation question twice, then discovering at offer stage that the band never reached your number.
What to demonstrate
- Whether your hard constraints are compatible with the role before a loop gets booked: earliest start, notice period, what authorisation you hold and when it needs action, days on site, willingness to carry a pager
- Whether you give a compensation range with something behind it, such as current total compensation or a competing timeline, rather than leaving the band untested
- Whether your stated timeline is real, since a competing deadline raised now is something scheduling can sometimes work around and the same deadline raised at offer stage usually is not
How to prepare
- Write each constraint down in one line before the call and state them as facts rather than negotiating them live under a question you were not expecting
- Set your range from two or three current data points for that level and location, and name the structure you are quoting in, so the number is comparable to the one they are holding
- If another process is running, say where it stands and by when, and ask directly whether this loop can be scheduled inside that window
Technical Interviews
reportedCandidates describe this stage as several technical interviews that include coding challenges and system design discussions, so prepare for both rather than betting on one. Reported coding prompts are standard algorithm problems: the longest substring without repeating characters, and a binary search implementation. Related bank titles add graph traversal with BFS and DFS, parsing mathematical expressions and Jaccard similarity between two strings. Reported design prompts cover a system handling millions of requests per second, a real-time data processing pipeline and data consistency in a distributed system. In coding, get a correct version running first and trace it on empty input, a single element and repeated values before you call it finished. In design, fix the scope and the load numbers before you name any component, and say what fails first as traffic grows.
What to demonstrate
- Whether your code is correct on inputs nobody showed you, including empty input, one element and repeated values, and whether the complexity you state matches what you wrote
- Whether a sliding-window or binary-search solution keeps its invariant at the boundaries, which is where off-by-one errors live
- Whether a design answer is built from stated numbers (request rate, read/write split, data size) rather than a list of components
- Whether you can name the consistency guarantee each piece of data needs and what you would give up to keep the system available when part of it fails
How to prepare
- Solve the longest substring without repeating characters with a last-seen map, moving the left edge to max(left, last[c] + 1), and explain why the max is required: a repeat seen before the current window must not pull the window backwards
- Write binary search as 'first index where a predicate holds', put the loop invariant in a comment above the loop, and test it on an empty array and on arrays where the predicate is all true or all false
- For the millions-of-requests prompt, do the capacity arithmetic out loud: per-instance throughput, instance count, which tier is stateless, and where caching or partitioning takes load off the store
- For the consistency prompt, pick one concrete invariant (for example, a record updated by two services) and walk through the mechanism that protects it: a single writer, idempotency keys, a transactional outbox or a saga with compensations
Cultural Fit Assessment
reportedCandidates describe this stage as an evaluation of fit with Moveworks' core values and culture. This guide does not list those values, so read the company's own published material before the interview and pick out the parts you can connect to real work you have done. The reported behavioral prompts are practical: a challenging project and how you got past its obstacles, how you prioritise several deadlines at once, how you helped a teammate succeed, and a trade-off between features and performance. Other reported prompts ask what collaboration means to you in practice and how you worked with a difficult team member. Answer each one with a specific episode: what you decided, what it cost, and what changed because of it.
What to demonstrate
- Whether your stories describe decisions you made yourself, not work you watched someone else lead
- Whether you can explain how you chose between competing deadlines and who you told, with a concrete example
- Whether a collaboration answer, including one about a difficult teammate, describes what you actually did rather than a general principle
- Whether you can link your own working style to what the company says about itself, using its own words rather than guesses
How to prepare
- Write one story for each reported prompt (challenging project, several deadlines, helping a teammate, feature-versus-performance trade-off, difficult teammate) and make sure no two rely on the same project
- For each story, write a short line for the situation, one for your decision, and one for the measurable result, and add what you would do differently
- Read the company's published description of itself and its values, and note two points you can support with a story from your own work
- Rehearse answering 'what does collaboration mean to you' with an example first and the principle second
PracHub editorial advice for the preparation topics above.
Answering 'describe your experience with cloud computing platforms' with a list of service names
This is a reported domain question, and the role lists AWS or Azure experience as a must-have. A list of services tells the listener nothing about depth. Pick one system you ran in the cloud and say what it did, which managed services it depended on, one thing that failed or got expensive, and what you changed. Prepare the same kind of short, specific answer for the other reported domain questions: how you ensure code quality and maintainability, and how you have optimised application performance. For performance, name the measurement that showed the bottleneck before you describe the fix.
Writing the sliding window for longest substring without repeats so the left edge can move backwards
The usual bug is setting left = last[c] + 1 unconditionally. When the repeated character was last seen before the current window, this pulls the window backwards and lets in a duplicate you already excluded. Use left = max(left, last[c] + 1), update last[c] = i, and take the best of i - left + 1. That runs in O(n) time with space bounded by the alphabet. Before you say you are done, trace 'abba' (answer 2), an empty string (0) and a single character (1).
Answering 'millions of requests per second, what components would you include' with boxes and no arithmetic
Naming a load balancer, a cache and a database does not answer the question. Start from the numbers. Ask for, or state, the request rate, the read/write split, the payload size and the latency target. Then work out how many instances the stateless tier needs, what hit rate the cache must reach to protect the store, and how the store is partitioned. Name the partition key and the query it makes expensive, then say what breaks first at ten times the load and what you would shed to stay up.
Saying 'use eventual consistency' or 'use distributed transactions' as a blanket answer to the data consistency prompt
The reported prompt asks how you keep data consistent in a distributed system, and a related bank question asks about several services updating the same data. A single label for the whole system does not answer either. Name the specific invariant at stake, for example two services changing the same record. Choose a mechanism for that invariant, such as one owning writer, idempotency keys on retries, a transactional outbox for publishing changes, or a saga with explicit compensations, and describe what a reader sees while the system is converging. Then take one failure, such as a crash after the write but before the publish, and show that your design recovers from it.
Answering cultural fit questions with principles and no episode
The reported prompts ask what collaboration means to you, how you worked with a difficult team member, and how you helped a teammate succeed. An answer that stays abstract ('I value open communication') gives the interviewer nothing to assess. Lead with one specific episode: what the disagreement or the need was, what you did, and what changed. Then state the principle in one sentence. Do not describe the company's values from guesswork. Read its own material beforehand and quote only what it actually says.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Write a function to find the longest substring without repeating chara…
Write a function to find the longest substring without repeating characters.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- State the target complexity and say which constraint rules the naive version out.
- Walk one small example through your approach before writing the whole thing.
Follow-up
- Which test case would catch an off-by-one here?
- How does this change if the input no longer fits in memory?
Parse and verify a timestamped multi-signature webhook header
An inbound webhook carries a signature header of at most 1 KiB shaped t=<unix seconds>,v1=<64 hex chars>, with up to five v1 values during secret rotation and possibly unknown scheme keys. You hold the raw request body bytes and the currently active signing secrets. Write the parser and the verifier: accept when any active secret reproduces a signature and the timestamp is within a five-minute tolerance in either direction, reject otherwise. Single left-to-right pass over the header, no regular expression. State what is inside the MAC and why.
Approach
- Parse in one scan: split on
,, then on the first=only, since a value may itself contain=under a future scheme. Accepttexactly once and treat a secondtas a reject rather than last-wins. Push everyv1onto a short list and ignore any other key, so av2can be introduced later without breaking this verifier. - Say what is signed: HMAC-SHA256 over the exact byte string
<t>.<raw body bytes>, yielding 32 bytes or 64 hex characters. The timestamp sits inside the MAC because otherwise an attacker replays yesterday's body with its still-valid signature and only has to edit the header timestamp. - Hash the bytes as received. Verifying against a re-serialised JSON body is the usual defect: key order, whitespace and number formatting all change the bytes while the parsed objects compare equal, so signatures fail for honest senders and the popular 'fix' is to stop checking.
- Compare in constant time over fixed-length digests. Decode the hex to 32 bytes, accumulate
acc |= a[i] ^ b[i]across the whole length, and testacc == 0at the end. Evaluate every candidate without an early exit; at five candidates that is five HMACs over the body, linear in body size and negligible beside the network. - Apply the tolerance as a two-sided bound, rejecting when
|now - t| > 300seconds. A sender whose clock runs ahead of yours is an ordinary case, and an unbounded future timestamp is a free replay window. - Complexity: O(L) over the header producing k candidates, plus k HMACs at O(|body|) each. Space is O(k) beyond the body itself. Do the cheap rejections, including the tolerance check, before any cryptography runs.
Follow-up
- The body is 40 MB. What changes about where you verify, and what can you do before the whole body has arrived?
- A customer reports that signatures fail for exactly the requests whose body contains a non-ASCII character. What is your first hypothesis?
- How do you rotate the signing secret with no failed deliveries, and how long do both secrets stay live?
Schedule ordered webhook retries with a heap of subscription queues
Design the in-memory scheduler for webhook delivery. Up to 20 million rows sit in status pending or failed_retryable across 200,000 subscriptions, each row carrying next_attempt_at and attempt_count, and each endpoint having a circuit breaker. Deliveries for one subscription must be attempted in order, so at most one attempt per subscription may be in flight. Support due(now), complete(delivery, outcome) and insert(delivery) in O(log S), where S is the subscription count rather than the delivery count. Give the backoff formula you schedule retries with.
Approach
- Key the global heap by subscription, not by delivery. Each subscription owns a FIFO of its due deliveries in event order; the heap holds one entry per eligible subscription, keyed by its head's
next_attempt_at. That is 200,000 heap entries instead of 20 million, and it makes the one-in-flight rule structural rather than a check somebody can forget. due(now): peek the minimum. If its key is in the future, sleep until then instead of spinning. Otherwise pop it, move the subscription into an in-flight set, and do not re-push it. A subscription absent from the heap cannot be dispatched twice, which is precisely how ordering is preserved.complete: on success, drop the head and re-push the subscription keyed by its new head, or leave it out when the queue empties. On a retryable failure, incrementattempt_countand setnext_attempt_at = now + uniform(0, min(cap, base * 2^attempt)), sampled uniformly across the whole interval. That is full jitter; deterministic backoff re-synchronises the herd you just created.- Circuit breaker: park the subscription in a second heap keyed by its half-open time, so an endpoint dead for six hours costs one heap entry and zero attempts rather than consuming worker slots. Admit exactly one probe at half-open and close the breaker only on its success.
- Say the price of the ordering guarantee out loud. One in-flight attempt per subscription means an endpoint answering in 10 seconds drains at 0.1 deliveries/second however many workers you run, and its backlog grows until it recovers. If the customer does not need order, allow k in flight and document delivery as unordered; that is the trade, and it is a product decision.
- All three operations are O(log S) with O(S) resident heap memory and the queues themselves backed by the store. The database-backed equivalent is a partial index on
(subscription_id, next_attempt_at) where status in ('pending','failed_retryable')claimed withFOR UPDATE SKIP LOCKED, and the write-back must be fenced onlease_tokenso a worker that stalled and resumed cannot overwrite a newer attempt.
Worked solution 30 min
- Define the four structures explicitly:
queues: subscription_id -> deque[delivery],ready: min-heap of (next_attempt_at, subscription_id),inflight: set[subscription_id],breaker: min-heap of (half_open_at, subscription_id). - Write down the invariant you will assert after every operation: a subscription appears in at most one of
ready,inflightandbreaker, never in two. - Implement
due,completeandinsert, then simulate 200,000 subscriptions with Zipf-distributed queue depths totalling 20 million deliveries. - Add one endpoint that always times out after 10 seconds and one that always answers in 20 ms, then measure the fast endpoint's throughput with and without the per-endpoint breaker.
- Instrument heap size across the run.
Follow-up
- One subscription has 4 million queued deliveries. What stops it from starving the other 199,999, and what does your heap look like under that load?
- A customer requests redelivery of last Tuesday's events. Where do those rows enter your structure, and what keeps them from reordering live traffic?
- The process restarts. How much state do you rebuild, and what stops every subscription from being attempted in the same second?
Paginate a tenant's delivery export without skipping rows
A customer exports webhook_delivery: delivery_id (bigint identity), subscription_id, tenant_id, event_id, status, attempt_count, next_attempt_at, created_at, delivered_at, updated_at. The endpoint runs select ... where tenant_id = $1 order by created_at desc limit 100 offset $2, and customers report rows missing from exports taken while new deliveries are being inserted. Write the replacement query and the index that supports it, paging a tenant's deliveries newest first at constant cost per page. State why updated_at cannot be the cursor column.
Approach
- Name the defect precisely. OFFSET is a position in a result set that is recomputed on every request, so a row inserted ahead of the window shifts everything back by one and the next page starts after a row the client never received. Nothing errors and no identifier gap appears, so the loss is silent.
- Replace the position with a value predicate over a stable, unique, indexed ordering:
where tenant_id = $1 and (created_at, delivery_id) < ($2, $3) order by created_at desc, delivery_id desc limit 100. The row comparison is load-bearing: created_at alone is not unique, so ties straddling a page boundary are dropped or repeated, which is the same bug in a smaller window. - Index
(tenant_id, created_at, delivery_id). PostgreSQL scans a btree in either direction, so an all-DESC ORDER BY is served by an ASC index read backwards and no DESC modifiers are needed; they only matter when the ORDER BY mixes directions. Confirm the plan has no Sort node above the index scan, or the LIMIT stops being an early exit. - Price both forms: keyset is one index descent plus 100 adjacent leaf entries per page, constant regardless of depth, while OFFSET still produces and discards every skipped row, so page N costs time proportional to N times the page size and a deep page on a large table goes from milliseconds to seconds.
- Rule out updated_at as the cursor from the precondition, not from taste: a cursor column must never change value for a row already paged past. updated_at moves on every delivery attempt, so a row the client already emitted re-enters a later page and is exported twice. created_at and delivery_id are immutable, which is the whole qualification.
Follow-up
- The client wants a snapshot as of one instant rather than a live tail. Compare a repeatable-read transaction held open, an added
created_at <= $snapshotbound, and a materialised export table. - A retention job deletes deliveries older than 90 days. What does a client mid-walk see, and does keyset pagination help at all?
- The customer wants to resume an export from yesterday's last cursor. What must be true of the cursor for that to be safe?
Find the join that inflates every invoice total
invoice_line_item holds line_id, invoice_id, tenant_id, sku, rate_tier, quantity, unit_price_micros, amount_minor (bigint), currency, kind, voided_at. invoice_payment_attempt holds attempt_id, invoice_id, tenant_id, amount_minor, status (succeeded, failed, pending), created_at, and an invoice has many attempts. A finance report runs select i.invoice_id, sum(l.amount_minor), count(p.attempt_id) from invoice i join invoice_line_item l using (invoice_id) join invoice_payment_attempt p using (invoice_id) group by 1 and the totals are wrong. Say precisely what the sum now equals, and write a version that is also correct for invoices with zero attempts.
Approach
- Compute what the query actually returns before fixing it. The two joins form a Cartesian product per invoice, so each line row repeats once per attempt row:
sum(l.amount_minor)is the true total multiplied by the attempt count, andcount(p.attempt_id)is attempts times lines. Three lines and two attempts report double the money and six attempts. - Reject the reflex repair.
count(distinct p.attempt_id)does fix the count, because attempt_id is unique.sum(distinct l.amount_minor)does not fix the sum, because two legitimate lines with equal amounts collapse into one. DISTINCT inside an aggregate deduplicates values, not rows, and the difference stays invisible until two lines happen to match. - Aggregate each branch to invoice grain before joining: one CTE summing lines by invoice_id, one counting attempts by invoice_id, then join the two results. A LATERAL subquery per invoice is equivalent and sometimes plans better when the outer set is small. Either way every aggregate stays at the grain it was defined at.
- Keep invoices with no attempts by making the attempt branch a LEFT JOIN with
coalesce(attempt_count, 0). An inner join here silently drops every unpaid invoice, which is usually the exact population finance is asking about. - Push each filter to its own grain:
where l.voided_at is nullbelongs inside the line CTE, not the outer query, or it would also filter the attempt branch through the join. Put the tenant predicate on both branches, since the denormalised tenant_id is what stops a wrong join crossing tenants. - Leave yourself a standing check: an invoice total is a function of its non-voided lines and of nothing about payments, so if changing the payment filter moves the money figure, the fan-out is back.
Worked solution 25 min
- Create one invoice with three lines of 1000, 1000 and 500 minor units and two payment attempts, then run the original query.
- Confirm it reports 5000 and 6 rather than 2500 and 2.
- Apply
sum(distinct l.amount_minor)and confirm the total becomes 1500, which is worse rather than better. - Write the two-CTE version with a LEFT JOIN and coalesce, and confirm 2500 and 2.
- Add a second invoice with lines and no attempts and confirm it still appears.
Follow-up
- Add a third branch for credit notes applied to the invoice. Does the CTE shape still hold, and when would a single pass with
filter (where ...)be better? - Over 500k invoices this report takes minutes. Which grain would you materialise, and how do you keep it correct when a line is voided?
- The same report is needed per tenant per month. What index makes the line CTE cheap?
Explain how you would handle data consistency in a distributed system.
Explain how you would handle data consistency in a distributed system.
Approach
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- What breaks first when traffic grows ten times?
- What would you drop to keep the system up under load?
Design a system that can handle millions of requests per second. What …
Design a system that can handle millions of requests per second. What components would you include?
Approach
- Choose a partition key and say what query it makes expensive.
- Name the failure you are designing for, then the recovery path.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- What breaks first when traffic grows ten times?
- What would you drop to keep the system up under load?
How would you approach designing a real-time data processing pipeline?
How would you approach designing a real-time data processing pipeline?
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- State the consistency you need, and where you are willing to be stale.
- Choose a partition key and say what query it makes expensive.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
How do you ensure code quality and maintainability in your projects?
How do you ensure code quality and maintainability in your projects?
Approach
- Clarify what is being asked and what a complete answer contains.
- Work from the requirement backwards to the design.
- State your assumptions explicitly before working the problem.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
How would you approach debugging a complex system issue?
How would you approach debugging a complex system issue?
Approach
- State your assumptions explicitly before working the problem.
- Work from the requirement backwards to the design.
- Clarify what is being asked and what a complete answer contains.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Authorisation cache with a bounded revocation window
The edge gateway serves about 30k requests/second from roughly 120 pods across three regions and may add no more than 10 ms at p99. Each request presents an API key that must resolve to an authorisation context: tenant, workspace, scopes, entitlements and credential version. The control plane that owns those rows takes tens of writes/second. A revoked credential must stop authorising within a bound you state as a number. Design the cache - what is keyed, what invalidates it, how many tiers - and specify what the gateway does for the duration of a control-plane outage.
Approach
- Fix the entry shape before the topology: key on SHA-256 of the presented secret, value is the resolved context plus the principal's auth_version and a fetched_at. Cache negative lookups too, with a much shorter TTL and a bounded-size structure, because otherwise every sprayed invalid key is a control-plane round trip, and unbounded negative entries let a sprayer evict live ones.
- Compute the control-plane read load before choosing a TTL, and notice the multiplier is pods, not regions, when the cache is in-process: distinct_active_keys x pods / TTL. At 50,000 active credentials, 120 pods and a 60 s TTL that is 100,000 reads/second against a single-writer primary with read replicas, which is not serviceable - so the design needs two tiers, a per-region shared cache in front of the control plane with the in-process cache held to a few seconds.
- State the bound as the sum of the tiers, not as a hope: with a 10 s in-process TTL over a 60 s regional TTL, worst-case staleness absent any invalidation message is 70 s, because an in-process entry can be filled from a regional entry that was itself about to expire. Publish-subscribe invalidation on every credential and entitlement mutation makes the typical case sub-second, but it is lossy under partition, so the TTL is the only enforced bound and both tiers must subscribe.
- Make auth_version propagate through the same path: a password reset or sign-out-everywhere bumps the principal and revokes its keys with no hook of its own, so the invalidation publisher has to expand principal -> credentials and publish per key, or the cache keeps serving keys whose auth_version no longer matches.
- Decide the partition behaviour in advance and write it as two rules: on a cache hit past TTL, serve from the stale entry up to a grace ceiling (say 10 minutes); on a cache miss, refuse, because authorising something never seen converts a control-plane outage into an authorisation bypass. Worst-case revoked-key lifetime during an outage is then TTL + grace, about 11 minutes, and that number is the price of not turning a control-plane outage into a total data-plane outage.
- Protect the refill path: per-key single-flight so a mass invalidation or a cold pod does not stampede the control plane, TTL jitter so entries created together do not expire together, and a small separately replicated deny-list for compromised keys that is consulted on the hot path and survives control-plane loss.
Worked solution 35 min
- Write the cache entry shape - key, value fields, and which of those fields a request actually reads on the hot path - and mark which field makes a password reset propagate.
- Compute control-plane reads/second for TTLs of 10 s, 60 s and 300 s using distinct_keys x cache_instances / TTL, once with cache_instances = 3 regions and once with cache_instances = 120 pods, and note which of the two the in-process design actually implies.
- Enumerate the four states a revocation can be in - published and received, published and dropped, control plane unreachable, pod started after the publish - and write which entry serves the next request in each.
- Write the outage policy as two rules (hit past TTL within grace: serve; miss: refuse) and compute worst-case revoked-key lifetime as the sum of both tier TTLs plus the grace.
Follow-up
- A key is found in a public repository and must stop working in seconds, not minutes. What changes, and what does it cost on the request path?
- One region is partitioned from the control plane while the control plane itself is healthy. What do that region's pods do, and how do you distinguish this from a control-plane outage?
- How would you measure the actual revocation bound in production rather than asserting it from the configuration?
Invoice detail latency triples after an ORM relationship refactor
An invoice detail endpoint returned in 40 ms at p99 last week. After a refactor replaced a hand-written join with ORM relationship access it returns in 1.4 s, and the regression grows with the number of invoice_line_item rows on the invoice. Database CPU rose, but no statement in the slow-query log exceeds 3 ms. You have request traces with per-span SQL, the ORM statement log, and a staging copy of the data. Produce an ordered diagnostic checklist, the measurement that confirms the cause before any code change, and the fix.
Approach
- Count statements per request before reading any statement duration. A slow-query log hides this class by construction, because every individual query is fast and only their number is wrong; take one trace and count SQL spans.
- Establish proportionality rather than asserting it: sample invoices with 5, 20, 60 and 200 line items and plot statements per request against line count. A straight line of slope 1 through an intercept of one or two identifies a lazy relationship load, and no index or cache would move that line.
- Locate the emitting attribute access in the refactored code and check whether the same shape repeats one level deeper, for instance a tax or adjustment collection hanging off each line, which turns the cost quadratic.
- Fix with a bounded statement count: either one join that fetches invoice and lines together, or two statements where the second is WHERE invoice_id = $1 AND tenant_id = $2. Keep tenant_id in the predicate so the read stays tenant-scoped even though invoice_id already implies it.
- Choose between the two deliberately: the join duplicates the wide parent row across N children on the wire, the two-statement form avoids that for one extra round trip. Prefer the join for narrow parents and the split for wide ones.
- Pin it with a per-request statement-count assertion in a test that varies line count, because a latency assertion passes on a small fixture and would not have caught this.
Follow-up
- The endpoint now also needs per-line tax rows. Show the shape that keeps statement count constant instead of reintroducing the same defect one level down.
- How does this change if a transaction-pooling proxy sits between the service and the database, so each statement may land on a different backend session?
- The same page paginates invoices with LIMIT and OFFSET. Why is that a second, independent defect, and what replaces it?
For someone who has spent the last few years shipping features and reading other people's code, and who has not solved a timed problem from a blank file in a long time. Five days rebuild the primitives and the patterns that sit on them, working from invariants rather than remembered solutions, and the last two attach that back to the rest of the loop.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Recruiter screen logistics and reported technical/domain questions
- Write your hard constraints as one-line facts (start date, notice period, work authorisation, location, remote expectations) and a compensation range backed by current data points, ready for the recruiter screen
- Prepare a short, specific answer to the reported question 'describe your experience with cloud computing platforms' built around one system you ran, since AWS or Azure experience is listed as a must-have
- Prepare answers to the other reported domain questions: how you ensure code quality and maintainability, and what strategies you use to optimise application performance, each with one measured example
- Decide which of Python, Java or JavaScript you will code in, and ask the recruiter to confirm it is accepted
Deliverable: A one-page sheet: recruiter-screen constraints and a compensation range with its basis, plus three rehearsed answers to the reported technical/domain questions.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Coding: strings, hash maps and binary search
- Solve the reported longest substring without repeating characters (reported-algorithms-1) with a last-seen map and the max(left, last[c] + 1) update, then trace 'abba', an empty string and a single character by hand
- Implement binary search as 'first index where a predicate holds', write the invariant above the loop, and test the empty, all-true and all-false cases
- Work the bank's Jaccard similarity between two strings problem: first settle whether the sets are characters, words or shingles and what two empty inputs return, then write it
- For each solution, state the time and space complexity before running it and compare with what the code actually does
Deliverable: Three solved problems, each with its invariant written down and the edge cases you traced before running it.
Practice prompt ↗Practice prompt ↗03Coding: graphs, parsing and word-game search
- Implement BFS and DFS over one adjacency list and return the visit order for each, matching the bank's graph traversal problem; note which one finds shortest paths in an unweighted graph
- Write an evaluator for mathematical expression strings with operator precedence and parentheses, and test it on unary minus, nested parentheses and division
- Work the bank's word-game problems (selecting the next Hangman letter, finding a secret word via match feedback), and for each write down how the candidate set shrinks after each guess
- Work through the worked exercise for drill-coding-4 (ordered retries with a heap of subscription queues) and check that you can explain why the heap is keyed by subscription
Deliverable: Working graph traversal and expression evaluator, two word-game solutions, and a written note on the drill-coding-4 heap invariant.
Practice prompt ↗Practice prompt ↗04System design: throughput and availability
- Answer the reported prompt to design a system handling millions of requests per second (reported-systemdesign-3) with the arithmetic first: request rate, read/write split, instance count, cache hit rate needed, partition key
- Work the bank question on availability during partial failures: name the dependency that fails, how the system degrades gracefully, and what it stops doing to stay up
- Work through the worked exercise for drill-design-5 (authorisation cache with a bounded revocation window) and recompute its read-load numbers yourself
- For each design, write what breaks first at ten times the load
Deliverable: Two design sketches, each with capacity numbers, a named first bottleneck and a stated degradation policy.
Practice prompt ↗Practice prompt ↗Worked solution ↗05System design: pipelines, consistency and search
- Answer the reported real-time data processing pipeline prompt (reported-systemdesign-4): ingestion, partitioning, late or duplicate events, replay, and where results are served
- Answer the reported data consistency prompt (reported-systemdesign-2) around one concrete invariant, and walk through a crash after the write but before the publish
- Sketch an inverted-index search design from the bank: index layout, sharding, and how updates reach the index
- Work through the worked exercise for drill-sql-2 (join fan-out in an aggregate) as a correctness check you can apply to any data-processing answer
Deliverable: Pipeline and consistency answers, each with one failure case walked through to recovery, plus a search index sketch.
Practice prompt ↗Practice prompt ↗06Debugging and trade-off questions
- Answer the reported 'how would you approach debugging a complex system issue' prompt (reported-other-8) as an ordered checklist: reproduce, measure, form one hypothesis, confirm it before changing code
- Practise that checklist on drill-debugging-6 (latency regression after an ORM refactor), writing down the measurement that confirms the cause before the fix
- Prepare the reported feature-versus-performance trade-off story (reported-behavioral-5): what you chose, what you gave up, and how you measured the result
- Prepare a story about explaining a technical trade-off to a non-technical stakeholder, a topic in the bank
Deliverable: A written debugging checklist applied to one drill, and two trade-off stories with measured outcomes.
Practice prompt ↗Practice prompt ↗07Cultural fit stories and a full mock
- Write one specific story for each reported cultural fit and behavioral prompt: challenging project, several deadlines, helping a teammate succeed, a difficult team member, what collaboration means in practice
- Read the company's own published material on its values and note two points you can support with your stories, quoting only what it says
- Run a mock: one timed coding problem from days 2-3, one design prompt from days 4-5, and three behavioral prompts, narrated out loud
- Re-solve from scratch the problem you were slowest on this week
Deliverable: A story bank covering every reported behavioral prompt and notes from one full mock covering coding, design and behavioral.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The reported behavioral prompts are concrete: a challenging project, several deadlines at once, helping a teammate, a feature-versus-performance trade-off, a difficult colleague. Answer each with one episode you owned. Say what you decided, what it cost and what changed as a result, then state the principle in a sentence. Use a different project for each story so one weak example does not show up in every answer.
Describe a time when you had to make a trade-off between features and …
Describe a time when you had to make a trade-off between features and performance. What did you choose and why?
Approach
- Name the disagreement and how you resolved it with evidence.
- Close with what you would do differently, concretely.
- Pick a story where you made the decision, not one where you watched it.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Describe a challenging project you worked on. How did you overcome the…
Describe a challenging project you worked on. How did you overcome the obstacles?
Approach
- Give the blast radius: what could have broken, and what you measured.
- Pick a story where you made the decision, not one where you watched it.
- Close with what you would do differently, concretely.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Estimate a tenant-leading index migration you have never run
Someone needs a date. usage_event carries an index on (occurred_at) and needs (tenant_id, occurred_at); the largest tenant holds roughly a hundred times the median tenant's rows, the table is partitioned daily with years of retention, and you have never run a migration on a table this large. Give an estimate you would defend: how you decompose the work, the two or three numbers you would go and measure first, the range and confidence you state, and what you commit to when the person asking needs a single date today.
Approach
- Refuse the bare number and then give one anyway, in the form that is actually useful: a range plus the measurement that collapses it. 'Four to eleven days; one afternoon building this index on a restored copy of the largest partition takes that to within a day' is an answer, while 'it depends' is not.
- Decompose by failure mode rather than into equal chunks, because that is where estimates go wrong. On a partitioned parent you create the index ON ONLY the parent, build each partition's index with CREATE INDEX CONCURRENTLY, then ALTER INDEX ... ATTACH PARTITION, at which point the parent index becomes valid. CONCURRENTLY does not block writes but scans each partition twice, waits out older transactions, cannot run inside a transaction block, and on failure leaves an invalid index you must drop concurrently and retry.
- Name the two unknowns that dominate and price them: build time on one restored partition of realistic size, and whether the planner actually chooses the new index for the skewed tenant, since selectivity for a tenant holding most of the rows is a different question from selectivity for the median tenant. Both are half-day measurements against a replica, and both are cheaper than being wrong by a week.
- State the assumptions the range is conditional on, because that is what makes a slip a re-estimate instead of a credibility event: no partition above a stated row count, one concurrent build at a time so it does not compete with ingest for I/O, and an ingest backlog that can absorb the added write amplification while both indexes exist.
- Budget the step nobody budgets: verification and the old index's removal. Dropping the old index is fast, but deciding it is safe to drop means confirming no plan still uses it, and that confirmation waits on real traffic across a full weekly cycle rather than on your patience.
- Answer the single-date request honestly. Commit to a date for the first checkpoint — the measured build number from the replica — and to re-estimating on that date, and say plainly what you are not committing to yet. A date with a scheduled re-estimate is worth more to the asker than a confident wrong one, and you should say why in those words.
Follow-up
- The concurrent build fails half way through the largest partition. What is the state of the database and what do you do next?
- Your estimate slips by sixty percent. Which assumption broke, and at what point would you have known?
- The person asking needs the date for a customer commitment. Does your answer change?
- 01
Describe a challenging project you worked on. How did you overcome the obstacles?
- 02
How do you prioritize tasks when you have multiple deadlines?
- 03
Give an example of how you have helped a teammate succeed.
- 04
Describe a time when you had to make a trade-off between features and performance. What did you choose and why?
- 05
Can you describe a time when you had to collaborate with a difficult team member?
- 06
What does collaboration mean to you, and how do you practice it in your work?
Is this an official Moveworks interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Moveworks. Rounds and questions reflect what candidates have reported, not a process Moveworks has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What stages do candidates report for the Moveworks Software Engineer interview?
Three stages: a recruiter screen, technical interviews that include coding challenges and system design discussions, and a cultural fit assessment. Candidate reports put the process at roughly three to five weeks. Ask your recruiter how many technical interviews your loop includes and what each covers.
PracHub Software Engineer practice ↗How difficult are the interviews at Moveworks?
Candidates report a mix of coding and system design questions that need solid technical fundamentals and clear problem-solving. The coding prompts are standard algorithm problems. The design prompts are about scale and correctness: high request volume, real-time pipelines and distributed consistency. Prepare for both instead of focusing on one.
PracHub interview research ↗What coding topics should I practise?
The reported prompts are the longest substring without repeating characters and a binary search implementation. Related bank questions cover graph traversal with BFS and DFS, parsing mathematical expressions, Jaccard similarity between two strings, and word-game problems such as choosing the next Hangman letter. Practise sliding windows, hash maps, binary search on a predicate, graph traversal and expression parsing, and trace edge cases before you call a solution finished.
PracHub Software Engineer practice ↗What system design topics come up?
The reported design prompts are a system handling millions of requests per second, a real-time data processing pipeline, and data consistency in a distributed system. Bank questions add availability during partial failures, event and feature pipelines, an inverted-index search engine and a system design question on an AI-assisted product. Start each answer from load numbers and one named invariant, not a list of components.
PracHub Software Engineer practice ↗Which programming language should I use?
The role lists proficiency in Python, Java or JavaScript as a must-have. Use the one you can write correctly without an IDE, and confirm with your recruiter that it is accepted in the coding interviews.
PracHub Software Engineer practice ↗How should I prepare for the cultural fit assessment?
Candidates describe this stage as assessing fit with the company's values and culture. This guide does not list those values, so read the company's own published material and link it only to stories you can back up. Prepare a specific episode for each reported prompt: a challenging project, several deadlines, helping a teammate, a difficult colleague, and what collaboration means to you in practice.
PracHub Software Engineer practice ↗Is remote work an option?
The sources do not settle this, and it may vary by team. Raise location, time zone and remote expectations with the recruiter in the first call so a mismatch comes up before the technical interviews are booked.
PracHub interview research ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24