cswork / libraryOpen practice workspace

Interview guides · Bidgely

Bidgely Software Engineer Interview Guide

Bidgely Software Engineer interview guide: 4 reported rounds, BST and greedy coding, Car Parking design, leadership round and a 7-day plan.

The 60-second brief

Bidgely builds data-driven software that turns utility data into insights for energy consumers, and that also serves the providers who run the grid. Candidates describe the Software Engineer role as backend work: services that handle large volumes of utility data, including streams of meter readings, plus close work with data scientists and product managers to get analytical models running as production features. The requirements candidates describe list Java, C++ or Python, solid data structures, and system design experience, with cloud, distributed systems and large-scale data processing as extras.

This guide is for engineers interviewing for a backend Software Engineer position at Bidgely. It covers the four reported rounds (Technical Screening, Coding Assessment, Design Skills Assessment, Leadership Evaluation), the eight reported question themes across algorithms and machine coding or design, and a plan for the leadership conversation about your past projects.

Candidates report four rounds spread over roughly three to five weeks: Technical Screening, Coding Assessment, Design Skills Assessment and Leadership Evaluation. The reported questions fall into algorithm problems (a BST, greedy arrays) and machine coding or object-oriented design (a Car Parking system).

Focus areas

  • Machine Coding
  • Data Structures
  • Algorithms

Overview

The reported questions split into two families. The first is algorithmic: the Kth largest element in a Binary Search Tree, medium-level greedy problems on arrays, tree manipulation, and optimizing search and traversal. The second is machine coding and design: a Car Parking system, business logic written so it can be extended, state-management edge cases, and class hierarchy and interface design. Preparing both families matters, because candidates describe the design side as a place where clean, modular code counts as much as a correct answer.

Candidates also describe two habits that run through the loop. One is to talk through your reasoning while you code rather than work in silence. The other is to confirm requirements before writing anything, then walk through edge cases before declaring a solution done. Both are free to practise: record yourself solving a problem and listen for silent stretches.

The role itself is backend work on utility data, with data scientists and product managers as collaborators, so the later rounds are likely to touch your real project history. Candidates report that the managerial round goes into past technical decisions and project conflicts. Pick two or three projects you can explain at the level of trade-offs, and rehearse them as carefully as the coding questions.

Pick one object-oriented language from the Java, C++ and Python the requirements list, and use it for every practice problem. Writing a BST traversal, a greedy loop and a small class hierarchy fluently in one language beats switching between several. Aim for code that runs on the first honest attempt, since candidates are advised to favor a working, clean solution over extra features.

The Bidgely interview process

Candidates describe four stages, each with a short stated purpose, and do not tie individual questions to individual stages, so prepare every question family for every round.

Technical Screening

Candidates describe Technical Screening as the opening assessment of your technical skills and problem-solving. Reports do not say which questions appear here, so treat it as the place where fundamentals are checked: the BST, greedy-array and tree questions are the likeliest material to warm up on. Practise saying your reasoning out loud while you write code in your chosen language, and finish by walking through edge cases yourself.

What to demonstrate

  • Technical skill and problem-solving, as the stage is described
  • Working code in a language you chose, with its complexity explained
  • Whether you clarify requirements before you start coding

How to prepare

  • Solve the Kth largest element in a Binary Search Tree three ways: reverse in-order recursion, an iterative stack, and a version that stops after k nodes
  • Do three medium greedy array problems and state why the greedy choice is safe before you code it
  • Write a short verbal summary of time and space complexity after every practice solution

Coding Assessment

The Coding Assessment is described as a deeper look at your coding through practical exercises. The reported algorithm themes are tree manipulation and optimizing search and traversal, so expect problems where the first working answer is not the one to stop at. Write the straightforward version, state its cost, then improve it. Compile or run mentally against a small example before you say you are done.

What to demonstrate

  • Coding ability on practical exercises, as the stage is described
  • Moving from a correct solution to an efficient one
  • Handling of edge cases such as an empty tree, a single node or duplicate values

How to prepare

  • Practise tree problems in both recursive and iterative form: traversals, lowest common ancestor, validating a BST, and subtree size
  • Rehearse binary search and BFS or DFS variants, noting what each one costs in time and in stack or queue space
  • After each solution, test on an empty input, one element and a worst-case shape such as a skewed tree

Design Skills Assessment

Candidates describe the Design Skills Assessment as a test of how well you design software solutions. The reported design themes are a Car Parking system, extensible business logic, state-management edge cases, and class hierarchy and interface design, with candidates mentioning patterns such as Strategy and Factory and SOLID principles. Model the domain first (vehicle, spot, ticket, lot), keep each class to one job, and get a minimal flow working before adding anything else.

What to demonstrate

  • Object-oriented modelling: classes, interfaces and responsibilities
  • Extensibility, meaning new rules added without rewriting existing logic
  • Correct handling of system state, including invalid and repeated operations

How to prepare

  • Build a Car Parking system in your chosen language with several vehicle types and spot sizes, and run it from a small main method or test
  • Add a new pricing or allocation rule to it using Strategy, and confirm no existing class had to change
  • List ten state edge cases for it, such as a full lot, double entry of one vehicle, exit with a lost ticket, and concurrent entries, and handle each one

Leadership Evaluation

Candidates call the last stage the Leadership Evaluation, and also refer to it as the managerial round. They report that it goes beyond behavioural questions into your technical past: the challenges you faced, the decisions you made, how you defended them, and how project conflicts were handled. Prepare two or three projects in depth and be ready to explain what you would change if you built them again.

What to demonstrate

  • Leadership qualities and fit within the team, as the stage is described
  • Ability to explain and defend technical decisions from past projects
  • How you handled conflict and contributed to team outcomes

How to prepare

  • Write a one-page outline for each of two or three projects: goal, your role, the hardest decision, the alternative you rejected and the outcome
  • Prepare one conflict story with the disagreement, what you did, and what changed afterwards

Where candidates lose points

Coding the Kth largest in a BST by sorting or collecting every value first

State the in-order property up front: a reverse in-order walk visits values from largest to smallest, so you can stop after k nodes at O(h + k) time. Mention that a Morris traversal or an augmented subtree-size field changes the space or repeated-query cost.

Applying a greedy rule to an array problem without showing it is safe

Before coding, say why the locally best choice cannot hurt the final answer, or test it against a small counterexample. If the rule fails on that example, switch to dynamic programming instead of patching the greedy.

Starting the Car Parking system by writing code before modelling the domain

Spend the first minutes naming the entities, the vehicle-to-spot-size rules and the operations (park, unpark, availability). Confirm scope, then code the smallest working flow and extend it.

Hard-coding rules in the machine coding solution, then having to rewrite it when a new rule is requested

Put pricing, spot allocation and vehicle compatibility behind small interfaces and inject them. Practise the specific refactor of adding a new rule without editing existing classes.

Silent coding, and no pass over edge cases at the end

Narrate decisions as you write, and finish by running through an empty input, a full lot, a duplicate operation and a skewed tree. Candidates are told that unexplained work leaves nothing to assess.

Questions & practice

Kth largest element in a Binary Search Tree

Medium · Data Structures and Algorithms

Approach

  1. Use the BST ordering: a reverse in-order traversal (right subtree, node, left subtree) visits values in descending order, so the kth node visited is the answer. Decrement a counter on each visit and stop when it reaches zero.
  2. Write it iteratively with an explicit stack: push right children down the spine, pop, count, then move to the left child. Time is O(h + k) and extra space is O(h), where h is the height. h is O(log n) when the tree is balanced and O(n) when it is skewed.
  3. If the follow-up asks for O(1) extra space, use a reverse Morris traversal, which threads each node's in-order predecessor back to it. Time stays O(n), but the tree is modified while it runs: if I stop early at k, I have to finish the traversal or undo the threads I created.
  4. Settle the edge cases before coding: an empty tree, k <= 0, k larger than the node count (return a sentinel or raise, as agreed), and duplicate keys. With duplicates, decide whether 'kth largest' counts repeated values or only distinct ones.
  5. Test it on a left-skewed tree, a right-skewed tree, k = 1 (the rightmost node) and k = n (the leftmost node).

Follow-up

  • If kth-largest queries arrive repeatedly while keys are inserted and deleted, how would you make each query O(h)? (Store subtree sizes in each node: if k <= size(right), go right; if k == size(right) + 1, the current node is the answer; otherwise subtract size(right) + 1 from k and go left.)
  • How would you return the kth smallest element instead, or every element between the kth and mth largest?
  • Your recursive version fails on a tree with 100,000 nodes in a chain. Why, and what is the fix?

Medium-level problems focusing on greedy approaches in arrays

Medium · Data Structures and Algorithms

Approach

  1. Name the greedy choice and argue that it is safe before writing code. The usual argument is an exchange argument: take any optimal solution, swap its first choice for the greedy one, and show the result is no worse.
  2. Know the standard shapes. Jump reachability: keep farthest = max(farthest, i + nums[i]) and fail if i ever passes farthest, which is O(n) time and O(1) space. Interval scheduling: sort by end time and take each interval whose start is >= the last end you took. Gas station: if total gas is less than total cost there is no answer; otherwise move the start to i + 1 whenever the running tank drops below zero.
  3. State the cost. Most array greedies are one O(n) pass, or O(n log n) when they sort first. Say which one yours is and why the sort is needed.
  4. Find out where greedy breaks before you commit to it. Coin change with denominations {1, 3, 4} and target 6 gives 4+1+1 greedily, but 3+3 is optimal, so that problem needs DP. A quick counterexample check stops you from coding the wrong algorithm.
  5. Cover the edge cases: an empty array, a single element, ties in the sort key (does the order among equal keys change the answer?), zeros that block progress, and negative values if the input allows them.

Follow-up

  • Prove that sorting by end time, rather than by start time or by length, gives the maximum number of non-overlapping intervals.
  • Return the chosen elements or the jump path, not just the count or the boolean. What extra state do you keep?
  • Change the problem so greedy no longer works (minimum jumps with costs, or arbitrary coin denominations). What do you switch to, and what does it cost?

Efficient manipulation of tree structures

Medium · Data Structures and Algorithms

Approach

  1. Find out which operation is being asked for, because the title covers several standard tree tasks: BST insert and delete, lowest common ancestor, invert or mirror, flatten to a linked list, serialize and deserialize, and building a tree from its traversals.
  2. BST delete has three cases. A leaf is removed. A node with one child is replaced by that child. A node with two children takes the value of its in-order successor (the leftmost node of its right subtree), and then the successor is deleted from that subtree. Each operation is O(h).
  3. Use the BST invariant whenever it applies. LCA in a BST walks down from the root, going left while both values are smaller and right while both are larger, which is O(h) and needs no extra memory. In a general binary tree, LCA needs a postorder recursion that is O(n).
  4. Building from preorder and inorder: put the inorder value-to-index pairs in a hash map so each root split is O(1). That makes the whole build O(n) rather than O(n^2), assuming the values are unique.
  5. Say what recursion depth costs. Space is O(h), and a skewed tree makes h = n. Python's default recursion limit is about 1000, so for deep inputs write the iterative version with an explicit stack, or mention that a self-balancing tree (AVL or red-black) keeps h at O(log n).

Follow-up

  • Serialize a binary tree to a string and rebuild it exactly. How do you encode null children, and why is inorder alone not enough?
  • Do the same operation iteratively. What goes on the stack?
  • How does the LCA solution change if the nodes have parent pointers, or if one of the two values may be missing from the tree?

Optimization of search and traversal algorithms

Medium · Data Structures and Algorithms

Approach

  1. Start from the brute force and name the property that removes work. A sorted or monotonic input allows binary search. An unweighted graph allows BFS. Repeated subproblems allow memoization. A known target lets you prune branches early.
  2. Binary search: keep a clear invariant (for example, the answer always lies in [lo, hi)), compute mid as lo + (hi - lo) / 2 to avoid overflow in fixed-width languages, and choose lower-bound or upper-bound form for the first or last occurrence. The same template works on an answer space whenever a feasibility check is monotonic, which gives O(log range * cost of check).
  3. Graph traversal: BFS gives shortest paths in an unweighted graph in O(V + E). DFS with a visited set prevents revisits and infinite loops on cycles. With non-negative edge weights, use Dijkstra with a binary heap, O((V + E) log V). Bidirectional BFS shrinks the explored frontier when both endpoints are known.
  4. Pick the representation from the operations. An adjacency list costs O(V + E) memory and suits sparse graphs. A grid can be searched in place, with direction arrays and a visited matrix or in-place marking.
  5. Cover the edge cases: an empty input, duplicates around the target, a target that is absent, disconnected components, self-loops, and a start equal to the goal.

Follow-up

  • Some edge weights are negative. Why does Dijkstra fail, and what do you use instead? (Bellman-Ford, O(V * E), which also detects negative cycles.)
  • Search a sorted array that has been rotated at an unknown pivot in O(log n). How do you handle duplicates?
  • The graph is too large to fit in memory. How do you change the traversal?

Stop tag and share joins from fanning out a page

Medium · join fan-out · semi-joins · many-to-many

resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.

Approach

  1. Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
  2. Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
  3. Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
  4. Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.

Follow-up

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.

Approach

  1. Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
  2. Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
  3. Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
  4. Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.

Follow-up

  • Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
  • Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?

Designing a Car Parking system

Medium · Machine Coding and System Design

Approach

  1. Ask for the requirements before you write a class: which vehicle types, which spot sizes, how many floors and gates, how tickets work, and how fees are charged (hourly, flat or tiered). Also ask whether a smaller vehicle may take a larger spot. State the scope you will build in code and what you leave for discussion.
  2. Core model: ParkingLot (owns its floors and the active tickets), Floor, ParkingSpot (id, size, occupied flag), Vehicle (plate, type), and Ticket (id, plate, spot, entry time). Put the parts that change behind interfaces: a SpotAllocationStrategy (nearest first, or lowest floor first) and a FeeStrategy (hourly or flat). Then a new rule means a new class, not an edit to the lot.
  3. Choose data structures for the hot path. Keep a free-spot pool per size, either a set or a min-heap keyed by distance from the entrance, so park() and unpark() cost O(1) or O(log n) rather than a scan of every spot. Keep a map from ticket id to Ticket and from plate to ticket so exit lookups are O(1).
  4. Walk through both flows end to end. Entry: validate the vehicle, find a fitting spot, mark it occupied, issue a ticket. Exit: look up the ticket, compute the fee, free the spot, close the ticket. Return clear results for lot full, unknown ticket and a plate that is already parked.
  5. Raise concurrency: two gates competing for the last spot is a check-then-act race. Guard allocation per size pool with a lock, or claim the spot with an atomic compare-and-set, so a spot cannot be handed out twice.

Follow-up

  • Add EV charging spots and a different rate for them. Which classes change, and which stay untouched?
  • Two entry gates call park() at the same moment for the last compact spot. Walk through what your code does.
  • A driver loses the ticket. How do you find the vehicle's record and compute what they owe?

Implementing core business logic with focus on extensibility

Medium · Machine Coding and System Design

Approach

  1. Separate the parts that vary from the parts that stay fixed, and put an interface only where variation is stated or clearly likely. Pricing rules, allocation policies and validation checks are common candidates. An interface nothing will ever vary behind is abstraction for its own sake.
  2. Use Strategy for interchangeable algorithms (for example, a FeeCalculator with hourly and flat versions) and a Factory or a registry map from type to implementation for creation. Adding a type is then a new class plus one registration line, which is the open/closed principle in practice.
  3. For a growing set of business rules, model each rule as an object behind a common interface and evaluate an ordered list of them (chain of responsibility). Each rule can be tested on its own, and new rules can be added without editing old ones.
  4. Keep the domain logic free of I/O: inject repositories and clocks through constructors, so the core can be unit-tested with in-memory fakes and a fixed time. Injecting time matters for anything that computes a duration or a fee.
  5. Get a working, tested core first, then show where an extension slots in. Saying 'I would add X as a new strategy here' is cheaper than half-building three variants.

Follow-up

  • A new rule has to run only for certain customer types. How do you add it without an if-chain in the core service?
  • How would you unit-test the fee logic without waiting for real time to pass?
  • Which of your interfaces would you remove if the requirements never change, and why?

Handling edge cases in system state management

Medium · Machine Coding and System Design

Approach

  1. Make the states explicit: an enum per entity (for example, a spot moves FREE to OCCUPIED to FREE, and a ticket moves ACTIVE to PAID to CLOSED) plus a transition table or a method per transition. Rule out impossible combinations by construction, not with scattered boolean flags.
  2. Reject illegal transitions loudly. Exiting with a closed ticket, paying twice, or freeing a spot that is already free should return a specific error rather than fail silently, so the caller knows what happened.
  3. Make retried operations idempotent where a retry is realistic. Keying payment and exit on the ticket id means a duplicate request returns the earlier result instead of charging twice or freeing a spot that has since been reassigned.
  4. Keep the invariants checkable, for example: the free count per size equals the number of FREE spots of that size, and every OCCUPIED spot has exactly one ACTIVE or PAID ticket. Assert them in tests after every operation.
  5. Cover the edge cases: capacity at zero and at full, unknown or null ids, events arriving out of order (exit before payment), and concurrent requests on the same entity. Guard the check-then-act sections with a lock or an atomic update.

Follow-up

  • The process crashes between charging a payment and freeing the spot. What state is left behind, and how do you recover?
  • How would you write table-driven tests that cover every valid and invalid transition?
  • Two requests try to close the same ticket at once. What does each caller see?

Discussing class hierarchy and interface design

Medium · Machine Coding and System Design

Approach

  1. Use inheritance only for a real is-a relationship with shared invariants. Use interfaces for capabilities (Chargeable, Payable) and composition for behaviour that varies independently. A deep hierarchy built to share a few lines of code is the usual mistake.
  2. Check every subclass against Liskov substitution: any subclass must work wherever the base type is expected. A subclass that throws UnsupportedOperationException, or quietly ignores a base method, means the abstraction is wrong. Split the interface or move the behaviour into a component.
  3. Keep interfaces small (interface segregation) so an implementer never has to stub methods it does not need. Have classes depend on the interface, not on a concrete class, so tests can pass in fakes.
  4. Know the language details. In Java, an abstract class can hold state and constructors, while an interface cannot hold instance fields but can provide default methods (since Java 8). In Python, abc.ABC with @abstractmethod stops instantiation of incomplete subclasses. In C++, pure virtual functions do the same job, and a polymorphic base needs a virtual destructor.
  5. Replace type switches with polymorphism. An instanceof or type-check chain that has to grow with every new type should become a method on the type, or a strategy looked up from a map.

Follow-up

  • Would you make Vehicle an abstract class or an interface? Justify the choice from the state and behaviour it needs to share.
  • Adding one new type means editing how many files in your design? Can you get it down to one new class?
  • Where would you choose composition over inheritance in this design, and what does it cost you?

Edge instances grow 400 MB per hour until the nightly restart

Hard · memory leak · heap analysis · metric cardinality

Edge API instances start at 700 MB resident and grow about 400 MB/hour; a nightly rolling restart has hidden it for weeks. Growth continues unchanged when request rate halves overnight, p99 degrades in the last hours before an instance is recycled, and heap used immediately after a forced full GC rises monotonically. The service holds no product state. Name the discriminating measurement that separates the plausible causes, give the most likely cause, and give the fix and how you would verify it.

Approach

  1. Separate resident memory from live heap first, because they fail differently. Resident size can grow from fragmentation, native buffers or thread stacks while the heap is flat; heap used after a full GC rising monotonically is the measurement that says objects are reachable and not being released. You already have it, so this is retention, not fragmentation, and that closes off half the candidate list.
  2. Use the rate's independence from traffic as the discriminator. Growth that continues at half the request rate rules out per-request objects that are merely slow to collect and points at a structure that grows with distinct values observed rather than with call volume. Write the candidates that have that property: a metrics registry keyed on a high-cardinality label, an unevicted cache, an interner, a per-key lock map.
  3. Take two heap snapshots an hour apart and diff by retained size, reading the dominator tree, not by allocation count or instance count. Expect one root holding a map with millions of entries, then follow the reference chain to the code that inserts and never removes. Allocation profilers point at churn, which is the wrong signal here.
  4. The candidate that fits this service is an observability label carrying an identifier, such as a request path recorded before templating so that /v1/resources/48213 becomes its own metric series. That grows with distinct ids seen, is independent of rate, and explains the late p99 degradation, since GC cost rises with the size of the live set.

Follow-up

  • Post-GC heap is now flat but resident size still creeps. What are you looking at, and does it matter?
  • How would you have detected this before an OOM, given the nightly restart masked the trend?

Suggested practice checklist

The plan follows the reported question families in order: tree and greedy algorithms first, then machine coding and design, then the leadership conversation, with a full mock on the last day.

Day 1 — Choose a language and set a baseline

  • Pick one of Java, C++ or Python and write a BST node class, an in-order traversal and a stack-based version without looking anything up
  • Write down the four reported rounds and mark the one where your preparation is weakest
  • Time yourself explaining the complexity of each traversal out loud

Deliverable: A working BST node and traversal file in your chosen language, plus a one-page note of the four rounds with the weakest marked.

Day 2 — Binary Search Tree questions

  • Solve the Kth largest element in a Binary Search Tree with reverse in-order recursion, then with an explicit stack
  • Add a subtree-size field and answer repeated Kth queries in O(h), then write down when the extra field is worth it
  • Solve two more tree-manipulation problems, such as validating a BST and finding a lowest common ancestor

Deliverable: Three solved tree problems, each with its time and space complexity written beside it, and the Kth largest solved in two ways.

Day 3 — Greedy problems on arrays

  • Solve three medium greedy array problems, for example jump reachability, interval merging and gas-station style circuits
  • For each one, write the sentence that justifies the greedy choice, and one counterexample that shows when a different rule fails
  • Re-solve the slowest one in linear time and state the space cost

Deliverable: Three greedy solutions, each with a written justification and a linear-time version where one exists.

Day 4 — Search and traversal optimization

  • Practise binary search on a sorted array and on an answer range, then BFS and DFS on a tree and on a grid
  • For each, write the plain version, its cost, and the improvement, such as early exit or pruning
  • Run each solution against an empty input, a single element and a worst-case shape

Deliverable: Four search or traversal solutions with a brute-force cost and an improved cost for each, and a list of the edge cases that broke your first attempts.

Day 5 — Machine coding: the Car Parking system

  • Write the entities first: vehicle types, spot sizes, ticket, level and lot, then the park, unpark and availability operations
  • Implement the smallest end-to-end flow and test it from a main method or unit test with several vehicle types
  • Add a fee calculation and a spot-allocation strategy behind interfaces

Deliverable: A running Car Parking system with tests for at least four scenarios and a one-paragraph description of the class design.

Day 6 — Extensibility, state edge cases and class design

  • Add a new parking rule to your lot using Strategy and verify no existing class changed
  • List and handle state edge cases: a full lot, parking the same vehicle twice, unparking an unknown ticket, and two vehicles taking the last spot
  • Explain your class hierarchy and interfaces aloud, including where you chose composition over inheritance and why

Deliverable: An extended parking system that handles a written list of at least eight edge cases, plus a spoken explanation of the class hierarchy.

Day 7 — Leadership round preparation and full mock

  • Write one-page outlines for two or three past projects covering decisions, rejected alternatives, conflicts and results
  • Run one full mock: a tree or greedy problem first, then a short machine coding task, then project questions, narrating throughout

Deliverable: Project outlines for two or three projects, three rehearsed practice answers and one completed mock with notes on where you went silent.

Behavioural preparation

Candidates describe the Leadership Evaluation, also called the managerial round, as more than a formality: it involves detailed discussion of past projects, the challenges you hit, the decisions you made, how you contributed to the team, and how conflicts were handled. For a backend role that works with data scientists and product managers, choose stories with technical substance and with a working relationship in them.

  1. Walk through the project you are proudest of: your role, the hardest technical decision, the alternative you rejected and what you would change now.
  2. Describe a time you disagreed with a teammate about a technical approach. What did each side argue, how was it resolved, and what was the result?
  3. Tell me about a feature you built with a data scientist or product manager. How did you turn their requirements or model into something you could ship?
  4. Describe a design review where your design was challenged. What did you change, and what did you defend and why?
  5. Tell me about a time you owned a complex task from start to finish and something went wrong along the way. What did you do and what did you measure?

Narrate an outage you owned from page to postmortem

Hard · incident response · blast radius · postmortems

Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.

Approach

  1. Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
  2. Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
  3. Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
  4. Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.

Follow-up

  • What would you do differently in the first five minutes, given the same dashboard and no more information?
  • Which follow-up action did you deliberately not take, and why was dropping it the right call?

Tell callers you do not own that their integration breaks

Medium · deprecation · compatibility · stakeholders

A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.

Approach

  1. Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
  2. Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
  3. Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
  4. Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.

Follow-up

  • How would you detect a consumer that reads the field only during a monthly export?
  • One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?

Ship under a deadline and bound the debt you chose

Medium · pagination · technical debt · tradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach

  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.

Follow-up

  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?