Interview guides · ARRISE
ARRISE Software Engineer Interview Guide
ARRISE Software Engineer interview guide: reported rounds, DSA, Java concurrency, system design and frontend questions, with a seven-day prep plan.
The 60-second brief
Candidate accounts describe ARRISE engineering as work on transaction-heavy digital platforms. The stack they mention spans distributed back-end services, real-time data pipelines and interactive front-end screens. The Software Engineer role is described as building features across back-end services, APIs or front-end interfaces, tuning database queries, caches and memory use in production, taking part in design discussions, reviewing code, writing unit, integration and performance tests, and coordinating releases with product managers, QA engineers and DevOps teams.
This guide is for candidates with a few years of professional development experience (candidates describe the role as asking for 2+ years) who are preparing for the ARRISE Software Engineer loop. It covers the five reported rounds, every reported coding, Java concurrency, system design and frontend question, and the manager and HR conversations at the end. Backend candidates should weight the Java and design sections; frontend and full-stack candidates should add the React and browser rendering questions.
Candidates report five rounds over roughly 4-6 weeks: an HR screening call, an online assessment, three to four technical interviews, an Engineering Manager conversation and a behavioral HR discussion.
Focus areas
- Data Structures & Algorithms (DSA)
- Java
- High-Level Design (HLD)
Overview
The reported questions fall into four groups. The coding group is classic algorithm work: longest common subsequence, Three Sum with a two-pointer pass, a next-element problem on a circular array, recursive deletion of empty folders, and a medium-to-hard array and string problem under a 30-minute window. The Java group is about concurrency: a deadlock you write and then fix, the differences between synchronized blocks, ReentrantLock and Semaphore, thread pools and ExecutorService configuration for I/O-heavy work, race conditions on shared state, and what Java 8 streams, lambdas and Optional change. The design group covers a JIRA-style collaboration tool, a dish recommendation system for food delivery, a notification system that calls SMS and email gateways, distributed locking or rate limiting across microservices, and partitioning, replication and consistency under heavy writes. A fourth group, for frontend and full-stack roles, covers a custom React component or hook, rendering performance with requestAnimationFrame and CSS keyframes, client-side state and caching, and interactive elements such as gradient animations or simple game mechanics.
Candidates also report practical details that shape how you practise. Code is written in a shared IDE or on a whiteboard, Java multithreading code is expected to compile and run, and live coding without auto-complete is common advice. Candidates describe brute force first, then optimisation, as the way to present coding answers, and clarifying requirements before drawing components as the way to start a design. Topic lists attached to the reported rounds name arrays and strings, dynamic programming, trees and graphs, monitors and wait/notify, ConcurrentHashMap, CopyOnWriteArrayList, blocking queues, volatile and thread-local storage, service discovery, API gateways, SQL versus NoSQL, Redis or Memcached caching, sharding, and Kafka or RabbitMQ.
The requirements candidates describe name Java, C++ or modern JavaScript/TypeScript, relational databases and SQL, data structures, OOP and multithreading as must-haves, with Kafka, Redis, Docker, Kubernetes, AWS or GCP and CI/CD as extras. Because the role is described as one that tunes queries and caches in production, expect the design answers to be judged on concrete choices: which store, which cache, which queue, what happens on failure. Spend your first days on the coding set, move to Java concurrency where you must produce running code, then to design where you must justify every component, and keep the last days for the frontend set and the stories you will tell the manager and HR.
The ARRISE interview process
The sequence below is what candidates describe, not a published process. Some accounts say the online assessment comes before the HR call, and later stages are often reported to slip because panel availability is hard to coordinate.
HR Screening Call
Candidates describe a first call with HR about your background and fit for the role. No technical questions are reported for this stage, so the work is a clear account of your experience that lines up with the stack the role lists: Java, C++ or JavaScript/TypeScript, SQL and multithreading. It is also the moment to learn how the rest of the loop will run, since candidates report scheduling delays in the later stages.
What to demonstrate
- How your experience maps to the Software Engineer role and its listed languages
- Fit for the role as discussed on the call
- Clarity when you summarise your own projects
How to prepare
- Write a two-minute walk through your CV that foregrounds the projects using Java, SQL, concurrency or distributed services, and say what you personally built in each.
- Prepare questions for the call: how many technical interviews to expect, whether you can choose your language for coding, and whether the online assessment is timed.
- Note the notice period and location constraints you will need to state, and ask for the timeline in writing, because candidates report delays after the technical rounds.
Online Assessment
Candidates complete an online assessment meant to measure coding proficiency. The reports do not say which of the reported coding questions appear here, so treat the whole coding set as fair material: longest common subsequence, Three Sum, the circular-array next-element problem, recursive removal of empty folders, and a medium-to-hard array and string problem under a 30-minute window. Assume working code that handles edge cases matters more than a clever idea you cannot finish.
What to demonstrate
- Coding proficiency, which an online assessment tests through working code
- Correct handling of edge cases such as empty input, a single element and duplicates
- Choice of an efficient approach over a brute-force one
How to prepare
- Solve the longest common subsequence and Three Sum problems cold in a plain editor without auto-complete, then compare against a brute-force version you can state in one sentence.
- For the array and string problem, set a 30-minute timer and practise reading the problem, naming the brute force, choosing two pointers, a sliding window or a hash map, and coding it in one pass.
- After every solution, write your own tests: empty input, one element, all-equal values, duplicates and the largest size you can imagine.
Technical Rounds
Candidates report three to four intensive technical interviews covering coding, language fundamentals and system architecture. Code is written on a shared IDE or a whiteboard, and Java multithreading code is expected to compile and run. Reported topics include arrays and strings, dynamic programming, trees and graphs, monitors and locks, concurrent collections, the Java memory model, and microservice, data and messaging design. Frontend or full-stack candidates also report React and browser rendering questions. The reports do not say which question belongs to which interview, so prepare all four groups.
What to demonstrate
- Working, compilable code written live for algorithm and concurrency problems
- Java language and concurrency fundamentals, including locks, thread pools and the memory model
- Reasoning about distributed system design, with trade-offs justified
- For frontend roles, component structure and browser rendering performance
How to prepare
- Write the deadlock program and its fix from memory in Java: two threads, two locks taken in opposite order, then a consistent lock order or tryLock with a timeout. Compile and run it.
- Prepare one-minute comparisons of synchronized, ReentrantLock and Semaphore, and a sizing argument for an ExecutorService on I/O-heavy work.
- Practise two full designs out loud, such as the JIRA-style tool and the notification system, starting with requirements and ending with failure handling.
- If your role is frontend or full-stack, build a small React hook and an animation loop with requestAnimationFrame without looking anything up.
Engineering Manager Conversation
Candidates describe a discussion with an Engineering Manager to assess technical and team fit. The reports give no question list, so expect a conversation about work you have done, how you make design decisions and how you work with others. Candidate advice for the technical side is to defend any departure from standard architecture with evidence, and to ask about the team's stack and engineering challenges.
What to demonstrate
- Technical fit, shown through how you discuss systems you have built
- Team fit and collaboration with product, QA and DevOps colleagues
- How you justify design choices and respond to new constraints
How to prepare
- Pick one project and prepare its story: the problem, the architecture, one decision where you weighed two options, and the measured result.
- Be ready to explain an architectural choice you made with data, such as why you added a cache or a queue, and what you would change if the load grew.
- Write three questions about the team's stack and release process, drawn from the Kafka, Redis, Docker, Kubernetes and CI/CD tools the role lists.
Behavioral HR Discussion
Candidates describe a final conversation with HR about behavioral aspects and cultural alignment. No questions are listed, so prepare stories that show ownership, collaboration across teams and handling of ambiguous requirements, which are themes of the role as candidates describe it. Candidates also report slow scheduling near the end, so stay in contact with your recruiter.
What to demonstrate
- Behavioral examples of ownership and collaboration
- Alignment with the working style described for the team
- Communication when discussing conflict, ambiguity and change
How to prepare
- Prepare three stories in situation, action and result form: a production performance problem you fixed, a disagreement over a design, and a change in requirements late in a release.
- Quantify each result, such as latency before and after or error rate, and be clear about what you did versus what the team did.
- Prepare a short account of how you work with QA, product managers and DevOps colleagues, since the role is described as cross-functional.
Where candidates lose points
The deadlock example does not compile, or never actually deadlocks when run.
Write it in a plain editor and run it. Use two locks acquired in opposite order by two threads, with a short sleep or latch between the acquisitions to force the interleaving. Then show the fix: one global lock order, or tryLock with a timeout, and explain which you would choose.
Jumping to the optimal Three Sum or longest common subsequence solution without a baseline, or missing duplicates.
State the brute force first: Three Sum is O(n^3) over all triples, while sorting plus two pointers is O(n^2). For duplicates, skip equal values after each move. For longest common subsequence, give the O(m*n) table, then the rolling-row space reduction, and say what you lose when you must rebuild the sequence itself.
Reciting definitions of synchronized, ReentrantLock and Semaphore without saying when to use each.
Tie each tool to a scenario: synchronized for simple mutual exclusion, ReentrantLock when you need tryLock, timed or interruptible acquisition, or fairness, and Semaphore to limit concurrent access to a resource such as a connection pool.
System design answers that list Kafka, Redis and a database but never say what fails.
For each design, say what happens when a dependency is down. For the notification system, cover retries, idempotency keys and a dead-letter path for a failing gateway. For the JIRA-style tool, say how updates reach active users and what happens when two people edit the same task.
Relying on auto-complete and an IDE, then stalling when you have to write and run code without them.
Practise Java, SQL or your chosen language in a bare editor. Candidates advise against leaning on auto-complete, so rehearse imports, collections APIs and your own tests until you can write them without hints.
Questions & practice
Write a program to find the Longest Common Subsequence (LCS) between two given strings and explain its time co
Medium · Data Structures & Algorithms (DSA)
Write a program to find the Longest Common Subsequence (LCS) between two given strings and explain its time complexity.
Approach
- Define dp[i][j] as the LCS length of the first i characters of a and the first j of b. If a[i-1] == b[j-1], dp[i][j] = dp[i-1][j-1] + 1; otherwise dp[i][j] = max(dp[i-1][j], dp[i][j-1]). Row 0 and column 0 are 0.
- Start from plain recursion: without memoisation it is exponential because the same (i, j) pairs repeat. The table runs in O(mn) time and O(mn) space.
- To return the subsequence and not only its length, backtrack from dp[m][n]: step diagonally on a match and record the character, otherwise step toward the larger neighbour. Reverse the result at the end.
- If only the length is needed, keep two rows sized for the shorter string, which cuts space to O(min(m, n)). Say plainly that backtracking needs the full table, or Hirschberg's divide-and-conquer if you want linear space.
- Edge cases: an empty string gives 0, identical strings give the whole string, no shared characters give an empty result. Several different LCSs can have the same length, and any one of them is a valid answer.
Follow-up
- How would you reconstruct the subsequence itself while using only linear space?
- What changes for the longest common substring, where the characters must be contiguous?
- How does this recurrence relate to edit distance or to a line-based diff tool?
Find all distinct triplets in an array that sum up to a specific target value (Three Sum problem) using an opt
Medium · Data Structures & Algorithms (DSA)
Find all distinct triplets in an array that sum up to a specific target value (Three Sum problem) using an optimized two-pointer approach.
Approach
- Sort the array in O(n log n). Fix index i, then run two pointers lo = i + 1 and hi = n - 1: if the sum is below the target, move lo up; if above, move hi down; on a match, record the triplet and move both.
- Handle duplicates without a set of triplets: skip i when nums[i] == nums[i-1], and after each match advance lo past equal values and hi past equal values. This is how the answer contains only distinct triplets.
- Complexity is O(n^2) time with O(1) extra space beyond the output and the sort's own stack. Compare it with brute force (O(n^3)) and with a hash set per i (also O(n^2), but O(n) space and dedup is harder).
- Prune on the sorted array: break once nums[i] + nums[i+1] + nums[i+2] > target, and skip i when nums[i] + nums[n-2] + nums[n-1] < target.
- Edge cases: fewer than three elements, all zeros (exactly one triplet [0,0,0]), and int overflow when summing large values in Java. Sum into a long.
Follow-up
- How does this extend to 4Sum or k-Sum, and what is the complexity?
- How would you count the triplets with sum below the target, without listing them?
- What would you do if the input could not be sorted or modified?
Populate the next right element in a circular array and discuss the edge cases of your solution.
Medium · Data Structures & Algorithms (DSA)
Approach
- Clarify the wording first. The usual reading is next greater element in a circular array: for each index, the first larger value to its right, wrapping past the end, or -1 if there is none. Confirm that, and whether the answer should be values or indices.
- Brute force: for each i, scan (i + k) % n for k = 1..n-1. That is O(n^2) time and O(1) extra space.
- Optimal: a monotonic stack over a virtual doubled array. Loop i from 2n-1 down to 0 with idx = i % n, pop while the top is <= nums[idx], and when i < n set the answer to the top (or -1 if the stack is empty). Then push nums[idx].
- There are at most 2n pushes and each element is popped at most once, so the run is O(n) time and O(n) space. Do not physically copy the array; use the modulo index.
- Edge cases: an empty array, a single element (-1), all-equal values (all -1 because the pop uses <=), and the maximum value, which always gets -1. If the interviewer only means the next neighbour with wrap-around, the answer is nums[(i + 1) % n], and n == 1 points to itself.
Follow-up
- How would you return the distance to the next greater element instead of its value?
- What changes to find the next smaller element?
- Why is the doubled pass still O(n) and not O(n^2)?
Write an algorithm to delete empty folders recursively from a directory structure while preserving folders tha
Medium · Data Structures & Algorithms (DSA)
Write an algorithm to delete empty folders recursively from a directory structure while preserving folders that contain files.
Approach
- Use post-order DFS: clean up every child folder before deciding about its parent. Have the recursion return a boolean that is true when the folder holds no files and all of its subfolders were deleted.
- Model it as a tree (Node with name, isFile, children) and remove the child from its parent's list. That visits each node once, so it is O(N) time with O(depth) recursion stack.
- On a real filesystem in Java, use Files.walkFileTree and delete in postVisitDirectory, or sort Files.walk output in reverse order. Files.delete on a non-empty directory throws DirectoryNotEmptyException, which makes a wrong decision fail safely.
- Ask whether the root itself should be deleted when it ends up empty, and whether hidden files such as .DS_Store count as files.
- Edge cases: don't follow symlinks (they can create cycles or delete outside the tree), handle permission errors per folder without aborting the whole run, and switch to an explicit stack for very deep trees to avoid StackOverflowError.
Follow-up
- What happens if another process writes a file into a folder after you decided it was empty?
- How would you add a dry-run mode that reports what would be deleted?
- How would you parallelise this over millions of directories?
Solve a medium-to-hard LeetCode challenge involving array manipulation and string optimization within a 30-min
Medium · Data Structures & Algorithms (DSA)
Solve a medium-to-hard LeetCode challenge involving array manipulation and string optimization within a 30-minute window.
Approach
- Map the problem to a known pattern before coding. Two pointers for sorted or palindromic input. A sliding window with a count array or HashMap for substring constraints (for example, the longest substring without repeating characters in O(n)). Prefix sums plus a HashMap for subarray-sum-equals-k in O(n). Sort plus a sweep for intervals.
- State the brute force and its complexity in one sentence, then use the input size to set the target. For n around 10^5, O(n^2) is too slow, so aim for O(n) or O(n log n).
- Write Java string code that is efficient: use StringBuilder rather than += in a loop, which copies the string each time and goes quadratic. Use an int[26] or int[128] count array instead of a HashMap when the alphabet is fixed.
- Before declaring it done, trace it by hand on an empty input, a single element, all-identical characters and negative numbers. A sum-based sliding window breaks on negatives; prefix sums still work.
- Once it is correct, say the final time and space complexity out loud, and name the one optimisation you skipped and why.
Follow-up
- Why does a sliding window fail for subarray sums when the array has negative numbers, and what would you use instead?
- How would the solution change if the input arrived as a stream?
- What invariant does your window or pointer maintain, and how do you know it holds after each step?
Explain the key differences between various synchronization mechanisms in Java, such as synchronized blocks,
Medium · Java, Multithreading & Concurrency
Explain the key differences between various synchronization mechanisms in Java, such as synchronized blocks, ReentrantLock, and Semaphore.
Approach
- synchronized uses the object's intrinsic monitor. It is block-structured, reentrant, released automatically on exception, and coordinates threads with wait/notify on a single wait set. A thread blocked waiting to enter cannot be interrupted, and there is no fairness option.
- ReentrantLock gives the same mutual exclusion and memory-visibility guarantees, plus tryLock (with or without a timeout), lockInterruptibly, an optional fair mode and several Condition objects via newCondition(). The cost: unlock() must be in a finally block.
- Semaphore counts permits and has no owner. Any thread can call release, it is not reentrant, and its job is to cap concurrency (for example, at most N calls in flight to a slow dependency). A one-permit semaphore is not the same thing as a mutex.
- Choosing: synchronized for simple short critical sections, ReentrantLock when you need timeouts, interruption or separate conditions, Semaphore to bound access to a resource, and ReadWriteLock or StampedLock for read-heavy shared data.
- Mention virtual threads if Java 21 comes up: blocking inside synchronized pinned the carrier thread until JDK 24 (JEP 491). That is a reason older code used ReentrantLock there.
Follow-up
- What does a fair ReentrantLock cost compared with the default non-fair one?
- How would you build a bounded blocking buffer with one lock and two Conditions, notFull and notEmpty?
- What happens if an exception skips unlock() on a ReentrantLock?
How do Java 8 features like Streams, Lambdas, and Optional classes improve code readability and memory managem
Medium · Java, Multithreading & Concurrency
How do Java 8 features like Streams, Lambdas, and Optional classes improve code readability and memory management?
Approach
- Readability: lambdas and method references replace anonymous inner classes. Stream pipelines (filter, map, collect, Collectors.groupingBy) state what is computed instead of how the loop runs.
- Be precise about memory, because the gains are limited. Streams are lazy: intermediate operations fuse into one pass and only run at the terminal operation, short-circuiting ops (findFirst, anyMatch, limit) stop early, and no intermediate lists are built.
- Name the costs too. Stream<Integer> boxes every element, so use IntStream or mapToInt in numeric code. Each pipeline allocates stage objects, and in a hot loop a plain for loop can be faster. Parallel streams run on the common ForkJoinPool, so blocking I/O inside them starves other users of that pool.
- Optional makes absence explicit in a return type and replaces null checks with map, flatMap and orElseGet. orElse evaluates its argument eagerly while orElseGet is lazy. Don't use Optional for fields or parameters: it is an extra allocation and not Serializable.
- Conclude honestly: these features mostly help readability and correctness, and their memory effect depends on how they are used.
Follow-up
- When would you avoid a stream: checked exceptions, mutating local state, a hot loop?
- What happens with Collectors.toMap when two elements produce the same key?
- Can you run a second terminal operation on the same stream?
Describe how thread pools work in Java and how you would configure an ExecutorService for an I/O-heavy applica
Medium · Java, Multithreading & Concurrency
Describe how thread pools work in Java and how you would configure an ExecutorService for an I/O-heavy application.
Approach
- Explain ThreadPoolExecutor's parameters: core size, max size, keepAliveTime, work queue, ThreadFactory and RejectedExecutionHandler. A new task starts a thread while fewer than core threads exist; otherwise it is queued. Only when the queue is full does the pool grow toward max, and past that the task is rejected.
- Name the factory-method traps. newFixedThreadPool uses an unbounded LinkedBlockingQueue, so a backlog grows memory without limit. newCachedThreadPool can create a practically unbounded number of threads.
- Sizing for I/O-heavy work: threads ≈ cores × (1 + wait time / compute time), so blocking work justifies far more threads than cores. Use a bounded queue, CallerRunsPolicy or explicit rejection for backpressure, and a timeout on every remote call.
- Give each downstream dependency its own pool (a bulkhead) so one slow service cannot take every thread. Name threads in the ThreadFactory so thread dumps are readable, and monitor queue depth and active count.
- On Java 21, Executors.newVirtualThreadPerTaskExecutor() suits blocking I/O. Limit concurrency toward a dependency with a Semaphore rather than through pool size. Shut down with shutdown() and awaitTermination, falling back to shutdownNow.
Follow-up
- Why does a pool with core 10, max 50 and an unbounded queue never run more than 10 threads?
- What happens to an exception thrown by a task passed to submit() compared with execute()?
- How would you notice that the pool is saturated in production?
Walk through the execution flow of a concurrent application and explain how to prevent race conditions when up
Medium · Java, Multithreading & Concurrency
Walk through the execution flow of a concurrent application and explain how to prevent race conditions when updating shared state.
Approach
- Trace the interleaving step by step. count++ is read, add, write, so two threads can both read 5 and both write 6, losing an update. Check-then-act code (if !map.containsKey(k) then put) has the same gap between the check and the act.
- Bring in the Java Memory Model. Without a happens-before edge, one thread may never see another's write. volatile gives visibility and ordering but does not make compound operations atomic.
- Match the tool to the shared state: AtomicInteger or AtomicLong (incrementAndGet, compareAndSet) for single values, LongAdder for heavily contended counters, ConcurrentHashMap.compute, merge or putIfAbsent for atomic per-key updates, and synchronized or a lock when one invariant spans several fields.
- Prefer designs that share less: immutable objects, thread confinement, or handing data over through a BlockingQueue. Keep each invariant within one critical section and do no I/O while holding a lock.
- Test it: start many threads together with a CountDownLatch, run many iterations, and assert the final state. For memory-model behaviour, mention jcstress.
Follow-up
- Is calling get and then put on a ConcurrentHashMap thread-safe?
- Why does double-checked locking for a singleton need a volatile field?
- What is the ABA problem with compare-and-set, and how does AtomicStampedReference handle it?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
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?
Walk through the high-level and low-level design of a collaborative project management tool like JIRA, focusin
Medium · System Design & Architecture
Walk through the high-level and low-level design of a collaborative project management tool like JIRA, focusing on task assignment and real-time updates.
Approach
- Set the scope first. Core entities: projects, issues (title, status, assignee, reporter, priority, sprint), per-project workflows of allowed status transitions, comments and watchers. Non-functional needs: board updates appear for other viewers without a refresh, and history is auditable.
- Use a relational model: issues, projects, users, workflow_states, workflow_transitions, comments and an append-only issue_history table, indexed on (project_id, status) and (assignee_id). Add a version column so conflicting edits are caught with UPDATE ... WHERE id = ? AND version = ?.
- Low-level design: an Issue entity, a Workflow state machine that rejects invalid transitions, and an AssignmentService that checks the assignee belongs to the project and can plug in strategies such as round-robin or least-loaded. Notifications are observers of issue events.
- Real-time path: clients keep a WebSocket open to a gateway tier. The issue service commits the change and publishes an event through a transactional outbox (avoiding a dual write) to a topic keyed by project. Gateways subscribe and push to clients viewing that board.
- Recovery and scale: events carry a sequence number, so a reconnecting client fetches what it missed since its last one. Full-text search is served from an index fed by the same events, and data partitions by tenant or project.
Follow-up
- Two users drag the same card to different columns at the same moment. What happens?
- How does a client that was offline catch up without reloading the whole board?
- How would you let each project define its own workflow without schema changes?
Design a personalized dish recommendation system for a food delivery application, detailing the data models an
Medium · System Design & Architecture
Design a personalized dish recommendation system for a food delivery application, detailing the data models and caching strategies.
Approach
- Fix the scope: a ranked list of dishes per user for the current delivery address. A recommendation is useless if the restaurant is closed, out of delivery range or the dish is unavailable.
- Data model: users, restaurants, dishes (dish_id, restaurant_id, cuisine, price, dietary tags), orders and order_items, plus an event stream of views, clicks and add-to-cart. Derived features include per-user cuisine affinity, recency and dish popularity by area and time of day.
- Two-stage serving. An offline batch job (item co-occurrence such as "ordered X, also ordered Y", or matrix factorisation) produces candidates. Online, filter by open status, delivery radius, availability and dietary constraints, then rank with a lightweight scorer and re-rank so one restaurant does not dominate.
- Caching by volatility: per-user top-N candidates in Redis with a TTL, popular dishes per geo cell for fast fallback, long-lived dish metadata. Availability and open status must not be cached long; use a short TTL or invalidate on events.
- Handle cold start (new users get popular dishes in their area for that time of day; new dishes are matched by content tags) and measure with click-through and order conversion in an A/B test.
Follow-up
- A dish sells out after it was cached in many users' lists. How do you stop recommending it?
- Which offline and online metrics would tell you the recommendations are better?
- How does the design hold up at the dinner-time peak?
How would you design a high-throughput notification system that integrates with third-party SMS and email gate
Medium · System Design & Architecture
How would you design a high-throughput notification system that integrates with third-party SMS and email gateways?
Approach
- API: accept user, template, channels, priority and an idempotency key. Check user preferences and opt-outs, store a notification record with status, and enqueue rather than calling providers inline.
- Use separate queues or topics per channel and priority, with their own workers. A slow SMS gateway then cannot delay email, and bulk campaigns cannot delay transactional messages such as one-time passcodes.
- Reliability: the queue gives at-least-once delivery, so dedupe on the idempotency key before calling a provider. Retry timeouts and 5xx responses with exponential backoff and jitter, but never retry permanent errors such as an invalid number. Send exhausted messages to a dead-letter queue.
- Provider handling: a token bucket per provider to stay within its rate quota, a circuit breaker with failover to a second provider, and webhook callbacks that update the delivery status on the notification record.
- Add per-user frequency caps, template rendering and localisation in the worker, and a delay queue for scheduled sends.
Follow-up
- A worker crashes after the provider accepted an SMS but before the queue message was acknowledged. How do you avoid a duplicate?
- How do you send a campaign to millions of users without delaying OTP messages?
- How would you report delivered, failed and bounced counts per campaign?
Explain how you would implement distributed locking or rate limiting in a microservices architecture.
Medium · System Design & Architecture
Approach
- Distributed lock on Redis: SET key token NX PX ttl, where token is a unique value per holder. Release with a Lua script that deletes only if the stored value equals your token, so you never delete someone else's lock. The TTL frees the lock if its holder crashes.
- State the weakness: a GC pause or network stall longer than the TTL lets two holders act at once. Fencing tokens fix this: a monotonically increasing number issued with the lock, and the storage layer rejects writes carrying an older token. Where correctness matters, use etcd or ZooKeeper, or a database row lock (SELECT ... FOR UPDATE).
- Rate-limiting algorithms: token bucket (capacity plus refill rate, allows bursts), fixed window (cheap, but up to about twice the limit can pass across a window boundary), sliding window log (exact, stores every request) and sliding window counter (a weighted approximation that uses little memory).
- Distributed enforcement: a Lua script in Redis keyed by client or API key, so check-and-decrement is atomic. Enforce at the gateway and return 429 with Retry-After. A cheaper approximation is a local limiter per instance set to limit / instance count.
- Decide the failure mode up front: when Redis is unavailable, fail open (keep serving, risk overload) or fail closed (protect the backend, reject legitimate traffic), and say which fits which endpoint.
Follow-up
- The lock holder pauses past the TTL and another node takes the lock. What protects the data?
- Redis is down. Does the rate limiter fail open or closed, and why?
- How would you enforce one global limit for a client calling from several regions?
Discuss how you would handle database partitioning, replication, and consistency in a system experiencing high
Medium · System Design & Architecture
Discuss how you would handle database partitioning, replication, and consistency in a system experiencing high write volumes.
Approach
- Partition key: choose a high-cardinality key that spreads writes evenly, for example a hash of user_id. A monotonically increasing key such as a timestamp sends every write to one hot range partition. Hash partitioning gives even load but loses range scans; range partitioning keeps scans but needs splits.
- Hot keys and rebalancing: salt a hot key with a suffix and merge on read, and use consistent hashing or many small virtual partitions so adding nodes moves only part of the data.
- Replication trade-offs: single-leader async replication keeps writes fast but has replica lag and can lose acknowledged writes on failover. Synchronous or semi-synchronous replication trades latency for durability. Leaderless quorums need W + R > N for read and write sets to overlap.
- Consistency choices: read-your-writes by routing a user's reads to the leader for a short period after they write, or by waiting for a replica to reach the write's log position. Avoid cross-shard transactions with sagas or an outbox, and make writes idempotent so retries are safe.
- Write-volume tactics: batch inserts, append-only tables, LSM-tree stores for write-heavy workloads, and a log such as Kafka in front of the database to absorb bursts.
Follow-up
- How would you reshard a live table without downtime?
- The leader fails with writes that never reached a replica. What happens to them?
- A query needs data from every shard. How do you serve it?
Implement a custom React component or state hook within a strict time limit, demonstrating clean code structur
Medium · Frontend & UI Engineering
Implement a custom React component or state hook within a strict time limit, demonstrating clean code structure.
Approach
- Clarify the API before typing: the hook's arguments and return value, or the component's props and whether it is controlled or uncontrolled. Then write the smallest version that works.
- Know the common hooks well. useDebounce: useEffect sets a setTimeout and the cleanup clears it. useFetch: state for data, error and loading; an effect keyed on the URL; an AbortController aborted in cleanup so a slow, stale response never overwrites a newer one.
- Follow the rules of hooks: call them only at the top level, keep dependency arrays complete, use useRef for mutable values that should not trigger a re-render, and add useCallback or useMemo only where a child or effect depends on reference stability.
- For components, use stable ids as list keys (not array indexes when items reorder), semantic elements (button, not a clickable div), and keep state logic separate from presentation. Use useReducer when several state fields change together.
- Point out that React StrictMode runs effects twice in development to surface missing cleanup. Then say how you would test the code: renderHook from React Testing Library, with fake timers for the debounce.
Follow-up
- The URL changes twice in quick succession. How do you make sure the older response cannot win?
- When would you switch from several useState calls to useReducer?
- How would you unit test this hook?
Explain how to optimize rendering performance in a web application using techniques like `requestAnimationFram
Medium · Frontend & UI Engineering
Explain how to optimize rendering performance in a web application using techniques like requestAnimationFrame and CSS keyframes.
Approach
- Walk the rendering pipeline: script, style, layout, paint, composite. Animating transform and opacity can stay on the compositor; animating top, left, width or height triggers layout on every frame.
- requestAnimationFrame runs a callback just before the next repaint, matches the display's refresh rate and is throttled in background tabs. Move objects by the timestamp argument (elapsed time), not a fixed step per frame, so speed is the same on 60 Hz and 120 Hz screens.
- Avoid layout thrashing: batch DOM reads (getBoundingClientRect, offsetHeight) before writes. Alternating a write and a read forces a synchronous layout on each read.
- Prefer CSS keyframes and transitions for declarative motion; browsers can run transform and opacity animations off the main thread. Use will-change sparingly because every promoted layer costs GPU memory.
- Measure before and after in the DevTools Performance panel (long tasks, dropped frames). Typical fixes: throttle scroll handlers, mark listeners passive, virtualise long lists, and move heavy computation to a Web Worker.
Follow-up
- When would you choose a JavaScript requestAnimationFrame loop over a CSS animation?
- Why can a rAF animation still stutter, and how would you find the cause?
- How would you show that a change actually improved rendering?
Discuss your approach to building highly interactive elements, such as custom gradient animations or basic gam
Medium · Frontend & UI Engineering
Discuss your approach to building highly interactive elements, such as custom gradient animations or basic game mechanics, using standard web APIs.
Approach
- Gradient animation: browsers do not interpolate background-image gradients directly. Options are animating background-position on an oversized gradient (repaints every frame), cross-fading stacked layers by opacity (compositor-friendly), or registering custom properties with CSS @property so colour stops can transition.
- Game mechanics: a requestAnimationFrame loop that separates update from render, with a fixed-timestep accumulator for the physics. Clamp delta time after a tab switch so objects don't jump.
- Pick the rendering surface by scale: DOM elements for a handful of interactive pieces, Canvas 2D for many moving sprites, WebGL when that is not enough. Scale the canvas by devicePixelRatio to keep it sharp.
- Input and state: keep a map of pressed keys from keydown and keyup, use pointer events for mouse and touch together, and check collisions with axis-aligned bounding boxes. Reuse objects from a pool to limit garbage-collection pauses.
- Finish the details: respect prefers-reduced-motion, support keyboard input, call cancelAnimationFrame on unmount, and pause on visibilitychange when the tab is hidden.
Follow-up
- The game runs twice as fast on a 120 Hz monitor. Why, and how do you fix it?
- How do you choose between DOM, Canvas and WebGL for this element?
- How would you make the animation accessible to users who prefer reduced motion?
Write a functional code snippet that demonstrates a deadlock situation, and then explain how to resolve it.
Medium · Java, Multithreading & Concurrency
Approach
- Write the classic two-lock case. Thread 1 runs synchronized(A) { ... synchronized(B) { } } and thread 2 runs synchronized(B) { ... synchronized(A) { } }. Add a short sleep or a CountDownLatch after the first acquisition so the deadlock happens on every run, not just sometimes.
- Explain it with the four Coffman conditions: mutual exclusion, hold and wait, no preemption, circular wait. Every fix breaks one of them.
- Main fix: a global lock order. Always acquire the lower id first, for example the two accounts in a transfer. When objects have no natural id, order by System.identityHashCode and use a tie-breaker lock for equal hashes.
- Alternative: ReentrantLock.tryLock with a timeout. If the second lock is not acquired, release the first, back off with random jitter and retry. Mention that retrying without jitter can livelock.
- Show how to confirm it: a jstack thread dump reports the Java-level deadlock and names the threads, and ThreadMXBean.findDeadlockedThreads() can detect it in code. To prevent it, keep critical sections small and don't call unknown code while holding a lock.
Follow-up
- In a bank-transfer method, how do you order the locks when both accounts compare as equal?
- What is the difference between deadlock, livelock and starvation?
- Can a fixed-size thread pool deadlock with no explicit locks, for example tasks waiting on subtasks submitted to the same pool?
Suggested practice checklist
The plan follows the reported loop: coding first, then Java concurrency where you must write running code, then system design, then the frontend set and the stories for the manager and HR conversations.
Day 1 — Map the loop and set a coding baseline
- Write the five reported rounds on one page and mark what each needs: coding, language depth, design, or conversation.
- List your gaps against the listed stack: Java, SQL, OOP, multithreading, and the distributed tools Kafka, Redis and Docker.
- Set a 30-minute timer and solve one medium-to-hard array and string problem cold, then write the brute force, the optimised approach and the complexity.
Deliverable: A one-page loop map, a gap list against the stack, and one timed solution with its brute force and complexity.
Day 2 — Dynamic programming and two pointers
- Write the longest common subsequence in code with a full table, then reduce space with a rolling row and say what changes if you must return the sequence.
- Write Three Sum with sorting and two pointers, skipping duplicates, and state the complexity against the brute force.
- Run both on empty, single-element and all-equal inputs and fix every failure.
Deliverable: Two working solutions with complexity notes and a list of the edge cases each one failed first.
Day 3 — Circular arrays and recursive folder cleanup
- Restate the circular-array next-element question aloud, list the clarifying questions you would ask, then solve it with modulo indexing and cover wrap-around, empty and single-element cases.
- Write the empty-folder deletion with a post-order traversal so a parent is judged after its children, preserving any folder that holds files.
- Add a test tree for nested empty folders, a folder with only an empty child, and a folder with one file.
Deliverable: Two solutions plus a test tree and an edge-case list for the circular array and the folder cleanup.
Day 4 — Java synchronisation and race conditions
- Write and run a program where two threads deadlock, then fix it by lock ordering and again with tryLock and a timeout.
- Write a one-page comparison of synchronized, ReentrantLock and Semaphore with one use case each.
- Write a counter that loses updates, then fix it with synchronized, AtomicInteger and a lock, and note the difference.
Deliverable: A running deadlock program and fix, a comparison table, a race-condition demo with three fixes, and the finished SQL drill.
Day 5 — Thread pools, Java 8 and rate limiting
- Explain ThreadPoolExecutor parameters (core and maximum pool size, queue type, rejection policy) and write a sizing argument for an ExecutorService doing I/O-heavy work.
- Rewrite a loop-based method with streams, lambdas and Optional, and note where the stream version allocates more or hides a cost.
- Write a thread-safe rate limiter class, then outline how the same limit works across several service instances with a shared store like Redis.
Deliverable: A thread-pool sizing note, a before-and-after Java 8 rewrite with trade-offs, and a working rate limiter plus a distributed outline.
Day 6 — System design practice
- Design the JIRA-style tool aloud: requirements, data model for tasks and assignments, how updates reach active users, and conflict handling for concurrent edits.
- Design the notification system with SMS and email gateways: queue, workers, retries, idempotency and a dead-letter path.
- Outline the dish recommendation system: data models, what you cache and how you invalidate it.
- Write one page on partitioning, replication and consistency for a write-heavy system, naming the partition key and what query it makes expensive.
Deliverable: Spoken designs for the first three systems with a diagram each, and a one-page note on write-heavy data scaling.
Day 7 — Frontend set and behavioral stories
- Backend-only candidates may skip the first two tasks. Build a small React hook or component from scratch and an animation driven by requestAnimationFrame, then compare it with a CSS keyframes version.
- Write your answer to the client-side state and caching question as a story with a measured before and after.
- Prepare three situation, action, result stories for the manager and HR conversations.
Deliverable: A working hook and animation, a written client-state answer with numbers, and three rehearsed stories.
Manager and HR conversations
The last two stages are an Engineering Manager conversation about technical and team fit and an HR discussion of behavioral aspects and cultural alignment. The role is described as cross-functional, with product managers, QA and DevOps, and as including production monitoring, so prepare stories where you owned a decision, measured the result and worked through a disagreement or changing requirement.
- Tell me about a time you found a performance bottleneck in a production system and how you confirmed your fix worked.
- Describe a design decision you disagreed with, what evidence you brought, and what you did after the decision was made.
- Tell me about a time requirements were ambiguous or changed late in a release, and how you adapted.
- Describe a code review where you pushed back on a teammate's approach, or accepted pushback on yours.
- Tell me about a time you worked with QA, product or DevOps colleagues to resolve a release problem.
How do you handle complex state synchronization and caching on the client side without degrading application r
Medium · Frontend & UI Engineering
How do you handle complex state synchronization and caching on the client side without degrading application responsiveness?
Approach
- Treat it as a design-plus-story question: name a real project, say what the UI state was (lists, filters, drafts, live updates), and say what was slow or inconsistent before you changed anything. Lead with the symptom you measured, such as dropped frames, input lag or stale rows, not with a library name.
- Separate server state from local UI state. Keep one cache keyed by request, with stale-while-revalidate behaviour, and keep form or selection state local. Store normalised entities by id and derive views with memoised selectors instead of storing copies that can drift.
- Describe the synchronisation rules: optimistic updates with rollback when the server rejects, invalidation of only the affected keys after a mutation, and merging live pushes by id and version so an older event never overwrites newer data. Say how you handled two tabs or devices editing the same item.
- Protect responsiveness: virtualise long lists, batch or debounce updates, move heavy parsing to a Web Worker, and bound the cache with expiry or eviction. Persist only what survives a reload, and clear it on logout or schema change.
- Close with a result: the profiler or timing numbers before and after, what you tried that did not help, and one thing you would change. The mistake that sinks this answer is a list of tools with no measurement and no account of what you personally decided.
Follow-up
- Two tabs edit the same record while the user is offline; how do you reconcile them when connectivity returns?
- How would you prove the optimistic update never showed wrong data after a server rejection?
- When would you refuse to cache something on the client?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?
Tell callers you do not own that their integration breaks
Medium · deprecation · compatibility · stakeholders
A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.
Approach
- Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
- Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
- Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
- Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.
Follow-up
- How would you detect a consumer that reads the field only during a monthly export?
- One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?