Interview guides · Arkad Spa
Arkad Spa Software Engineer Interview Guide
Arkad Spa Software Engineer interview guide: three reported rounds, coding, system design and behavioral questions, with a 7-day prep plan.
The 60-second brief
Candidate reports describe Arkad Spa's engineering work only in general terms: backend microservices, data pipelines and user-facing applications, on services that carry heavy daily traffic. Engineers are said to work alongside product managers, designers, and operations and QA specialists. Nothing in those reports names a specific product, customer or team, so this guide does not guess one. The role they describe mixes writing and reviewing code with turning business requirements into technical specifications, watching system health, troubleshooting production issues and building automated tests that prevent regressions. For preparation, that points at two things: sound general-purpose coding and design skills, and ready examples of working with non-engineers and keeping a live service healthy.
This guide is for Software Engineer candidates at Arkad Spa, at mid-level or senior. Candidates describe the requirements as one modern language (Java, Python, C++ and Go are the examples given), data structures, algorithms and object-oriented design, experience building and consuming REST APIs, and a firm grounding in relational databases, with roughly 2 to 5+ years of production experience depending on level. Cloud platforms (AWS, GCP, Azure), Docker, Kubernetes, CI/CD and microservices are described as extras. The guide covers the three reported rounds and all fifteen reported questions: five coding, five system design and five behavioral.
Candidates report three rounds over about 3-5 weeks: an Initial Recruiter Screen, a Technical Assessment with a coding screen on data structures and algorithms, and Onsite or Virtual Onsite Loops covering advanced coding, system design and behavioral evaluation. The order of technical rounds is said to vary by team.
Focus areas
- Interview process evaluation
- Feedback during technical evaluation
- Communication skills (recruitment interaction)
Overview
Candidates describe a hands-on role. Time goes to writing and reviewing code, translating product requirements into specifications, joining sprint planning and retrospectives, and working with operations and QA on monitoring and production issues. That is why the reported questions split into three families: classic data-structure coding, system design, and stories about how you behave on a team. Prepare all three; none of them is a warm-up for the others.
The five reported coding questions are compact classics: detect a cycle in a directed graph, find two indices that sum to a target, build an LRU cache, find the lowest common ancestor in a binary search tree, and validate balanced parentheses. Candidates also mention longest substring without repeating characters, serializing a binary tree and merging overlapping intervals as example scenarios. The five design questions are not one domain. They cover a distributed key-value store, real-time location of delivery drivers, a notification pipeline for millions of emails and SMS messages, an image hosting platform, and data consistency across microservices in a checkout flow. Each stresses something different: partitioning and replication, high-rate writes, asynchronous fan-out, blob storage and caching, and distributed transactions.
Because relational databases and REST APIs appear among the stated requirements, spend a little time outside the coding sets: be ready to talk through schema and index choices, and to explain how an API behaves under retries and partial failure.
Candidates advise pacing the work: coding fundamentals first, then design, then behavioral stories as the onsite approaches. The seven-day plan below follows that order, with the three behavioral families rehearsed aloud rather than only read.
The Arkad Spa interview process
These are the three stages candidates describe, with a reported total of about 3-5 weeks. They also report that the exact sequence of technical rounds can differ by team, so confirm the format with your recruiter.
Initial Recruiter Screen
Candidates describe this first call as a conversation to align on your background, your expectations and whether the role fits. Some also report that recruiters weigh whether a different team or role would suit your profile better. Treat it as a short, structured pitch plus a logistics check, and use it to learn what the technical assessment looks like for your team.
What to demonstrate
- How clearly you summarise your background and recent work
- Whether your expectations and the role line up
- Communication in your first contact with the recruiting team
How to prepare
- Write a short walk through your career that ends on work close to the stated stack: backend services, REST APIs, relational databases, and any cloud or container experience.
- Decide which language you will code in (Java, Python, C++ and Go are the examples candidates cite) and say so, so the technical assessment matches it.
- Have your salary expectations, location constraints and availability written down before the call.
- Prepare questions: what the technical assessment consists of, whether the onsite order depends on the team, and what other roles the recruiter might raise.
Technical Assessment
Candidates report a coding screen centred on data structures and algorithms. The areas named are arrays and strings (sliding window, two pointers), hash maps and sets, and trees and graphs (BFS, DFS, shortest paths, tree balancing). The five reported coding questions are the best practice material for this stage: cycle detection in a directed graph, two-sum by indices, an LRU cache, lowest common ancestor in a BST and balanced parentheses. Practise them in your chosen language without autocomplete, stating complexity as you finish.
What to demonstrate
- Choice of data structure and the time and space complexity you can justify
- Working code that handles empty input, duplicates and other edge cases
- Explaining your reasoning aloud while you code
How to prepare
- Solve the cycle-detection question twice: recursive with three visit states, then iterative with Kahn's algorithm on in-degrees.
- Write the LRU cache from scratch with a hash map and a doubly linked list so get and put are both O(1).
- For the two-sum and balanced-parentheses questions, say the brute-force version first, then the one-pass hash map or stack version, with edge cases (negative numbers, repeated values, interleaved bracket types).
- Run the sliding-window and BST problems with a timer so your explanation and code finish together.
Onsite or Virtual Onsite Loops
Candidates describe the final stage as onsite or virtual onsite rounds covering advanced coding, system design and behavioral evaluation. They add that dynamic programming, union-find and harder graph algorithms occasionally appear for senior candidates, and that design topics include scalability, SQL versus NoSQL storage, microservice APIs, message queues, event-driven architectures and sharding. Reported example scenarios include longest substring without repeating characters, serializing and deserializing a binary tree, merging overlapping intervals, a URL shortener, a real-time chat service and an API rate limiter. Prepare one repeatable design framework and five or six behavioral stories.
What to demonstrate
- Coding beyond the screen: harder graph, tree and interval problems written as working code
- Requirements, capacity estimates, data flow and bottlenecks in a system design
- Past behavior on conflict, ownership, adaptability and feedback
How to prepare
- Practise the five reported design questions with one framework: requirements and scale, API, data model, read and write paths, then bottlenecks, failure modes and consistency.
- Rehearse the checkout-consistency question with saga steps, idempotency keys, an outbox table and compensating actions.
- Write the interval-merge and tree-serialization solutions once each, then add a dynamic-programming and a union-find problem as a stretch.
- Prepare stories for the five reported behavioral questions and tie each to a concrete engineering decision.
Where candidates lose points
Solving two-sum by sorting the array first and then returning values instead of indices
Sorting destroys the original positions, and the question asks for indices. Use a hash map from value to index in a single pass, check for the complement before inserting the current element, and state O(n) time and O(n) space. Ask whether duplicates and negative numbers can appear.
An LRU cache that scans a list or re-sorts timestamps on each access, so get or put is O(n)
Combine a hash map from key to node with a doubly linked list ordered by recency, using sentinel head and tail nodes to remove edge-case branches. Move a node to the front on get and put, evict from the tail at capacity, and run tests for capacity one, an overwrite and a missing key.
Detecting a cycle in a directed graph with a single visited set, which flags a diamond-shaped graph as cyclic
Track three states (unvisited, on the current path, finished) and report a cycle only when you meet a node on the current path, or use Kahn's algorithm and compare the processed count with the node count. Handle disconnected graphs by starting a search from every unvisited node, and mention recursion depth.
A design answer that lists boxes (load balancer, queue, cache, database) without choosing a data model, a partition key or a failure behavior
For each of the five design questions, state the scale you assumed, name the read and write paths separately, pick a partition or shard key and say what query it makes expensive, and say what happens when a dependency is down. For checkout consistency, name idempotency keys and compensating steps rather than saying 'use a distributed transaction'.
Behavioral answers told entirely as 'we', with no decision of your own and no result
Use STAR but spend most of the answer on Action: what you decided, what you rejected and why. For the production-bug and missed-deadline questions, include the evidence you used and what you changed afterwards.
Questions & practice
Write a function to detect a cycle in a directed graph.
Medium · Coding and Algorithms
Approach
- Clarify the representation (adjacency list, vertex count V, edge count E) and whether self-loops or parallel edges can appear; a self-loop u->u is already a cycle.
- Run DFS with three colours: white (unvisited), gray (on the current recursion stack), black (fully explored). An edge into a gray vertex is a back edge, so a cycle exists. A single visited set is wrong for directed graphs: in a diamond A->B, A->C, B->D, C->D it reports a cycle at D that is not there.
- Start a DFS from every white vertex so disconnected components are covered. Time O(V+E), space O(V) for colours plus stack depth.
- Alternative: Kahn's algorithm. Compute in-degrees, repeatedly remove zero in-degree vertices; if fewer than V vertices are removed, the rest lie on or behind a cycle. It is iterative, so no recursion-depth limit on long chains.
- Edge cases to test aloud: empty graph, a single vertex with a self-loop, two components where only one has a cycle, and a long path graph that would overflow a recursive stack in Python.
Follow-up
- Can you return the vertices of one cycle, not just a boolean? (Keep parent pointers and walk back from the gray vertex when the back edge is found.)
- How would you produce a topological order, or report that none exists, from the same pass?
- How does detection change for an undirected graph? (Ignore the edge to the parent, or use union-find on edges.)
Given an array of integers, return the indices of the two numbers that add up to a specific target.
Medium · Coding and Algorithms
Approach
- Brute force checks every pair: O(n^2) time, O(1) space. State it, then improve.
- Single pass with a hash map from value to index: for each i, look up target - nums[i] first, then insert nums[i]. Looking up before inserting stops an element pairing with itself and still handles duplicates such as [3,3] with target 6.
- Result: O(n) average time, O(n) extra space. Return the stored index and i.
- Sorting plus two pointers is O(n log n) and O(1) extra if indices did not matter. Here they do, so sort (value, index) pairs, or prefer the hash map.
- Clarify the rest: is exactly one answer guaranteed, what to return when none exists, and whether negatives or zeros appear. In Java or C++, target - x can overflow int at the extremes, so compute it in a long.
Follow-up
- If the array is already sorted, can you do it in O(1) extra space? (Two pointers moving inward.)
- Return all unique pairs, or extend to three numbers summing to the target (sort, then two pointers per anchor, O(n^2)).
- What changes if the numbers arrive as a stream, or do not fit in memory?
Implement a Least Recently Used (LRU) cache.
Medium · Coding and Algorithms
Approach
- Requirements: get(key) and put(key, value) both in O(1), and evict the least recently used entry when size exceeds capacity. Both get and put count as a use.
- Data structures: a hash map from key to node, plus a doubly linked list ordered by recency, with sentinel head and tail nodes so insert and unlink never special-case an empty list.
- get: on a miss return -1 (or null). On a hit, unlink the node and move it to the front. put: if the key exists, update the value and move it to the front. Otherwise insert at the front, and if size > capacity remove tail.prev and delete its key from the map. That is why each node stores its key.
- Library shortcuts, if allowed: in Python, OrderedDict with move_to_end(key) and popitem(last=False). In Java, LinkedHashMap(capacity, 0.75f, true) with removeEldestEntry overridden to return size() > capacity. Be ready to write the manual version anyway.
- Edge cases: capacity 0 or 1, put on an existing key (must not evict), repeated gets on the same key, and evicting immediately after an update.
Follow-up
- How would you make it thread-safe? (One lock around map and list together, since a concurrent map alone does not keep both structures in step. Then lock striping or sharded caches for throughput.)
- Add per-entry TTL expiry: lazy check on get, a background sweep, or both?
- How would you build an LFU cache with O(1) operations? (Frequency-to-list buckets plus a tracked minimum frequency.)
Find the lowest common ancestor of two nodes in a binary search tree.
Medium · Coding and Algorithms
Approach
- Use the BST ordering instead of searching the whole tree. Start at the root: if both values are smaller than the node, go left; if both are larger, go right; otherwise the current node is the split point and is the LCA.
- The 'otherwise' branch covers a node that equals p or q, so a node that is an ancestor of the other is returned correctly.
- Write it iteratively: O(h) time and O(1) space, where h is about log n for a balanced tree and n for a skewed one. A recursive version costs O(h) stack.
- Clarify the assumptions: are both nodes guaranteed to be in the tree, and are values unique? If presence is not guaranteed, confirm each node with an O(h) search before trusting the answer.
- Contrast it with a general binary tree, where you need a post-order traversal that returns the node when both subtrees report a hit: O(n) time.
Follow-up
- Solve it for a binary tree with no ordering property.
- If each node has a parent pointer, can you do it without the root? (Treat it like intersecting two linked lists: equalise the depths, then step up together.)
- How would you answer many LCA queries on a static tree quickly? (Binary lifting: O(n log n) preprocessing, O(log n) per query.)
Write a program to validate if a given string of parentheses is balanced.
Medium · Coding and Algorithms
Approach
- Clarify the alphabet: only (), or also [] and {}; and should other characters be ignored or rejected?
- Scan left to right with a stack. Push each opener. On a closer, fail if the stack is empty or its top is not the matching opener (keep a closer-to-opener map). At the end the string is balanced only if the stack is empty.
- O(n) time, O(n) space in the worst case. Odd length can be rejected immediately when only brackets are allowed.
- With a single bracket type a counter is enough: increment on '(', decrement on ')', fail if it goes negative, require zero at the end. Say why this breaks with mixed types: '([)]' keeps every counter valid but is not balanced.
- Test cases to name: empty string (balanced), ')(' (fails on the first character), '((' (leftover openers), '([]{})' and '([)]'.
Follow-up
- What is the minimum number of insertions or removals needed to balance the string, and which characters would you remove?
- Find the length of the longest valid parentheses substring in O(n). (A stack of indices seeded with -1, or DP.)
- If '*' can stand for '(', ')' or nothing, is the string still checkable in linear time? (Track the low and high possible open counts.)
Keep soft-deleted accounts from blocking re-registration
Easy · soft delete · unique constraints · engine differences
app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.
Approach
- Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
- Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
- For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
- State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.
Follow-up
- A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
- How do you honour an erasure request while resource_revision.actor_user_id still references this table?
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?
Design a distributed key-value store.
Medium · System Design
Approach
- Scope: get/put/delete on opaque values, rough key count and value size, read/write ratio, and whether the target is high availability with eventual consistency or linearizable reads. That choice drives everything else.
- Partitioning: consistent hashing with virtual nodes. Adding or removing a node moves only about 1/N of the keys, and virtual nodes even out load across mixed hardware. Replicate each key to the next N distinct nodes on the ring.
- Consistency: tunable quorums, where R + W > N makes every read set overlap the latest successful write set (for example N=3, W=2, R=2). Concurrent writes need a resolution rule: last-write-wins by timestamp is simple but exposed to clock skew; vector clocks detect conflicts and hand them to the client. For strong consistency instead, run one Raft group per partition with a leader.
- Storage engine per node: a write-ahead log plus an LSM tree (memtable flushed to sorted SSTables, compaction, Bloom filters to skip files on reads) suits write-heavy loads; a B-tree favours reads.
- Failure handling: gossip for membership and failure detection, hinted handoff while a replica is down, read repair on divergent reads, and Merkle-tree anti-entropy between replicas. Name what CAP costs you during a network partition under each choice.
Follow-up
- One key receives a large share of the traffic. How do you handle the hot key?
- Walk through what happens on a read when one replica is down and another holds a stale value.
- How would you add linearizable compare-and-set on top of this design?
How would you design a system to track the real-time location of delivery drivers?
Medium · System Design
Approach
- Scope: how many active drivers, how often each reports, who reads (a customer watching one order, dispatch searching for nearby drivers), and how stale a position may be. Write load is active drivers divided by the report interval, so the interval is the main cost lever.
- Write path: driver app -> gateway (a persistent WebSocket, or batched HTTPS posts) -> a log such as Kafka partitioned by driver_id, which keeps one driver's updates in order -> a location service that keeps only the latest position in memory. Full history goes to a time-series or columnar store asynchronously, off the hot path.
- Latest-position store: Redis GEOADD into a per-city or per-region key, queried with GEOSEARCH for nearby drivers, or a geohash/H3 cell index. Geo-set members have no individual TTL, so keep a separate last-seen key per driver with an expiry and prune drivers who went offline.
- Read path for tracking one delivery: the customer subscribes over WebSocket or SSE, and the service publishes each driver update on a per-order pub/sub channel, so nobody polls the database.
- Edge cases: out-of-order or duplicate pings (carry a client sequence number or timestamp and drop older ones), GPS jitter (smoothing or snapping to the road), dense city centres that overload one partition (split cells finer), and driver battery and bandwidth.
Follow-up
- How do you find the nearest N available drivers to a pickup point efficiently?
- What happens to customers watching a map when a location-service node dies?
- How long do you retain location history, and how do you handle privacy?
Architect a notification system that sends millions of emails and SMS messages daily.
Medium · System Design
Approach
- Size it: 10 million messages a day averages about 116 per second, but campaign sends are bursty, so design for peak, not the mean. Separate transactional traffic (one-time codes, receipts) from bulk marketing.
- Flow: a send API that accepts an idempotency key -> validate, check user preferences, opt-outs and quiet hours -> render the template -> enqueue on per-channel, per-priority queues so a slow SMS provider never backs up email.
- Channel workers call the providers (an email service, an SMS gateway) within each provider's rate limits. Retry transient failures with exponential backoff and jitter, and send exhausted messages to a dead-letter queue. Fail over to a secondary provider when error rates spike.
- Delivery is at-least-once, so dedupe on the idempotency key before the provider call and record status transitions (queued, sent, delivered, bounced) in a status store, updated by provider webhooks.
- Edge cases: hard bounces and unsubscribes must suppress future sends, SMS costs per message so throttle and dedupe aggressively, and scheduled sends must respect the recipient's local time zone.
Follow-up
- Can you guarantee a user never gets the same SMS twice? Where can duplicates still slip through?
- The primary email provider is down for a long stretch. What happens to the queues and to time-sensitive codes?
- How would you support sending a campaign at 9 a.m. in each recipient's local time?
Design an image hosting platform like Instagram or Imgur.
Medium · System Design
Approach
- Scope: is it upload-and-share-link (Imgur-like) or also a follower feed (Instagram-like)? Read-to-write ratio, average image size, and retention. Rough storage: 1 million uploads a day at 2 MB is about 2 TB a day before resized variants.
- Upload path: the client asks the API for a pre-signed URL and uploads straight to object storage, so image bytes never pass through app servers. Then write a metadata row (image_id, owner_id, storage key, dimensions, created_at, visibility).
- Async processing via a queue: generate thumbnails and resized variants, strip EXIF location data, compute a content hash for dedupe, run moderation, then mark the image ready.
- Read path: serve images through a CDN in front of object storage with long cache lifetimes on immutable keys, and cache hot metadata. Use random base62 or Snowflake-style IDs so URLs are not guessable or enumerable. Shard metadata by image_id, or by owner_id if per-user listing dominates.
- For a feed: fan-out on write (push post IDs into followers' timelines) is cheap to read but expensive for accounts with huge followings. A hybrid pulls those accounts' posts at read time.
Follow-up
- A user deletes an image that is cached at the CDN edge. How do you make it disappear?
- One image goes viral. Which layer takes the load, and what could still break?
- How would you detect and store identical uploads only once without leaking that another user has the same file?
How would you ensure data consistency across multiple microservices in a checkout flow?
Medium · System Design
Approach
- Frame the problem: order, inventory and payment each own their own database, so one ACID transaction is unavailable. Two-phase commit is possible but blocks while the coordinator is down and is rarely supported by brokers and payment providers, so a saga is the usual answer.
- Saga: a sequence of local transactions, each with a compensating action. Create the order as PENDING -> reserve inventory -> authorize payment -> confirm the order. On failure, release the reservation or void the authorization. Orchestration (a central state machine) is easier to trace; choreography (services reacting to events) couples less but scatters the flow.
- Avoid the dual-write bug with a transactional outbox: write the state change and the outgoing event row in the same local transaction, then relay the outbox to the broker by polling or change data capture.
- Delivery is at-least-once, so every handler must be idempotent: dedupe on message ID, and send the payment provider an idempotency key so a retried charge is not taken twice.
- Edge cases: the payment succeeds but the response is lost (query the status before retrying), a compensation itself fails (retry, then a manual-review queue), and other readers seeing PENDING state mid-saga (semantic locks, plus a reconciliation job for drift).
Follow-up
- Why not two-phase commit here? When would you accept it?
- How do you prevent overselling the last unit when two checkouts race?
- What does the customer see while the saga is still in progress, and when do you time it out?
Listing latency scales with page size, not with filters
Easy · n+1 · latency · query batching
The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.
Approach
- Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
- Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
- Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
- Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.
Follow-up
- The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
- How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?
Suggested practice checklist
The plan follows the reported order: coding fundamentals for the screen first, then system design, then behavioral stories for the onsite. Each day names the reported questions it practises and ends with something you can check.
Day 1 — Map the loop and fix your language
- Write out the three reported rounds (Initial Recruiter Screen, Technical Assessment, Onsite or Virtual Onsite Loops) and note what each stage covers.
- Choose one language from Java, Python, C++ or Go and write a template with fast input handling, a small test harness and the standard collections you will need.
- Rate each of the fifteen reported questions from 1 to 3 for confidence and mark the five weakest.
Deliverable: A one-page loop map, a runnable language template, and a ranked list of the fifteen reported questions.
Day 2 — Hash maps, stacks and sliding windows
- Solve the two-sum question in one pass with a hash map and write tests for duplicates, negatives and no-solution input.
- Solve the balanced-parentheses question with a stack, extending it to three bracket types and an early exit on mismatch.
- Write longest substring without repeating characters with a sliding window, and merge overlapping intervals after sorting.
Deliverable: Four passing solutions, each with a stated time and space complexity and at least three edge-case tests.
Day 3 — Graphs and trees
- Detect a cycle in a directed graph with three-state depth-first search, then with Kahn's algorithm, and compare them on a disconnected graph.
- Find the lowest common ancestor in a binary search tree both recursively and iteratively, and handle the case where one node is the ancestor of the other.
- Serialize and deserialize a binary tree with preorder traversal and null markers.
Deliverable: Two cycle-detection implementations, two LCA implementations and a tree round-trip test, all passing.
Day 4 — LRU cache and relational data
- Implement the LRU cache from scratch with a hash map and a doubly linked list, with sentinel nodes, and test capacity one, overwrites and evictions.
- Write a short note on how you would expose the cache's get and put through a REST API, including status codes.
Deliverable: A tested LRU cache, two completed SQL drills and a half-page API sketch.
Day 5 — System design: storage and messaging
- Design a distributed key-value store: partitioning with consistent hashing, replication, quorum reads and writes, and what you give up during a partition.
- Architect the notification system for millions of emails and SMS messages: a queue, per-channel workers, retries with backoff, deduplication and rate limits towards providers.
- Write for each design the first bottleneck when traffic grows tenfold.
Deliverable: Two one-page designs, each with a data model, the read and write paths, and a bottleneck and failure section.
Day 6 — System design: location, media and consistency
- Design real-time tracking of delivery drivers: location update ingestion, a hot store keyed by driver, push to subscribers and history retention.
- Design the image hosting platform: upload path, object storage, thumbnails generated asynchronously, CDN caching and metadata in a database.
- Design consistency across microservices in a checkout flow with a saga, idempotency keys, an outbox table and compensating actions.
Deliverable: Three designs, with the consistency model and the failure behavior spelled out for each.
Day 7 — Behavioral stories and a full mock
- Write a five-sentence STAR outline for each of the five reported behavioral questions, then pick a different project for each where you can.
- Run a mock onsite: one coding problem, one design and two stories aloud, with a recording.
Deliverable: Five STAR outlines, a written debugging sequence, and one recorded mock with notes on where you lost the thread.
Behavioral stories for the Arkad Spa onsite
The reported behavioral questions ask about learning a new technology, disagreeing with a senior engineer, a hard production bug, a missed deadline and unexpected feedback. Candidates also cite conflict resolution, ownership and delivery, and adaptability as the themes, and recommend STAR answers that keep context short and give most of the time to your own actions and the result. For a role that involves working with product managers, operations and QA, choose stories that show your decisions and how you communicated them.
- Describe a situation where you had to push back on a product requirement because of technical constraints.
Tell me about a time you had to learn a new technology completely from scratch to complete a project.
Medium · Behavioral and Past Experience
Approach
- Choose a project where the new technology was needed to deliver, not a side course. Name it, say why the team lacked it, and say what deadline or constraint made learning on the job necessary.
- Describe your learning method concretely: read the official documentation for the core model, build a small throwaway prototype of the riskiest part, read existing production code, and ask one experienced person to review your first real change.
- Show how you checked your understanding with evidence, such as tests that failed first, a code review from someone who knew the tool, or a load test, rather than saying it felt right.
- Close with the result and what stuck: what shipped, what you got wrong early and corrected, and how you passed the knowledge on (a short write-up or a pairing session).
- The story sinks if it stops at 'I read tutorials' or claims instant mastery. Keep two sentences for the situation and spend the rest on decisions you made and mistakes you fixed.
Follow-up
- How did you decide how deep to go before you started building the real thing?
- What did you get wrong early that you later had to undo?
- How would you onboard a teammate to the same technology now?
Describe a situation where you disagreed with a senior engineer's architectural decision. How did you resolve
Medium · Behavioral and Past Experience
Describe a situation where you disagreed with a senior engineer's architectural decision. How did you resolve it?
Approach
- Pick a disagreement about a real technical trade-off, such as service boundaries, a data store choice or a synchronous call that should have been a queue. State your concern as a concrete risk (coupling, scaling limit, operability) rather than a preference.
- Show that you understood the senior engineer's reasoning first: what constraints they were solving for, and what you might have been missing. Say what you asked them to explain.
- Describe how you made the disagreement concrete and low-stakes: a short written comparison of options, a quick benchmark or prototype, and raising it in the design review rather than around the person.
- State the resolution honestly. They may have been right and you changed course, you may have reached a compromise, or you may have committed to their decision while agreeing on a metric to revisit. Any of these is fine if you can say what you learned.
- Avoid casting the senior as obstinate or the story as 'I was right'. The answer needs your reasoning, your respect for theirs, and an outcome.
Follow-up
- What would you have done if the evidence had not changed their mind?
- Did you ever discover that you were the one who was wrong?
- How did the working relationship look afterwards?
Walk me through the most complex bug you have ever tracked down and fixed in production.
Medium · Behavioral and Past Experience
Approach
- Choose a bug whose cause was not obvious, such as a race condition, a cache invalidation issue, an unbounded query or a timeout cascade, and say what users or systems saw first and how it was detected.
- Lay out the sequence in order: contain the impact first (rollback, feature flag, restart or traffic shift), then narrow the cause with logs, metrics, traces and a diff of recent deployments, and list the hypotheses you ruled out and how.
- Explain how you reproduced it outside production, such as a failing test, a staging replay or a minimal script, because a reproduction is what makes the fix verifiable.
- Cover the fix and the follow-up: how you confirmed it in production, what regression test or alert you added, and any runbook or process change.
- The story sinks if it only describes the final one-line fix, blames another team, or leaves out your part. State clearly what you personally did, and who else was involved.
Follow-up
- How did you rule out the other hypotheses along the way?
- What did you add so this class of bug cannot recur unnoticed?
- What would you do differently in the early stage of the incident?
Tell me about a time you missed a project deadline. How did you communicate this to your stakeholders?
Medium · Behavioral and Past Experience
Approach
- Choose a real slip that was at least partly your responsibility. Name the cause plainly (underestimated work, scope growth, a late dependency) without hiding behind it.
- Say when you first knew the date was at risk, and show you raised it then rather than on the due date. This timing is the heart of the answer.
- Describe the message you gave stakeholders: what had happened, the new date with how confident you were, and the options you offered, such as cutting scope, adding help or moving the date, with the consequence of each.
- Give the outcome and the change in your habits afterwards: smaller milestones, an estimation buffer, an earlier checkpoint with the product manager, or a risk list reviewed in sprint planning.
- Avoid a disguised strength such as 'I cared too much about quality', and avoid a story with no consequence. The story should show an honest estimate of impact and a repair.
Follow-up
- At what point did you first know it would slip, and what did you do that day?
- What did you cut or reprioritise to limit the damage?
- How do you estimate work differently now?
Describe a scenario where you received unexpected feedback. How did you incorporate it into your work?
Medium · Behavioral and Past Experience
Approach
- Pick feedback that surprised you and concerned your work, for example a code review about structure, a comment on how you communicate in design discussions, or input on how you handle estimates. Say who gave it and why it was unexpected.
- Be honest about your first reaction, then show the step you took: you asked for specific examples so you could separate the pattern from a single incident.
- Describe the concrete changes you made, such as a review checklist, writing a short design note before coding, or sending status updates earlier, and how you kept them up.
- Show evidence that it worked, such as later review comments, a follow-up conversation with the same person, or a project that ran more smoothly. Do not make up figures.
- Avoid a humblebrag ('I was told I work too hard') and avoid defending yourself throughout. The answer should show you changing something observable.
Follow-up
- Was any part of the feedback something you disagreed with, and what did you do about that?
- How did you check that the change was working?
- Tell me about a time you gave a teammate similar feedback.