cswork / libraryOpen practice workspace

Interview guides · Coca-Cola FEMSA

Coca-Cola FEMSA Software Engineer Interview Guide

Coca-Cola FEMSA Software Engineer interview guide: online assessment, HR phone screen and panel rounds, with coding, system design and behavioral prep.

The 60-second brief

Coca-Cola FEMSA is a beverage company, and candidates describe the Software Engineer role as building and maintaining the internal software that supports its operations and the applications customers use. The areas they name are supply chain management, customer engagement and data analytics. Typical project types reported include integrating new technology into existing systems, improving supply chain tooling and building customer-facing applications. For you, that means the work sits between business workflows (stock, orders, customer interactions) and ordinary application engineering: APIs, databases, and services running on a cloud platform.

This guide is for candidates applying as a Software Engineer at Coca-Cola FEMSA. It covers the three reported stages, the coding questions (reversing a linked list, searching a sorted array, explaining complexity), the fundamentals questions (languages, object-oriented principles, code quality, version control), the design questions (real-time inventory, RESTful APIs, improving an architecture), the case-study questions (performance drops, app engagement) and the behavioral prompts. It ends with a seven-day plan that turns those into daily practice.

Candidates report three stages over roughly 3-5 weeks: an online assessment, a phone interview with HR, and panel interviews that combine technical challenges with behavioral assessment.

Focus areas

  • Coding proficiency (implied via online exam)
  • Technical screening (online exam)
  • Problem-solving under constraints

Overview

The requirements candidates describe for this role are practical and broad. The languages named are Java, Python and .NET; the data side is SQL and NoSQL; the platforms are AWS or Azure; and the working method is Agile or Scrum. React, Angular, Docker, Kubernetes and data analytics tools are listed as extras, and cloud principles, microservices and CI/CD are mentioned as advanced topics that may come up. Read that as a call for a generalist: someone who can write clean code in one main language, talk sensibly about databases and deployment, and work with product managers, designers, analysts and QA.

The reported questions match that profile. There are classic coding prompts (reverse a linked list, search a sorted array, explain the time complexity), a fundamentals group (which languages you know best, object-oriented principles, how you keep code maintainable, how you use version control), and two design questions that are tied to the work itself: a real-time inventory system and a RESTful API. Case-study prompts about a sudden performance drop and about raising engagement in an app test how you reason when the problem is open-ended. Because inventory and supply chain are named as project areas, a design answer that deals with concurrent stock changes will land better than a generic one.

Prepare in three layers. First, make the coding basics automatic in your main language so you can write a pointer-reversal loop or a binary search without hesitating and then state time and space cost out loud. Second, build a small set of reusable design answers (reserve-then-commit for stock, idempotent POSTs and cursor pagination for APIs, a layered debugging sequence for slow systems). Third, write STAR stories with real numbers for conflict, prioritisation, taking charge and improving an architecture, since the HR call and the panel both include behavioral content as candidates describe them.

The Coca-Cola FEMSA interview process

These are the three stages candidates describe. Reports give no published round order beyond this sequence and no per-round question list, so prepare by question type rather than by stage.

Online Assessment

Candidates report that the loop opens with an online assessment meant to check basic technical skills. Reports imply that it centres on coding and on data structures and algorithms, but the format, language options and number of problems are not described, so prepare for a timed, self-run coding screen. The practical approach is to work with a plain editor and a timer, write the solution in your strongest language, and test it on empty, single-element and boundary inputs before you consider it done.

What to demonstrate

  • Basic technical skills, as candidates describe the stage
  • Coding ability and fluency with data structures and algorithms, which reports imply for this screen
  • Whether your solution handles edge cases and you can state its time and space cost

How to prepare

  • Practise timed pointer and array problems in one language: reverse a singly linked list iteratively, then recursively, and note the stack cost of the recursive form.
  • Write binary search from memory, including the lower-bound variant for duplicates, and rehearse the off-by-one traps: loop condition, mid update, empty array.
  • After each solution, say its time and space complexity in one sentence and name any hidden cost such as slicing or inserting at the front of a list.

Phone Interview with HR

Candidates describe a phone interview with HR that covers cultural fit and basic qualifications. It is the stage to show that your background matches the requirements candidates describe (Java, Python or .NET; SQL or NoSQL; AWS or Azure; Agile or Scrum) and to give a clear account of why you want the role. Approach it as a concise, honest summary of your experience rather than a technical deep dive.

What to demonstrate

  • Basic qualifications against the role's listed requirements
  • Cultural fit, as candidates describe the purpose of this call
  • Clarity when you describe your own experience and working style

How to prepare

  • Write a one-minute summary of your background that names your main language, your database experience and the cloud platform you have used, and say plainly where you are working-level rather than expert.
  • Prepare a short answer to why you want a software role in a beverage and supply-chain business, based on what you actually find interesting about the work.
  • Have two STAR stories ready, one on teamwork and one on a deadline, because behavioral questions are reported in this loop and can start early.

Panel Interviews

Candidates report panel interviews that are more in depth, mixing technical challenges, behavioral assessment and discussion of previous experience. Reports do not say which questions come at which point, so cover all the reported types: design (real-time inventory, RESTful APIs, improving an architecture), case studies (a sudden performance drop, raising app engagement), fundamentals and behavioral prompts. With several people in the room, address the whole panel, state your assumptions before designing, and keep each answer structured so it is easy to follow.

What to demonstrate

  • Technical depth through design and problem-solving challenges
  • Behavioral competencies such as conflict handling, prioritisation and taking charge
  • How well you explain past projects and the decisions behind them

How to prepare

  • Prepare a real-time inventory design that covers the data model, an atomic reserve-and-commit flow, event ingestion from stores, and the consistency trade-off for dashboards.
  • Prepare an API design checklist: resource naming, method semantics, status codes, pagination, versioning, authentication and idempotency for POST.
  • Pick two past projects you can discuss in depth, including one where you improved a system's architecture, with before-and-after numbers and the trade-off you accepted.

Where candidates lose points

Reversing a linked list by overwriting the next pointer before saving it, or returning the old head

Keep three references (previous, current, next). Save next first, flip the pointer, advance both, and return previous. Then run an empty list, one node and two nodes before declaring it done.

Writing binary search that is nearly right: a wrong loop condition, a mid update that never shrinks the range, or no answer for duplicates

Pick one invariant (closed interval with lo <= hi, or half-open with lo < hi) and stick to it. Test the empty array, a target below the minimum, above the maximum and at both ends, and say its O(log n) time and O(1) space.

Describing inventory tables and APIs without saying what stops two checkouts from taking the last unit

Make reservation an atomic conditional update on available stock, add expiry for holds, record movements in an append-only ledger, and state which parts are strongly consistent and which can lag, such as dashboards.

Answering the languages and object-oriented questions with a list of six languages or textbook definitions

Name one or two languages you can be questioned on in depth and say what you built. For object-oriented principles, define each in one line and tie all four together in a single small example, such as a payment interface with two implementations.

Jumping to a fix for a performance drop, or telling a team story where every action is "we" and no result is measured

For performance, confirm and scope the drop, check what changed, mitigate, then walk through resources and dependencies. For behavioral stories, say "I" for your part, include a number for the outcome, and name what you changed afterwards.

Questions & practice

Write a function to reverse a linked list.

Easy · Coding / Algorithms

Approach

  1. Assume a singly linked list of nodes with val and next, reversed in place, returning the new head. The key insight: walk the list once and turn each next pointer around to face backwards.
  2. Keep three references: prev (starts as None), curr (starts at head) and nxt. Each step saves nxt = curr.next, sets curr.next = prev, then advances prev = curr and curr = nxt.
  3. The bug that breaks naive versions is overwriting curr.next before saving it, which severs the rest of the list. The other is returning head instead of prev; after the loop the old head is the tail.
  4. Complexity: O(n) time, O(1) extra space. The recursive version is also O(n) time but uses O(n) call stack and fails on long lists in Python, whose default recursion limit is about 1,000.
  5. Edge cases to test: an empty list (None returns None), a single node, two nodes, and reversing twice to get the original order back.

Worked solution (10 min)

  1. Define a minimal ListNode class and two helpers, from_list and to_list, so the function can be checked against plain Python lists.
  2. Start with prev = None and curr = head, and loop until curr runs off the end of the list.
  3. Inside the loop, stash the successor, point the current node back at prev, then slide both pointers one node forward in a single tuple assignment.
  4. Return prev: when the loop ends it holds the old tail, which is now the head.
class ListNode:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next


def reverse_list(head):
    """Reverse a singly linked list in place; return the new head."""
    prev, curr = None, head
    while curr is not None:
        nxt = curr.next      # save the rest before overwriting the link
        curr.next = prev     # flip this node's pointer backwards
        prev, curr = curr, nxt
    return prev              # old tail is the new head


def from_list(values):
    head = None
    for v in reversed(values):
        head = ListNode(v, head)
    return head


def to_list(head):
    out = []
    while head is not None:
        out.append(head.val)
        head = head.next
    return out

Expected result: to_list(reverse_list(from_list([1, 2, 3, 4]))) returns [4, 3, 2, 1]. It runs in O(n) time with O(1) extra space.

Verify it yourself

  • reverse_list(None) returns None
  • to_list(reverse_list(from_list([7]))) returns [7]
  • to_list(reverse_list(from_list([1, 2]))) returns [2, 1]
  • to_list(reverse_list(from_list([1, 2, 3, 4]))) returns [4, 3, 2, 1]

Follow-up

  • Can you reverse only positions m to n? Put a dummy node before the head so m = 1 needs no special case, reverse n - m + 1 nodes after the node before m, reconnect both ends, and return dummy.next.
  • How would you reverse in groups of k? Reverse each full block of k nodes, link the previous block's tail to the new block head, and leave a short final block as is unless told otherwise.
  • How do you check whether a list is a palindrome in O(1) space? Find the middle with slow and fast pointers, reverse the second half, compare, then reverse it back.

How would you implement a search algorithm for a sorted array?

Easy · Coding / Algorithms

Approach

  1. Use binary search, assuming ascending order and returning an index or -1. Because the array is sorted, one comparison with the middle element rules out half the remaining range, so it takes O(log n) steps instead of O(n) for a linear scan.
  2. Keep a closed interval lo = 0, hi = len(a) - 1 and loop while lo <= hi. Set mid = lo + (hi - lo) // 2; return on a match, set lo = mid + 1 if a[mid] is smaller than the target, otherwise hi = mid - 1.
  3. Classic bugs: while lo < hi with a closed interval skips the last candidate; lo = mid instead of mid + 1 can loop forever; (lo + hi) / 2 can overflow in fixed-width integers (Java, C++). Python ints do not overflow, but mention it.
  4. With duplicates, "any match" may not be enough: a lower-bound variant on a half-open [lo, hi) range sets hi = mid when a[mid] >= target and returns the first occurrence or the insertion point, like Python's bisect_left.
  5. Complexity: O(log n) time and O(1) space iteratively (a recursive version uses O(log n) stack). Test an empty array, one element, targets below the minimum and above the maximum, and matches at both ends.

Worked solution (15 min)

  1. Write the plain lookup on a closed interval and return as soon as the middle element equals the target.
  2. On a miss, always move a boundary past mid, so the interval shrinks every iteration and the loop is guaranteed to end.
  3. Add lower_bound on a half-open interval for inputs with duplicates; it never returns early, so it settles on the leftmost match or the insertion point.
  4. Test against bisect.bisect_left on random sorted inputs, which quickly flushes out off-by-one errors.
def binary_search(a, target):
    """Return an index of target in ascending list a, or -1."""
    lo, hi = 0, len(a) - 1            # closed interval [lo, hi]
    while lo <= hi:
        mid = lo + (hi - lo) // 2     # overflow-safe form in fixed-width languages
        if a[mid] == target:
            return mid
        if a[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1


def lower_bound(a, target):
    """Return the first index i with a[i] >= target (len(a) if none)."""
    lo, hi = 0, len(a)                # half-open interval [lo, hi)
    while lo < hi:
        mid = lo + (hi - lo) // 2
        if a[mid] < target:
            lo = mid + 1
        else:
            hi = mid
    return lo

Expected result: binary_search([1, 3, 5, 7, 9], 7) returns 3 and lower_bound([1, 2, 2, 2, 3], 2) returns 1. Both run in O(log n) time and O(1) space.

Verify it yourself

  • binary_search([1, 3, 5, 7, 9], 7) returns 3
  • binary_search([1, 3, 5, 7, 9], 4) returns -1
  • binary_search([], 1) returns -1
  • lower_bound([1, 2, 2, 2, 3], 2) returns 1
  • lower_bound([1, 2, 3], 10) returns 3

Follow-up

  • How would you search a rotated sorted array? With distinct values one half is always sorted: test the target against its range and discard the other; if a[lo] == a[mid] == a[hi] (duplicates), shrink both ends, so the worst case is O(n).
  • How do you find the last occurrence of a value? Find the first index with a[i] > target (an upper bound) and step back one, checking the index is valid and matches.
  • What if the array is unbounded or its length is unknown? Use exponential search: double an index until a[i] >= target, then binary search that range, for O(log i).

Can you explain the time complexity of your solution?

Easy · Coding / Algorithms

Approach

  1. Interpretation: this follow-up refers to whatever code you just wrote, so analyse that; the examples here use the linked-list reversal and sorted-array search from this section. Always give both time and space, and say what n measures.
  2. Method: count how often the dominant operation runs as a function of n, drop constants and lower-order terms, and state the worst case in Big-O; mention best or average case when they differ.
  3. Reversal: the loop body runs once per node and does a fixed number of pointer assignments, so total work grows linearly with the list length, O(n). The three pointers do not grow with n, so extra space is constant; a recursive variant would add one stack frame per node.
  4. Binary search halves the range each iteration, so after k steps about n / 2^k elements remain; it ends when that reaches 1, so k is about log2 n. Extra space is just lo, hi and mid, and the best case is a single comparison when the first middle matches.
  5. Traps that expose a weak answer: hidden costs in built-ins (slicing a[mid:] copies, turning slice-based binary search into O(n); list.insert(0, x) is O(n)), mixing up O (upper bound) with Θ (tight bound), and forgetting space.
  6. Finish by arguing optimality: reversal must touch every node, so it is Ω(n) and the O(n) loop is optimal; any comparison-based search of a sorted array needs Ω(log n) comparisons in the worst case, so binary search is optimal too.

Follow-up

  • What is amortized complexity? Average cost per operation over a worst-case sequence; Python's list.append is O(1) amortized even though an occasional resize copies all elements.
  • What happens to the running time if the input doubles? O(n) roughly doubles, O(log n) adds one step, O(n^2) quadruples; a quick benchmark confirms your claim.
  • Could you beat O(n) here? Only if you can skip reading some elements, as binary search does on sorted input, or precompute a structure such as a hash index or prefix sums that trades space for faster queries.

Replace offset paging on the resource feed with keyset

Easy · keyset pagination · composite index · sort order

resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.

Approach

  1. Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
  2. Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
  3. Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
  4. Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.

Follow-up

  • The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
  • Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Can you explain the principles of object-oriented programming?

Easy · Technical / Domain Questions

Approach

  1. Name the four principles (encapsulation, abstraction, inheritance, polymorphism), define each in one line, then tie them together in one small example, like the payment one below; definitions alone sound memorised.
  2. Encapsulation bundles state with the methods that change it and hides internals so invariants hold, e.g. Account.withdraw() rejects overdrafts instead of exposing a writable balance. Access modifiers (private) are the mechanism, not the principle.
  3. Abstraction exposes what an object does, not how: an interface like Storage.save(key, data) lets callers ignore whether it writes to local disk or AWS S3. Encapsulation hides data; abstraction hides implementation choices and complexity.
  4. Inheritance extends a base class (is-a). Polymorphism: shape.area() runs the right subclass's code at runtime; Java and C++ add compile-time overloading. Python lacks signature overloading (a later def wins) but overloads operators (__add__); duck typing gives polymorphism without inheritance.
  5. All four at once: a PaymentMethod interface declares charge(amount) (abstraction); Card and Wallet extend a BasePayment with shared retry code (inheritance), keep credentials private and validate inside charge() (encapsulation); checkout(m) calls m.charge(total) (polymorphism).
  6. The weak answer treats inheritance as the main reuse tool. The strong one prefers composition over deep hierarchies and respects Liskov substitution: a Square extending a mutable Rectangle breaks callers that set width and height independently.

Follow-up

  • When do you choose composition over inheritance? When the relation is "has-a" or behaviour varies independently; injecting a strategy object avoids fragile base classes and deep hierarchies.
  • Abstract class vs interface? An abstract class can hold fields, constructors and partial code, and a Java class extends only one; an interface has no instance state, may have default methods since Java 8, and a class can implement many.
  • What does SOLID add? Five design rules (single responsibility, open/closed, Liskov, interface segregation, dependency inversion) that keep OOP code changeable; give one example you applied.

How do you ensure code quality and maintainability?

Easy · Technical / Domain Questions

Approach

  1. Answer as layers of defence from cheapest to most expensive (automated checks, tests, review, design and documentation) and say which you would set up first on a codebase that has none.
  2. Automate the objective parts in CI so humans review design, not style: a formatter (black, Prettier), a linter, static types (mypy, TypeScript strict) and a pipeline that blocks the merge when any check fails.
  3. Test as a pyramid: many fast unit tests on business logic, fewer integration tests against a real database or queue, a handful of end-to-end tests. Coverage is a signal, not a target; 90% coverage with weak assertions proves little.
  4. Keep changes reviewable: small PRs with a clear description, reviewers checking naming, error handling and testability. Small single-purpose functions with injected dependencies are what make code testable in the first place.
  5. Maintainability over time means recording decisions (ADRs, a README that gets a new hire running locally), tracking tech debt as real backlog items paid down each sprint, and watching change failure rate and flaky-test counts.
  6. Anchor it with one concrete practice you introduced and what it caught, e.g. "added contract tests between two services and caught a breaking field rename before release"; "I write clean code" is the answer that fails.

Follow-up

  • How would you handle a legacy module with no tests? Add characterization tests that pin current behaviour at its boundaries first, then refactor in small steps behind them.
  • Is 100% coverage a good goal? No, because it rewards trivial tests; target critical paths and branches, and use mutation testing to check the tests actually assert something.
  • A teammate keeps opening 2,000-line PRs. What do you do? Help split it into stacked PRs or merge increments behind a feature flag, and agree a size norm as a team.

What tools and methodologies do you use for version control?

Easy · Technical / Domain Questions

Approach

  1. Describe a whole workflow, not just "Git": the tool (Git on a hosted platform such as GitHub or GitLab), a branching strategy, the review process, and how merges trigger CI/CD.
  2. Contrast strategies: trunk-based development keeps branches short-lived and merges to main at least daily, hiding unfinished work behind feature flags; GitFlow uses a long-lived develop plus release and hotfix branches. Trunk-based fits continuous deployment; GitFlow fits versioned releases.
  3. Merge vs rebase: merge keeps the branch's commits and joins histories with a merge commit, unless Git can fast-forward (--no-ff forces one). Rebase replays commits onto the new base for a linear history. Never rebase commits others have pulled: rewriting shared history forces them to reconcile.
  4. Hygiene: small atomic commits whose messages explain why, a protected main requiring review and green CI, release tags with semantic versioning, and no secrets or build artefacts in the repo (.gitignore, secret scanning).
  5. Show you can recover: git revert undoes a pushed commit safely, git reflog finds commits lost to a bad reset, and git bisect binary-searches history for the commit that introduced a regression.

Follow-up

  • A secret was pushed to the repo. What now? Rotate the credential first, since it must be treated as leaked; then purge it with git filter-repo and force-push.
  • How do you ship a three-week feature with trunk-based development? Merge small increments behind a flag that is off in production, enable it gradually, then delete the flag.
  • How do you resolve a hard merge conflict? Understand both sides' intent (ask the other author), resolve, rerun tests before pushing; merging main often keeps conflicts small.

How would you design a system for real-time inventory management?

Hard · System Design / Architecture

Approach

  1. Scope first. Assume stock is tracked per SKU per location, reads far outnumber writes, ~1,000 locations x 50k SKUs, peaks of a few thousand stock events/sec. Hard requirements: checkout never reserves more than the system shows available, and stock changes are visible within seconds.
  2. Data model: inventory(sku_id, location_id, on_hand, reserved, safety_stock); reservations(id, sku_id, location_id, qty, expires_at, status) with status held, committed or released; an append-only stock_movements ledger keyed by an idempotency ID, the audit trail and rebuild source.
  3. Reserve: checkout's synchronous POST /reservations is one transaction: per cart line, in key order to avoid deadlocks, run UPDATE inventory SET reserved = reserved + :qty WHERE ... AND on_hand - reserved - safety_stock >= :qty and insert a held row. Zero rows updated rolls back the cart.
  4. Commit and release: POST /reservations/{id}/commit or /release, and a TTL sweeper for expired holds, all run UPDATE reservations SET status = ... WHERE id = :id AND status = 'held' in the same transaction as the inventory change, so a sweep racing a late payment cannot act on a hold twice.
  5. Store sales: POS systems publish completed movements to Kafka keyed by sku_id, location_id (ordered per item), applied unconditionally since a sale cannot be refused. They arrive late and can take units reserved online, hence safety_stock; the guarantee covers the system's count, not the shelf.
  6. Tradeoff: strong consistency for reservations (conditional row updates on the primary, cheap unless one SKU is very hot), eventual consistency for dashboards via a cache and WebSockets. Dedupe events on the idempotency key; offline stores queue sales and apply them on sync, flagging negative stock.

Follow-up

  • How do you handle a flash sale on one SKU? Its row becomes a hot spot: split stock across bucket rows, or reserve in Redis with a Lua script that checks and decrements atomically (a bare DECRBY goes negative), then sync to the database.
  • How do you reconcile with physical counts? Record the difference as an adjustment movement instead of overwriting on_hand, so the ledger explains every change.
  • How would you add low-stock alerts? Consume the movement stream, compare on_hand - reserved to a per-SKU reorder point, and emit one deduplicated alert per threshold crossing.

What factors do you consider when designing a RESTful API?

Medium · System Design / Architecture

Approach

  1. Model resources, not actions: plural nouns (/orders, /orders/{id}/items), HTTP methods as the verbs, stable IDs, and shallow nesting (one level is usually enough).
  2. Respect method semantics: GET is safe; GET, PUT and DELETE are idempotent; POST is not, and PATCH is not guaranteed to be. For POSTs that create orders or payments, accept an Idempotency-Key header so client retries do not create duplicates.
  3. Use precise status codes (201 with Location, 204, 400, 401 for missing or invalid credentials vs 403 for authenticated but not allowed, 404, 409, 429) and one consistent error body, e.g. RFC 9457 application/problem+json with a machine-readable code.
  4. Design collections and change: cursor pagination for large or changing data, filtering and sorting via query parameters, and a versioning scheme (/v1 or a header). Add fields compatibly; never rename or repurpose existing ones.
  5. Cover cross-cutting concerns: HTTPS with OAuth 2.0 bearer tokens, rate limiting, caching with ETag and Cache-Control, optimistic concurrency via If-Match, and an OpenAPI spec as the contract.
  6. Tie it together with one endpoint: POST /v1/orders with an Idempotency-Key returns 201 and Location: /v1/orders/123; a retry with the same key replays that response instead of creating a second order, and a request without a token gets 401.

Follow-up

  • How do you ship a breaking change? Add a new version or field, run old and new side by side, announce deprecation (e.g. a Sunset header), and remove the old one once traffic hits zero.
  • Cursor vs offset pagination? Offset is simple but slow on deep pages and skips or repeats rows as data shifts; a cursor encodes the last sort key so each page is an indexed range scan.
  • When would you pick GraphQL or gRPC instead? GraphQL when clients need flexible aggregated reads across many resources; gRPC for low-latency internal calls with a strict schema.

If tasked with increasing user engagement in an app, what strategies would you employ?

Medium · Problem-Solving / Case Studies

Approach

  1. Define engagement before proposing features: choose a metric tied to user value (weekly users completing the core action, D7/D30 retention) and say why, since raw time in app can rise while users get less out of it.
  2. Diagnose with data first: map the funnel (sign-up, activation, core action, return visit), find the biggest drop-off in the event data, segment by cohort and platform, and pair it with qualitative input such as reviews and user interviews.
  3. Match the strategy to the gap you found: onboarding friction calls for a shorter sign-up and a guided first action; weak return calls for relevant, user-controlled notifications; shallow use calls for personalization; slow screens or crashes call for performance work.
  4. Validate every change with an A/B test: one primary metric plus guardrails (uninstalls, notification opt-outs, crash rate), a sample size set in advance, and enough runtime to get past novelty effects.
  5. Show the engineering side: an event-tracking schema, feature flags for gradual rollout, a notification service with rate limits and quiet hours, and dashboards. Rule out dark patterns; they lift short-term numbers and cost retention and trust.

Follow-up

  • How do you know a notification campaign worked? Compare retention of a holdout group with the notified group over weeks, not open rates, and watch opt-outs and uninstalls as guardrails.
  • An A/B test shows a lift in week one that fades. What happened? Likely a novelty effect; judge on the stabilized period or on longer-term retention cohorts.
  • How would you instrument the app? Define a small event taxonomy (name, user ID, timestamp, properties), batch and buffer events offline, and validate the schema in CI.

How would you approach a sudden drop in system performance?

Medium · Problem-Solving / Case Studies

Approach

  1. Confirm and scope it first: which metric dropped (latency, throughput, error rate), since when, and for everyone or one endpoint, region or tenant. Compare dashboards against the normal baseline, not anecdotes.
  2. Ask "what changed?" because most sudden regressions follow a change: a deploy, a config or feature-flag flip, a schema migration, a dependency upgrade, a traffic spike, or a batch job starting. Line the timeline up against the metric.
  3. Mitigate before root-causing: roll back the suspect deploy or flag, scale out, or shed and rate-limit load. Restore service first, while preserving logs, traces and heap dumps for the investigation.
  4. Walk the layers with the USE method (utilization, saturation, errors): CPU, memory and GC pauses, disk I/O, thread and connection pools; then dependencies such as slow queries, lock waits, cache hit rate and downstream latency. Distributed tracing shows which span grew.
  5. Rank likely causes: a bad deploy (N+1 queries, a missing index, a synchronous call in a hot path), a database plan change or lock contention, a cache failure causing a stampede, a slow dependency, or resource exhaustion (leak, full disk, pool exhaustion). Confirm each with a metric or EXPLAIN.
  6. After recovery, write a blameless postmortem with a timeline and root cause, and add the missing guard: an alert on the leading indicator, a load test, a canary stage, or timeouts and circuit breakers on the dependency.

Follow-up

  • Latency is up but CPU is low. What does that suggest? Requests are waiting, not computing: check pool exhaustion, lock contention, slow downstream calls, I/O waits, and container CPU throttling, which a low average hides.
  • Only some users are slow. How do you narrow it down? Segment by host, region, tenant and endpoint; one bad host points to infrastructure, one tenant points to data volume or a hot key.
  • Nothing was deployed and the database is the bottleneck. What next? Check the slow-query log and EXPLAIN for plan changes after a statistics update, lock waits, bloat, and data growth.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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 days follow the reported question types: coding and complexity first, then fundamentals, database and performance work, design, case studies and finally behavioral stories with a full mock.

Day 1 — Map the loop and your stack

  • Write the three reported stages in order and list the question types you expect to prepare: coding, fundamentals, design, case studies, behavioral.
  • List the requirements candidates describe (Java, Python or .NET; SQL or NoSQL; AWS or Azure; Agile or Scrum) and mark each as strong, working-level or gap.
  • Write a two-sentence answer to which language you are most proficient in and why, citing something you shipped with it.

Deliverable: A one-page stage map, a requirement-by-requirement skills table with gaps marked, and a written two-sentence language answer.

Day 2 — Coding screen: linked list, binary search, complexity

  • Write the iterative linked-list reversal in your main language, then the recursive version, and test empty, one-node and two-node lists.
  • Write binary search with a closed interval, then the lower-bound variant for duplicates, and test boundary targets.
  • State time and space complexity aloud for both solutions, including why the recursive reversal costs O(n) stack.

Deliverable: Four tested solutions (two reversals, two searches) plus a spoken complexity statement for each.

Day 3 — Fundamentals: OOP, code quality, version control

  • Write a small payment example that shows encapsulation, abstraction, inheritance and polymorphism, and add one sentence on composition over inheritance.
  • Write a code-quality checklist covering CI checks, a test pyramid, small reviewable pull requests and handling a legacy module with no tests.
  • Write out your version-control workflow: branching strategy, merge versus rebase, protected main, and how you recover from a pushed secret.

Deliverable: A short OOP example, a code-quality checklist and a written version-control workflow, each answerable in under two minutes.

Day 4 — Databases and performance problems

Deliverable: Two solved practice queries and a one-page debugging sequence for a sudden performance drop.

Day 5 — System design: inventory, REST API, architecture change

  • Design a real-time inventory system on paper: SKU-by-location data model, atomic reserve and commit, release of expired holds, an event stream from stores and a hot-SKU plan.
  • Design a small orders API with resource paths, status codes, an idempotency key on POST, cursor pagination and a versioning rule.
  • Outline a story about improving an existing architecture: the measured pain, options you weighed, the migration steps and the trade-off you accepted.

Deliverable: One inventory design diagram with its consistency notes, one API sheet and a written architecture-improvement outline.

Day 6 — Case studies and a hard technical story

  • Answer the engagement question aloud: define the metric, diagnose the funnel, match the fix to the gap, validate with an A/B test and guardrails.
  • Write a STAR story about a challenging technical problem you solved, with your hypotheses, one dead end, the fix and a measured result.
  • Review cloud basics, microservices trade-offs and a CI/CD pipeline for one service so you can answer if the panel goes there.

Deliverable: A recorded spoken answer to the engagement question, a written technical-problem story with numbers, and a half-page review sheet on cloud, microservices and CI/CD.

Day 7 — Behavioral stories and a full mock

  • Write STAR stories for conflict in a team, prioritising multiple deadlines and taking charge of a project, each with a measured result and an "I" for your actions.
  • Prepare the HR-call material: a one-minute background summary, why this role, and your language answer from day 1.
  • Run a mock panel using one coding problem, one design question and two behavioral prompts, and note where you lost structure.

Deliverable: Three written STAR stories, a rehearsed background summary and notes from one full mock run.

Behavioral and experience prompts

Candidates report cultural-fit questions on the HR phone call and behavioral assessment in the panel interviews. For a software role that means stories about working with others, ordering competing work, taking charge and learning from a technical problem. Each story needs your own actions, a concrete result and what you would do differently.

  1. How do you handle disagreements with team members?

What programming languages are you most proficient in, and why?

Easy · Technical / Domain Questions

Approach

  1. Name one or two languages you would happily be quizzed on live, not a list of six; depth beats breadth, and anything you call your strongest can be probed with a follow-up about its internals.
  2. Back each claim with evidence of real depth: production code you shipped, plus runtime and tooling knowledge (e.g. Python's GIL, packaging and asyncio; Java's JVM, garbage collection and generics erasure) and the ability to debug it under pressure.
  3. Make the "why" about fit to problems, not taste, and tie it to a project, e.g. "Python for data pipelines because of its libraries; Go for network services because of static binaries and cheap goroutine concurrency."
  4. Mark the edges honestly: name a language you are working-level in, what you have built with it, and what you would need to ramp up, such as its concurrency model, build tooling or idioms.
  5. Close with how you pick up a new language fast (read idiomatic code and the standard library, build a small real tool, get code reviewed), because any new role can require a language outside your strongest one.

Follow-up

  • What do you dislike about your strongest language? Give one concrete wart and your workaround, e.g. Python's mutable default arguments or JavaScript's this binding.
  • How would you choose a language for a new service? Weigh team familiarity, library ecosystem, performance and concurrency needs, and operational tooling over personal preference.
  • How long did your last new language take to learn? Give a realistic timeline and the steps that got you productive, such as pairing and shipping a small feature early.

Describe a challenging technical problem you have solved.

Medium · Technical / Domain Questions

Approach

  1. Pick a problem that was hard for a technical reason (a race condition, a memory leak, a scaling limit), not a vague deadline, and that you diagnosed yourself, so you can explain every layer involved (code path, data, infrastructure) and why the fix works.
  2. Set it up in two sentences with the stakes: what broke, who it hurt, and the constraint that made it hard, e.g. "intermittent duplicate orders under peak load that never reproduced locally."
  3. Spend most of the answer on the investigation: the hypotheses you formed, how you tested and ruled each out (logs, profiling, a reproduction harness), and at least one dead end. That chain of reasoning is the core of the story.
  4. Explain the fix and why it beat the alternatives, then quantify the result: error rate, latency, incidents per month, or engineer hours saved.
  5. End with what you put in place so it cannot recur (a regression test, an alert, a design rule) and what you would do differently. Say "I" for your part; an all-"we" story hides your contribution.

Follow-up

  • Why was the root cause hard to find? Name the obstacle, e.g. timing-dependent, only on production data, or masked by retries, and how you gained visibility.
  • What alternatives did you consider for the fix? Give one rejected option and the concrete tradeoff (risk, cost, time) that ruled it out.
  • How did you know the fix worked? Cite the metric or test that proved it and how long you watched production before closing the issue.

Describe a time when you improved an existing system architecture.

Medium · System Design / Architecture

Approach

  1. Choose an incremental improvement you diagnosed with evidence and rolled out safely on a live system, with a measured before and after; that beats "we rewrote it from scratch."
  2. Open with the pain in numbers and show that architecture caused it, e.g. "a synchronous chain of four services gave a 3-second p99 and cascading failures whenever one of them slowed down."
  3. Lay out the options you weighed (a cache, an async queue, splitting or merging services, a read replica) and why you chose one on cost, risk and team skills. Naming a rejected option shows judgment.
  4. Describe the migration, where seniority shows: a strangler-style cutover, shadow traffic or dual writes, feature flags, a rollback plan, and how you got the owning teams to agree.
  5. Quantify the outcome (latency, cost, deploy frequency, incident count) and name the tradeoff you accepted, such as more operational complexity or eventual consistency; claiming no downside sounds naive.

Follow-up

  • How did you convince others it was worth the effort? Show the data you used (incident cost, latency, engineer hours) and the low-risk first step that earned trust.
  • What would you do differently? Name a real mistake, e.g. underestimating data migration or missing a downstream consumer, and the practice you now use to avoid it.
  • How did you make sure nothing broke during the cutover? Compare old and new outputs on shadow traffic, roll out by percentage, and define the metric that triggers a rollback.

Describe a situation where you had to resolve a conflict within your team.

Medium · Behavioral / Leadership

Approach

  1. Pick a real disagreement about a technical approach, scope, ownership or review tone, either your own or one between two teammates that you mediated, and not a story where someone was simply wrong; show you handled it directly and kept the focus on the work.
  2. State each side's position so fairly they would agree with your summary; casting a colleague as the villain makes you look like the source of the conflict.
  3. Show your concrete steps: a private conversation first (with each person, if you were mediating), questions that surfaced the underlying concern (risk, workload, credit), and moving the argument onto evidence, such as a quick spike, a benchmark or agreed decision criteria.
  4. Be clear how it was resolved and your part in it: a compromise, the owner making the call, or you changing your mind. Escalation is fine as a last step if you explain why and frame it as a decision needed, not a complaint.
  5. Close with the outcome for both the work and the relationship (shipped, still working well together) and the habit you kept, e.g. agreeing decision criteria before debating solutions.

Follow-up

  • What if they still disagreed after the decision? Disagree and commit: document the concern, support the decision fully, and revisit with data if the risk shows up.
  • Have you ever caused a team conflict? Own a real example briefly, what you learned about your own behaviour, and what you do differently now.
  • When would you escalate to a manager? When the disagreement blocks delivery and a direct conversation has failed, ideally raising it together with the other person; conduct problems such as harassment go to a manager or HR straight away.

How do you prioritize tasks when faced with multiple deadlines?

Easy · Behavioral / Leadership

Approach

  1. Rank work by a repeatable method and surface tradeoffs early; silently working overtime or quietly missing a date are the two answers to avoid.
  2. Explain your triage: list the work, estimate effort, and rank by impact and urgency. Separate hard deadlines (a release, a contractual or regulatory date) from soft ones, and put first anything that is blocking other people.
  3. Show the communication step: when it will not all fit, tell stakeholders early, propose what slips or shrinks in scope, and let whoever owns the priority make the call.
  4. Give one real example with the competing items, what you chose, what you pushed back, and the result, e.g. "fixed the checkout bug first and moved the report to next sprint with the PM's agreement."
  5. Mention how you protect execution: limit work in progress, block focus time, and split big tasks into shippable pieces so partial progress still has value. "I just work harder" signals you have no system.

Follow-up

  • Two managers each say their task is top priority. What do you do? Put both items side by side with estimates and impact, and ask them or their shared manager to decide the order.
  • Have you ever missed a deadline? Pick a real one: when you saw the risk, how early you flagged it, and what you changed afterwards.
  • An urgent production issue lands mid-sprint. How do you handle it? Assess severity, take it if you own it or are on call, and tell the owners of planned work what slips.

Give an example of a project where you took the lead.

Medium · Behavioral / Leadership

Approach

  1. Show ownership and influence: that you drove something to done, aligned people and made decisions. You do not need a lead title; a project you initiated or coordinated as an individual contributor counts.
  2. Set context fast: the goal, team size, timeline, and why you ended up leading (you proposed it, you were asked, or there was a gap nobody filled).
  3. Spend the middle on leadership actions: how you split and assigned work, set milestones, ran decisions, unblocked people, and handled one specific setback such as a slipped dependency or a scope change.
  4. Quantify the result (shipped by the date, cut processing time by a stated percentage, adopted by other teams) and credit the team while being clear which decisions were yours.
  5. Close with a lesson about leading, e.g. delegating earlier or writing a design doc before splitting work. A story where you did all the work yourself reads as poor delegation, not leadership.

Follow-up

  • How did you handle a teammate who was not delivering? Talk privately and early to find the cause (unclear task, blocked, overloaded), adjust scope or support, and escalate only if it persisted.
  • Which of your decisions turned out wrong? Name it, how you noticed, and how quickly you reversed course.
  • How did you keep stakeholders informed? A regular written update with status, risks and asks, plus immediate notice when a milestone was at risk.