Interview guides · Apidel Technologies
Apidel Technologies Software Engineer Interview Guide
Apidel Technologies Software Engineer interview guide: three reported rounds, coding and system design questions, STAR prompts and a 7-day plan.
The 60-second brief
Candidate-facing material describes Apidel Technologies as a company in the technology sector that often places engineers inside client or internal teams, where they build internal and client-facing platforms. The Software Engineer role is described as spanning backend logic, database query work and API integrations, with some frontend contact, plus day-to-day team habits such as stand-ups with product managers, work alongside QA, code review and written technical documentation. Because engineers can land in varied codebases, the reported interview questions are general fundamentals (data structures, algorithms, system design basics) rather than questions tied to one product.
This guide is for engineers preparing for the Software Engineer loop at Apidel Technologies. It follows the three reported stages (Recruiter Screening, Technical Phone Screen, Onsite Interview) and covers the fifteen reported practice questions across coding, system design and behavioral topics. The role is described as asking for 2+ years of professional experience, with requirements rising for senior levels, so system design depth should scale with your level. Java, Python, C++ and JavaScript are the languages the requirements list as examples.
Candidates report three stages: a recruiter call that includes compensation, an online coding phone screen, and a half-day onsite with up to six people covering technical and behavioral topics. The whole process is reported at about 3-5 weeks.
Focus areas
- Code Writing Under Time Constraints
- Problem Solving
- Algorithmic Thinking
Overview
The reported questions split into three groups. The coding group is classic fundamentals: a hash-map lookup over an array, a string check with a character filter, merging two sorted linked lists, tree depth, and counting islands in a grid. The system design group mixes product-shaped designs (an image hosting platform, high-volume ticket sales) with infrastructure-shaped ones (a distributed cache, a rate limiter for a public API) and one comparison question on relational databases against document stores for a user profile service. The behavioral group covers disagreement with a senior engineer, ambiguous requirements, a missed deadline, maintainable code and helping a teammate with a hard bug.
Candidates describe the phone screen as easy to average, centred on arrays, strings and basic data structures. That sets the preparation priority: the fundamentals must be fast and bug-free before you spend time on rare topics. Dynamic programming, union-find and complex graph algorithms are reported as occasional, not central. Practise each coding problem in your strongest language with a brute-force statement, an optimised version, complexity, and a handful of edge cases such as empty input, a single element and duplicates.
The onsite is the part that rewards stamina and range. Candidates report up to six conversations in a half-day, mixing whiteboarding, system design (depending on level) and behavioral discussion. Prepare one reusable design skeleton (requirements, API, data model, read and write paths, scaling, failure handling) and a small set of STAR stories that can each serve more than one prompt. Since the role description mentions client-facing and cross-functional work, practise explaining a technical trade-off in plain language as well as in engineering terms.
Compensation comes up early. Candidates report that it is raised on the first recruiter call and that some were turned away late when their current package exceeded the role's budget. Decide your number and your flexibility before that call, so the technical rounds are spent on engineering and not on a mismatch you could have found out about on day one.
The Apidel Technologies interview process
Candidates describe three stages, from a recruiter call through an online coding session to a half-day onsite. Treat these as reported experiences, not a published process.
Recruiter Screening
Candidates report an opening call about your background, logistics and compensation expectations. Compensation is said to be raised right away, and some candidates report being turned away late in the process when their current package was above the role's maximum. Approach it as a short, factual conversation: be ready to summarise your experience, state availability and location constraints, and give a clear salary expectation.
What to demonstrate
- Fit between your background and the Software Engineer role
- Logistics such as location, availability and timing
- Alignment on compensation expectations before technical stages begin
How to prepare
- Write a concise walk through your recent roles that ends on why this kind of engineering work, including client-facing or embedded team work if you have done it.
- Decide your compensation expectation and your flexibility before the call, and state it plainly instead of deferring it.
- Prepare questions about the team's technical stack and the interview format; candidates are advised to use the recruiting team for these.
- Name your strongest language among Java, Python, C++ and JavaScript so you can answer which one you will code in.
Technical Phone Screen
Candidates report an online coding session that focuses on fundamental problem-solving, and rate it easy to average. It is described as arrays, strings and basic data structures rather than competition-style puzzles. Approach it as a clean-code exercise: restate the problem, give a brute-force idea, improve it, write it, then trace an example by hand.
What to demonstrate
- Working, bug-free code written in an online session
- Fundamental problem-solving on arrays, strings and basic data structures
- Ability to state time and space complexity for the solution you wrote
How to prepare
- Drill the fundamentals this stage covers: hash-map lookups, two-pointer string scans, linked-list pointer handling and tree recursion, using the coding questions in this guide.
- Write each solution in your chosen language without an IDE's autocomplete, then run your own test cases covering empty input and duplicates.
- Practise narrating while you type; candidates are advised to think out loud so any hints can land.
- Review sliding-window and BFS/DFS patterns once, since candidates mention them as part of the fundamentals.
Onsite Interview
Candidates report a half-day onsite with up to six people, including peers, engineering managers and cross-functional partners. Reported content includes technical whiteboarding, system design depending on level, and extensive behavioral discussion. Approach it as a series of fresh starts: reset after each conversation, and keep one design skeleton and a set of stories ready for reuse.
What to demonstrate
- Whiteboard coding on data structures and algorithms
- System design reasoning for mid-level and senior candidates, including database and caching trade-offs
- Behavioral and collaboration examples, including conflict, failure and mentoring
- Communication of technical ideas to non-technical colleagues
How to prepare
- Rehearse the system design questions in this guide aloud: image hosting, a distributed cache, ticket sales, a rate limiter and SQL against a document store for user profiles.
- Whiteboard the island-counting and tree problems by hand so you can write code legibly without running it.
- Write four or five STAR stories covering a disagreement, a failure, ambiguity and helping a teammate, and map each to several prompts.
- Prepare different questions for engineers (stack, testing, deployment) and for managers (team direction, how work is scoped).
Where candidates lose points
Leaving the compensation conversation until late in the process
Candidates report that compensation comes up on the first recruiter call and that some were turned away late when a current package exceeded the role's budget. Decide your expectation before that call and state it clearly.
Coding silently, then handing over a solution that was never traced
On basic problems such as the two-number lookup or the string check, say the brute-force idea, name the structure that improves it, and trace one example through your code before you declare it done.
Skipping edge cases on the simple problems
List cases before you finish: an empty string or punctuation-only input for the palindrome check, an empty list for the merge, a null root for tree depth, and an empty grid for the islands problem. For islands, ask whether it means counting islands or counting distinct shapes.
Starting design drawings without requirements or a failure plan
For the cache, rate limiter and ticketing designs, ask about scale and consistency first, then cover data model, read and write paths, and what happens on overload or partial failure. For ticket sales, say how you prevent selling the same seat twice.
Using the same story, or a vague one, for all six conversations
Prepare four or five STAR stories with a concrete result each, and map every story to several prompts. Use I for your own actions and name the outcome, even if it was a miss.
Questions & practice
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
Medium · Coding and Algorithms
Approach
- Start from brute force: check every pair in O(n^2) time and O(1) space, then improve it with a hash map from value to index.
- One pass: for each index i, compute complement = target - nums[i]. If the complement is already in the map, return [map[complement], i]. Otherwise store nums[i] -> i. This is O(n) time and O(n) space.
- Look up the complement before inserting the current value. That stops one element pairing with itself, and it still handles duplicates such as [3, 3] with target 6.
- Sorting with two pointers needs O(n log n) and loses the original indices unless you sort (value, index) pairs. Say why the hash map fits better when the task asks for indices.
- Edge cases: negative numbers and zero, no valid pair (agree on the return value), and in Java or C++ the int overflow in target - nums[i] (compute it as a long).
Follow-up
- If the array is already sorted, can you do it in O(1) extra space? (Two pointers from both ends.)
- How would you return every distinct pair of values that sums to the target, without duplicate pairs?
- How does the approach extend to three numbers that sum to the target, and what is the complexity?
Write a function to determine if a given string is a valid palindrome, considering only alphanumeric character
Medium · Coding and Algorithms
Write a function to determine if a given string is a valid palindrome, considering only alphanumeric characters and ignoring cases.
Approach
- Use two pointers, left at 0 and right at n-1. Move each pointer past characters that are not alphanumeric, then compare the two characters lowercased. Return false on the first mismatch.
- This is O(n) time and O(1) extra space. The simpler version builds a filtered, lowercased string and compares it with its reverse, which is also O(n) time but O(n) space. Name both and say which one you are writing.
- Guard the skip loops with left < right so a string made only of punctuation cannot run either pointer out of bounds.
- Edge cases: an empty string or one with only punctuation is a palindrome. "0P" is not, because a digit and a letter never match after lowercasing. Mixed case such as "A man, a plan, a canal: Panama" returns true.
- Know your language's character checks. Python's str.isalnum() and Java's Character.isLetterOrDigit accept Unicode letters and digits, not only ASCII, so ask whether the input is ASCII-only.
Follow-up
- What if you may delete at most one character? (On the first mismatch, check whether either remaining substring is a palindrome. That is still O(n).)
- How would you handle full Unicode, where lowercasing can change string length and case folding is locale-dependent?
- How would you find the longest palindromic substring instead?
Implement an algorithm to merge two sorted linked lists into a single sorted linked list.
Medium · Coding and Algorithms
Approach
- Iterative merge with a dummy head node and a tail pointer. While both lists are non-empty, attach the smaller head to tail.next and advance that list and the tail. Then attach whichever list is left.
- This is O(n + m) time and O(1) extra space, because the existing nodes are relinked and nothing new is allocated. Return dummy.next.
- Comparing with <= takes from the first list on ties, which keeps the merge stable. Say this if nodes carry data beyond the key.
- The recursive version is shorter but uses O(n + m) stack depth, which can overflow on long lists (Python's default recursion limit is 1000).
- Edge cases: one or both lists empty, lists of very different lengths, and duplicate values across the two lists.
Follow-up
- How would you merge k sorted lists? (A min-heap of the k heads gives O(N log k), and so does pairwise divide and conquer.)
- Can you merge two sorted arrays in place when the first one has room at the end? (Fill from the back.)
- Should the merge reuse the input nodes or copy them, and what does each choice mean for the caller?
Find the maximum depth of a binary tree.
Medium · Coding and Algorithms
Approach
- Recursive DFS: depth(null) = 0, and depth(node) = 1 + max(depth(node.left), depth(node.right)). It visits each node once, so it is O(n) time and O(h) stack space.
- Confirm the convention first: count depth in nodes (a single root has depth 1) or in edges (a single root has depth 0).
- On a skewed tree h equals n, so recursion can overflow the stack. In Python the default limit is 1000. For large inputs, use an iterative version.
- Iterative BFS: process the queue one level at a time (loop over the queue's current size) and count levels. This uses O(w) memory, where w is the widest level. An iterative DFS with a stack of (node, depth) pairs also works.
- Edge cases: an empty tree returns 0, a single node, and a tree that is entirely a left or right chain.
Follow-up
- How does the minimum depth differ? (A node with only one child is not a leaf, and BFS can stop at the first leaf it reaches.)
- How would you check whether the tree is height-balanced in O(n), not O(n log n) or worse?
- How would you compute the diameter of the tree with the same traversal?
Given a matrix of 1s (land) and 0s (water), count the number of distinct islands.
Medium · Coding and Algorithms
Approach
- Clarify the wording first. "Distinct islands" can mean the number of connected land regions or the number of distinct island shapes. Ask, and say you can do both.
- Counting islands: scan every cell, and on an unvisited 1 add one to the count and flood-fill its 4-directionally connected land, marking cells visited (or setting them to 0 if mutating the input is allowed). This is O(R*C) time.
- Use an iterative BFS or an explicit stack for the flood fill. On a grid that is all land, recursive DFS can reach depth R*C and overflow. Union-find over land cells is an alternative.
- Distinct shapes: during each fill, record every cell as an offset (r - r0, c - c0) from the island's first scanned cell. Store the sorted tuple of offsets (or a traversal path with backtrack markers) in a hash set, and the answer is the set's size. This is still O(R*C) overall.
- Edge cases: an empty grid or empty rows, no land at all, diagonal neighbours (not connected unless stated), and whether the input may be modified.
Follow-up
- What changes if two islands count as the same when one is a rotation or reflection of the other? (Normalise all 8 transforms and keep the smallest.)
- If land cells are added one at a time, how do you report the island count after each addition? (Union-find, with near-constant amortised cost per operation.)
- How would you count islands in a grid too large for memory, read one row at a time?
Describe a project where the requirements were highly ambiguous. How did you proceed?
Medium · Behavioral and Collaboration
Approach
- This is a behavioral question, so answer with one real project. Spend a sentence or two on the situation, then explain what exactly was unclear (the users, the scope, the success metric or the constraints).
- Show the steps you took to reduce the ambiguity: you wrote down your assumptions, took specific questions to the people who owned the answers, and turned the replies into a short written outline that everyone confirmed.
- Show how you made progress before everything was settled. For example, you built a thin end-to-end slice or a prototype to get feedback, and kept the parts most likely to change behind a clear boundary.
- End with a measurable result and one assumption that turned out wrong, including how you found it and what it cost.
- The mistake that sinks this answer is a story where you waited for someone else to clarify, or built everything up front and reworked it later without explaining what you would change.
Follow-up
- Two stakeholders gave you conflicting requirements. How did you decide?
- Which assumption was most expensive to get wrong, and how would you catch it sooner next time?
- When did you decide you had enough clarity to start building?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Explain why the owner filter ignores the listing index
Medium · composite index · query plans · sort elimination
The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.
Approach
- Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
- For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
- Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
- Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.
Follow-up
- 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
- A dashboard runs this for 40 owners in one page load. What changes about the design?
Design an image hosting platform (like Imgur or Instagram core features).
Medium · System Design
Approach
- Scope it first: upload, view by URL, and optionally a per-user gallery or feed. Do the back-of-envelope numbers: uploads per day, average image size, storage growth per year, and a read-to-write ratio that is heavily read-skewed.
- Upload path: the API checks auth and returns a pre-signed URL. The client uploads straight to object storage, and a metadata row (image id, owner, storage key, size, created time, status) goes into a database keyed by image id.
- Processing: an upload event goes onto a queue. Workers create thumbnails and resized variants, strip EXIF location data and run moderation, then mark the image ready. Retries must be idempotent and keyed by image id.
- Read path: put a CDN in front of object storage. Treat each variant's storage key as immutable so it can be cached with a long TTL, and keep hot metadata in a cache. Use random, non-sequential ids so nobody can enumerate other users' images.
- For an Instagram-style feed, compare fan-out on write (push to followers' timelines) with fan-out on read, and use a hybrid for accounts with very many followers. For deletion, cover the soft delete, the storage cleanup and the CDN purge.
Follow-up
- One image goes viral overnight. What absorbs the load, and what still hits your origin?
- How do you support large or resumable uploads on unreliable mobile connections?
- How would you detect duplicate uploads, and what does content-hash deduplication cost you when an image is deleted?
How would you design a distributed cache system?
Medium · System Design
Approach
- Define the interface (get, set with TTL, delete) and the targets: data size, QPS, latency, and whether a lost entry is acceptable. A cache can usually rebuild from the source of truth.
- Partitioning: consistent hashing with virtual nodes, so adding or removing a node remaps about 1/N of the keys and load spreads evenly. Decide whether routing happens in a client library or in a proxy tier.
- Per-node eviction: LRU built from a hash map plus a doubly linked list gives O(1) get, put and evict. TTL expiry can be lazy on read, with a periodic sweep on top.
- Replication and failure: give each shard a primary and replicas. With asynchronous replication, a failover can lose recent writes or serve stale reads, so say that you accept this trade-off for a cache.
- Usage patterns: cache-aside versus write-through versus write-back, invalidation on writes, and stampede protection (request coalescing, TTL jitter, early refresh). Handle hot keys with local near-caches or by replicating the key.
Follow-up
- A node dies. What happens to its keys, to the clients routing to it, and to the database behind the cache?
- With cache-aside, how can a read and a concurrent write leave a stale value cached indefinitely, and how do you prevent it?
- How would you find and handle a single hot key that saturates one shard?
Architect a system to handle high-volume ticket sales for a popular concert.
Medium · System Design
Approach
- Pin down the hard requirements: never oversell, survive a burst of buyers at on-sale time, and stay reasonably fair. Ask whether seating is reserved or general admission, because it changes the inventory model.
- Inventory: each seat is available, held or sold. Take a hold with an atomic conditional update (UPDATE ... SET status='held', holder=?, hold_expires=? WHERE seat_id=? AND status='available') and check the affected row count. For general admission, an atomic decrement on a counter is enough.
- Holds expire. Release them lazily (an expired hold counts as available) or with a background sweeper, so abandoned carts return seats without manual cleanup.
- Checkout: send a client-generated idempotency key with the payment request so retries cannot charge twice. Move a seat from held to sold only after the payment is confirmed, and reconcile with the payment provider when the outcome is unknown.
- Burst control: a virtual waiting room admits users at a controlled rate with signed tokens. Serve the seat map from a cache that may be slightly stale, because the authoritative check happens at hold time. Add per-user limits and bot defences. A single event is a hot partition, so shard inventory by section.
Follow-up
- The payment provider times out after charging the card. What state is the seat in, and how do you resolve it?
- How does the design change when traffic is ten times higher than at the last on-sale?
- How do you stop bots from holding most of the inventory without ever paying?
Explain the trade-offs between using a relational database versus a document store for a user profile service.
Medium · System Design
Approach
- Start from the access patterns. Most profile reads and writes are by user id, which either store handles well, so the decision rests on the other queries, constraints and scale.
- Relational: an enforced schema, unique constraints (email, username), foreign keys and joins to related data (organisations, follows), multi-row ACID transactions and flexible secondary indexes. The cost is schema migrations and harder horizontal sharding.
- Document store: a flexible schema, the whole profile with nested addresses and preferences read in one call, and sharding by user id built into systems such as MongoDB or DynamoDB. The costs are weak joins, limited or expensive multi-document transactions, and uniqueness across shards that needs a separate lookup table or index.
- Name the failure modes: unbounded arrays inside a document grow it toward size limits (MongoDB caps a document at 16 MB), and denormalised copies of profile data drift out of sync.
- Give a recommendation and say what would change it. A relational database with a JSON column (for example PostgreSQL JSONB) for sparse, variable preferences covers most profile services. Move to a document store when the schema varies widely and write volume needs native sharding.
Follow-up
- A user changes their email. How do you keep it unique, and update every place that stores it, in each design?
- Product adds friends and "people you may know". Does that change your choice?
- How would you migrate live profile data from one store to the other without downtime?
Design a rate limiter for a public-facing API.
Medium · System Design
Approach
- Define the key being limited (API key, user id or IP), the limits per tier, and what an over-limit client receives: HTTP 429 Too Many Requests with a Retry-After header, plus remaining-quota headers if clients need them.
- Compare the algorithms. Fixed window is cheap but allows up to twice the limit across a window boundary. A sliding window log is exact but stores a timestamp per request. A sliding window counter approximates by weighting the previous window. Token bucket allows controlled bursts using two numbers per key (tokens, last refill).
- Distributed enforcement: keep the counters in a shared store such as Redis, and make the check-and-update atomic (a Lua script, or INCR followed by EXPIRE on a new key) so concurrent gateway instances cannot both admit the last request.
- Placement and latency: enforce in the API gateway or middleware before any expensive work. Optionally add a local in-memory limiter that syncs periodically, which trades exactness for fewer network round trips.
- Failure mode: decide fail-open (allow traffic when the limiter store is down) or fail-closed (protect the backend), and say which suits a public API. Mention clock skew if timestamps come from different hosts.
Follow-up
- How would you enforce one global limit across several regions without a cross-region call on every request?
- A paying customer reports being throttled below their quota. How do you debug it?
- How would you limit by cost rather than by request count when some endpoints are much more expensive than others?
Every query on one table stalls for forty seconds mid-deploy
Medium · lock contention · ddl · migrations
During a release on PostgreSQL, every query touching resource times out for about 40 seconds and then recovers with no intervention. The release ran one migration, ALTER TABLE resource ADD COLUMN archived_reason TEXT, and the migration log shows it completing in 6 ms. Unrelated tables showed no change in error rate. Explain how a 6 ms statement caused a 40-second stall, give the ordered checks you would run on a live system to confirm it, and give the migration procedure that prevents a repeat.
Approach
- Separate the statement's duration from the lock's duration. ADD COLUMN with no default is a catalogue-only change and genuinely runs in milliseconds, but it requires ACCESS EXCLUSIVE, and it cannot acquire that until every transaction already touching the table has finished.
- Account for the queueing, which is the part that surprises people. A lock request that is waiting blocks later requests for conflicting modes behind it rather than letting them overtake, so one long-open transaction holds the DDL and the DDL holds all the traffic. The stall length is set by the longest open transaction, not by the size of the change.
- Confirm on a live system in this order: pg_stat_activity for that table ordered by xact_start, looking for the oldest transaction and specifically for state = idle in transaction; then pg_locks where granted = false to find the waiter; then join them on pid to name blocker and blocked. pg_blocking_pids() does that join for you and is the fastest single call.
- Prevent rather than merely time it better. Set lock_timeout to a second or two on the migration session so the DDL abandons the queue after a bounded wait and is retried, instead of holding it for as long as the oldest transaction lives. Be exact about what that buys: queries arriving during the wait still queue behind the pending ACCESS EXCLUSIVE request, so each attempt costs them up to one lock_timeout of added latency. The outage goes from 40 seconds to about one second per attempt, not to zero. Also run migrations away from deploy-time peaks, and put a statement timeout and an idle-in-transaction timeout on the analytics role that opens the long transactions.
Follow-up
- The same release also wants NOT NULL on that column. What is the sequence that gets there without a long lock?
- Your lock_timeout retry fails ten times in a row because the analytics transaction is always open. What do you change?
Suggested practice checklist
The plan starts with the recruiter call, then works through the reported coding questions, the reported design questions and the behavioral prompts, finishing with a full rehearsal.
Day 1 — Map the loop and prepare the recruiter call
- Write the three reported stages (Recruiter Screening, Technical Phone Screen, Onsite Interview) on one page, with what each covers and which one you feel least ready for.
- Write a short statement of your background, your compensation expectation and flexibility, your location and availability, and your strongest language among Java, Python, C++ and JavaScript.
- List questions to ask the recruiting team about the team's stack and the interview format.
Deliverable: A one-page loop map, a written recruiter-call statement including your compensation expectation, and a list of questions for the recruiting team.
Day 2 — Arrays, strings and hash maps
- Write the two-number target lookup with a hash map, state the brute-force cost against the map version, and handle duplicates and a missing answer.
- Write the alphanumeric palindrome check with two pointers that skip non-alphanumeric characters and compare case-insensitively; test empty and punctuation-only strings.
- Add the longest substring without repeating characters as a sliding-window exercise and write three tests for each solution.
Deliverable: Three solutions in your chosen language, each with at least three test cases and a stated time and space complexity.
Day 3 — Linked lists and trees
- Merge two sorted linked lists with a dummy head node and a tail pointer; test empty lists and lists of different length.
- Compute binary tree maximum depth recursively, then iteratively with a level-order queue; test a null root and a skewed tree.
- Reverse a linked list iteratively and recursively, and state the stack depth of the recursive version.
Deliverable: Four working solutions with edge-case tests and a note on recursion depth versus iteration.
Day 4 — Grids and graph traversal
- Count islands in a matrix of 1s and 0s with DFS or BFS, marking visited cells, and state time O(rows x cols) and the stack risk of deep recursion.
- Extend it to distinct island shapes by recording each island's cell offsets relative to its starting cell and storing them in a set.
- Write both versions on paper, without running them, then trace a small grid by hand.
Deliverable: A counting version and a distinct-shapes version of the islands problem, handwritten and traced on one example grid.
Day 5 — System design: image hosting and ticket sales
- Outline the image hosting platform: upload path, object storage versus metadata store, thumbnails, caching and delivery, and what happens when processing fails.
- Outline high-volume ticket sales: how inventory is held and released, how double-selling is prevented, how to handle a traffic spike, and how payment retries stay safe.
Deliverable: Two annotated design diagrams (image hosting and ticket sales) with the read path, write path and top failure modes labeled, plus a worked answer to the practice question.
Day 6 — System design: cache, rate limiter and storage choice
- Outline a distributed cache: key partitioning (including consistent hashing), eviction policy, replication, invalidation, and stampede protection.
- Outline a rate limiter for a public API: identify the caller, compare token bucket and sliding window counters, decide where counters live, and choose what the client sees when limited.
- Write the relational versus document store comparison for a user profile service: access patterns, schema change, transactions and indexing.
Deliverable: A one-page comparison table for the profile store, plus design outlines for the cache and the rate limiter, each with a stated scaling bottleneck.
Day 7 — Behavioral stories and a full rehearsal
- Write four or five STAR stories (disagreement, ambiguity, a missed deadline, helping with a bug, pushing back on technical debt) with a concrete result for each.
- Answer the five reported behavioral questions aloud and map each story to more than one prompt.
- Do a back-to-back rehearsal of a coding problem, a design outline and two behavioral answers, resetting between each as you would across several onsite conversations.
Deliverable: Written STAR outlines for each story, a prompt-to-story map, and a recording or notes from the back-to-back rehearsal.
Behavioral stories for a six-person onsite
Candidates report extensive behavioral discussion in the half-day onsite, with up to six different people. The reported prompts cover disagreement with a senior engineer, ambiguity, a missed deadline, maintainable code and helping a teammate. The role description stresses explaining trade-offs to non-technical colleagues and working inside other teams, so stories should show collaboration and a measurable result, not just technical skill.
- Describe a project where the requirements were highly ambiguous. How did you proceed?
- Tell me about a time you had to push back on a product requirement because of technical debt.
Tell me about a time you disagreed with a senior engineer on a technical design. How did you resolve it?
Medium · Behavioral and Collaboration
Approach
- Choose a real design disagreement with a concrete technical subject, such as synchronous calls against a queue, or one wide table against normalised tables. State both positions fairly, including the strongest point on the senior engineer's side.
- Spend most of the answer on how you moved from opinion to evidence: a small prototype, a benchmark, a load estimate, or a written comparison of options against agreed criteria such as latency, operational cost and reversibility.
- Describe the outcome honestly. It might be that your option was adopted, that theirs was, or that you combined them. Say what you did once the decision was made, including implementing a choice you argued against.
- End with the result and a lesson about the relationship, such as agreeing on decision criteria before debating options. Avoid framing the senior engineer as wrong or yourself as having won.
Follow-up
- What would you have done if the senior engineer still disagreed after seeing your evidence?
- How did you disagree in the meeting versus in writing, and why?
- Was there a point where you realised you were wrong or partly wrong?
Walk me through a time when you missed a critical deadline. What happened and what did you learn?
Medium · Behavioral and Collaboration
Approach
- Pick a miss that was genuinely yours to manage, with a clear cause such as underestimating an integration, a scope change or a dependency that slipped. A story where the deadline was impossible from the start and you could not have acted differently shows little.
- Give the timeline in terms of decisions: when you first saw the risk, when you told stakeholders, and what options you offered (cut scope, move the date, add help). The earlier you raised it, the better the story.
- State the impact plainly (what shipped late, who was affected) and the recovery you led, without hiding behind the team.
- Close with concrete changes to your practice, for example breaking work into milestones with a checkpoint, adding buffer for unknowns, or flagging risk at the first slip. Avoid the disguised-strength answer where the only lesson is that you work too hard.
Follow-up
- When did you first know you would miss it, and what did you do that day?
- How did you explain the delay to a non-technical stakeholder?
- What do you do differently when you estimate work now?
How do you ensure your code is maintainable and easily understood by other developers?
Medium · Behavioral and Collaboration
Approach
- Answer with specific practices, then anchor them in one example. Typical practices: descriptive names, small single-purpose functions, comments that explain why instead of what, consistent formatting enforced by a linter, and removing dead code.
- Link maintainability to tests and review: automated tests that document expected behaviour, small pull requests, and review comments that ask whether a newcomer could follow the change. Candidates report automated testing and Git experience among the listed requirements.
- Include documentation that survives: a short README for setup, notes on non-obvious design choices, and API docs kept next to the code.
- Name the trade-off. Abstractions that are too early make code harder to read than a little duplication, so say when you refactor (a third repetition, a bug caused by confusing code) and when you leave it.
Follow-up
- How do you handle a review disagreement about style or structure?
- How would you make an untested legacy module safer to change?
- Tell me about a time someone else's confusing code cost you time, and what you did about it.
Tell me about a time you went above and beyond to help a teammate solve a complex bug.
Medium · Behavioral and Collaboration
Approach
- Choose a bug that was hard for a reason you can explain, such as an intermittent failure, a race condition or a data-dependent edge case, and say whose bug it was and why the teammate had stalled.
- Walk through the debugging sequence you used: reproduce reliably, shrink to a minimal case, form a hypothesis, check logs or use git bisect to locate the change, confirm root cause, then fix and add a regression test.
- Show how you helped without taking over: pairing, explaining your reasoning aloud, and leaving the teammate able to handle the next one. Say what you set aside from your own work to do it.
- Report the outcome and the follow-through, such as a regression test, an alert or a note in the runbook. Avoid a hero story that makes the teammate look incompetent.
Follow-up
- What was the root cause, and how did you prove it was the cause and not a coincidence?
- How did you decide how much of your own work to put on hold?
- What did your teammate do differently on the next bug like this?