Interview guides · Aviso AI
Aviso AI Software Engineer Interview Guide
Aviso AI Software Engineer interview guide: reported coding, HashMap vs TreeMap, process vs thread and OS/DBMS questions, with a 7-day plan.
The 60-second brief
Candidates describe Aviso AI as a company that builds an AI-driven platform for sales, revenue and forecasting. The role description they see mentions forecasting engines, large data volumes, and newer LLM-based agentic features. It lists project examples such as refactoring a core module for speed, building an API for an agentic feature, and tuning a data pipeline that feeds forecasting models. In practice, a Software Engineer here sits between product managers, data scientists and the code that serves their models, so you should be ready to talk about both the algorithms you write and where predictions come from.
This guide is for candidates interviewing for the Software Engineer position at Aviso AI. It covers the coding questions candidates report (Two Sum, linear-time string work, HashMap versus TreeMap, complexity analysis, search edge cases), the process-versus-thread question, the OS, DBMS and Networks fundamentals that candidates are told to refresh, and a short layer of product and LLM context. Reported languages are Python, Java and C++, so pick one and use it throughout.
Candidates report two to three technical rounds and then an HR discussion, with online or written coding tests as well as live coding while sharing your screen.
Focus areas
- Java
- Data Structures & Algorithms (DSA)
- Problem Solving (coding under time constraints)
Overview
The reported question set is narrow and classic. Two Sum, a string task that must reach linear time, a choice between HashMap and TreeMap, a complexity write-up for your own solution, and edge cases in search algorithms are all things you can rehearse precisely. Because the set is small, shallow familiarity is not enough: you want to state the brute-force version, the improved version, the exact time and space cost of each, and the inputs that break each one, without prompting.
Candidates also report that core subjects such as OS, DBMS and Networks come up alongside coding, and the one reported example is the difference between a process and a thread. Treat that as a signal to refresh the standard fundamentals in a form you can say out loud: address spaces, context switches, shared state and locks, then indexes, transactions and isolation, then the request path across TCP and HTTP.
The role description mentions forecasting engines, data pipelines, LLM-based agentic features and working with data scientists. No reported question tests those directly, but candidates are told to be able to connect technical choices to sales and revenue forecasting. Prepare one or two examples where you chose a data structure, a query or an API shape because of a data volume or latency need, and be able to say in a sentence what an LLM-based feature would add to a forecasting product.
Reports also mention live sessions in which you hit a dead end and have to change direction, sometimes with a hint. Practise that deliberately: during mock sessions, stop halfway, declare the approach wrong, and write down what you would switch to and why.
Where to focus your preparation
Candidates describe the process as a few technical stages followed by an HR discussion, but no named rounds or question-to-round mapping has been reported, so the single entry below covers the whole loop.
Preparation focus
Candidates report two to three technical rounds and then an HR discussion, and that the technical part mixes online or written coding tests with live coding where you share your screen. Candidates also report that the whole process finishes within a few weeks of entering the pipeline when scheduling allows. No order of stages and no question-to-stage mapping has been reported, so ask the recruiter for the sequence and whether each stage involves writing code. For preparation, split your time between three things. First, the reported coding questions (Two Sum, a linear-time string task, HashMap versus TreeMap, complexity analysis, search edge cases) in one language you choose from Python, Java and C++. Second, the fundamentals that candidates say come up: OS, DBMS and Networks, including process versus thread. Third, a short account of how forecasting and LLM-based features relate to the work.
What to demonstrate
- Working code in a live setting, with the time and space cost stated for each version of the solution
- Core subject knowledge in OS, DBMS and Networks, as candidates report these alongside coding
- How you react when your first approach fails or you get a hint mid-session
- Whether you can connect technical decisions to sales and revenue forecasting
How to prepare
- Pick Java, Python or C++ and implement Two Sum three ways (nested loops, one-pass hash map, sorted two-pointer), writing the complexity of each next to the code
- Solve a sliding-window string problem and an anagram or frequency-count problem in linear time, then explain why string concatenation inside a loop can break linearity
- Write a one-page comparison of HashMap and TreeMap: lookup cost, ordering, range queries, null keys, and when each is the better pick
- Say aloud, with no notes, the difference between a process and a thread, then a short summary of what an index and a transaction do in a relational database
Where candidates lose points
Stopping at the hash-map Two Sum without covering duplicates or reuse of the same element
Check the complement before inserting the current value, so [3, 3] with target 6 works and one element is never paired with itself. State the sorted two-pointer variant and the O(1) space it buys, and say what you return when no pair exists.
Calling a string solution linear while it concatenates strings or slices inside a loop
Name where hidden quadratic cost can come from (repeated concatenation, copying substrings, recomputing a window sum) and use a StringBuilder, list join, index pointers or a running count. Then state the final time and space for the whole function.
Choosing HashMap or TreeMap by habit with no stated access pattern
Open with the operations the task needs. Pure key lookup points to a HashMap with O(1) average cost; sorted iteration, floor or ceiling lookups, or range queries point to a TreeMap with O(log n). Mention null-key behaviour and that hash quality drives the average case.
Giving a textbook definition of process versus thread with no consequence for design
Tie it to a choice: separate address spaces give crash isolation but need IPC, while threads share the heap, switch cheaply and need locks or immutable data to stay correct. Give one example of a shared-state bug and how you would prevent it.
Freezing or defending the first approach when it turns out to be a dead end during a live session
Practise saying what is wrong with the current approach (state it: the cost, the missed case), what property of the problem you will use instead, and then restarting from the working parts. Treat an interviewer hint as input to that sentence, not as a rescue.
Questions & practice
How would you implement a solution for the Two Sum problem?
Medium · Data Structures and Algorithms
Approach
- Start from the brute force: check every pair with two nested loops, O(n^2) time and O(1) extra space, and say that this is the baseline you are improving on.
- Do it in one pass with a hash map from value to index: for each element x at index i, look up target - x first, return the stored index if it is there, and only then insert x. Checking before inserting stops an element from pairing with itself and still handles duplicates such as [3, 3] with target 6.
- Give the cost: O(n) average time because hash lookups are average O(1), and O(n) extra space for the map. That memory is the trade-off against the O(1) extra space of the brute force.
- If the array is sorted, or the indices are not needed, use two pointers from both ends: move the left pointer up when the sum is too small and the right pointer down when it is too large. That is O(n) on sorted input and O(n log n) if you have to sort first. To return original indices, sort (value, index) pairs.
- Edge cases: no valid pair (say what you return or throw), negative numbers and zero, and in Java or C++ int overflow in target - x or a + b near the limits (compute in long).
Follow-up
- Return every unique pair that sums to the target, without duplicate pairs. How do you skip repeats?
- Design a class with add(number) and find(target). Which operation would you make O(1), and what does the other one cost?
- Extend this to 3Sum. What complexity can you reach, and how do you avoid duplicate triplets?
Can you optimize a string manipulation task to achieve linear time complexity?
Medium · Data Structures and Algorithms
Approach
- Ask which string task it is, then name the O(n^2) or O(n*m) version and what makes it slow: a nested scan over substrings, or repeated concatenation in a loop. Java Strings are immutable, so s += c inside a loop copies the whole string each time; use StringBuilder in Java and ''.join(parts) in Python.
- For substring problems such as the longest substring without repeating characters, use a sliding window with a map from character to last-seen index. When a repeat appears, jump the left edge to max(left, last[c] + 1). Each pointer only moves forward, so the total work is O(n).
- For anagram, permutation-in-string or character-count checks, keep a frequency count: an int[26] array for lowercase ASCII (O(1) extra space) or a hash map for a general character set. Compare counts as a fixed-size window slides, adding one character and removing one at each step.
- For pattern search, naive matching is O(n*m) in the worst case. KMP (a prefix-function table) or the Z-algorithm finds every match in O(n + m). Two pointers handle palindrome checks and in-place reversal in O(n).
- Edge cases: empty string, a single character, all characters identical, case and whitespace rules, and in Java the fact that char is a UTF-16 code unit, so characters outside the BMP take two chars.
Follow-up
- The input is now arbitrary Unicode rather than lowercase ASCII. What changes in your counting structure and in its space cost?
- Why is the sliding window O(n) even though it has a loop inside a loop? Give the amortized argument.
- Find every starting index in s where an anagram of p begins. What is the complexity?
Explain your approach to choosing between a HashMap and a TreeMap for a specific data retrieval task.
Medium · Data Structures and Algorithms
Approach
- Decide from the access pattern. If all you need is exact-key lookup, insert and delete, choose HashMap: average O(1) per operation, with no ordering guarantee. If you need sorted order or range and nearest-key queries, choose TreeMap, a red-black tree with O(log n) get, put and remove.
- Name the TreeMap operations that justify the extra cost: floorKey, ceilingKey, firstKey, lastKey, headMap, tailMap and subMap. Example: find the latest record at or before a timestamp, or every key in a range. A HashMap answers those only with a full O(n) scan.
- Contracts: HashMap depends on equals and hashCode being consistent, and a key whose hashCode changes after insertion becomes unreachable. TreeMap needs Comparable keys or a Comparator, and that ordering should be consistent with equals. HashMap allows one null key; TreeMap with natural ordering throws NullPointerException on a null key.
- Worst case and memory: since Java 8, a HashMap bucket that grows past 8 entries (once the table has at least 64 slots) turns into a tree, so heavy collisions degrade to O(log n) per lookup rather than O(n). Resizing makes insertion amortized O(1). Each TreeMap entry carries left, right and parent pointers plus a color.
- Mention the neighbours: LinkedHashMap for insertion order or access order (the basis of an LRU cache). Neither HashMap nor TreeMap is thread-safe, so concurrent code uses ConcurrentHashMap or ConcurrentSkipListMap for the sorted case.
Follow-up
- Build an LRU cache with LinkedHashMap. Which constructor flag and which overridden method do you need?
- What happens to lookups if attackers can choose keys that all collide, and how does Java 8 limit the damage?
- You need the top-k keys by value, with values updated often. Is either map enough, or would you add a heap or a second structure?
Describe the time and space complexity of your proposed solution for a given coding problem.
Medium · Data Structures and Algorithms
Approach
- Name the size variables first (n elements, m for a second input, k for the alphabet or window size), then count work by how loops and recursion depend on them. Nested loops over the same input are O(n^2) only when the inner loop really runs O(n) times on each pass.
- For recursion, write the recurrence and solve it. T(n) = 2T(n/2) + O(n) is O(n log n), as in merge sort. Binary search, T(n) = T(n/2) + O(1), is O(log n). Memoized DP costs the number of states times the work per state.
- Separate the cases: average versus worst (hash lookups average O(1) but can degrade; quicksort averages O(n log n) with an O(n^2) worst case) and amortized costs (appending to a dynamic array is amortized O(1) because of capacity doubling).
- For space, count the auxiliary structures and the recursion stack: DFS on a tree uses O(h), which becomes O(n) on a skewed tree. Say whether the output itself is counted.
- Point out hidden costs: Python slicing copies O(k), x in list is O(n), sorting is O(n log n), and hashing a string costs its length. Tie the result to the constraints, for example why an O(n^2) solution is unlikely to pass at n = 10^5.
Follow-up
- Can you bring the extra space down to O(1)? What do you give up in time or readability?
- What is the amortized cost of n appends to a dynamic array that doubles its capacity, and why?
- Your recursive solution is correct but fails on very large inputs. What happened, and how would you change it?
Explain the difference between process and thread in the context of design.
Medium · Core Computer Science Fundamentals
Approach
- Definitions: a process has its own virtual address space, file descriptor table and other OS resources. Threads in one process share the heap, globals and open files, but each thread has its own stack, registers, program counter and thread-local storage.
- Design consequences: processes isolate faults, so one crashing worker does not take down the others, while a segfault in one thread kills the whole process. Threads talk through shared memory, which is fast but needs mutexes, locks or atomics to avoid data races. Processes communicate through IPC (pipes, sockets, shared memory segments, message queues), which needs serialization.
- Cost: creating a process and switching between processes costs more than doing the same with threads, because each process switch changes address space. On Unix, fork uses copy-on-write to reduce the copying cost.
- Match the model to the workload. In standard CPython the GIL stops threads from running Python bytecode in parallel, so CPU-bound work uses multiprocessing, and I/O-bound work uses threads or asyncio. Java threads run in parallel on OS threads, and Java 21 virtual threads make large numbers of blocking I/O tasks cheap.
- Close with a concrete choice: a pre-forked pool of worker processes behind a web server for isolation, or a thread pool inside one service for shared in-memory caches. State the failure modes you would guard against: deadlock, races, and memory growth across many processes.
Follow-up
- How would you parallelize a CPU-bound batch job in Python, and what does sharing data between workers cost?
- What conditions must all hold for a deadlock, and how would you prevent one in your design?
- When would you choose async I/O over a thread pool, and what goes wrong if a coroutine makes a blocking call?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
Explain why the owner filter ignores the listing index
Medium · composite index · query plans · sort elimination
The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.
Approach
- Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
- For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
- Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
- Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.
Follow-up
- 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
- A dashboard runs this for 40 owners in one page load. What changes about the design?
Specify the outbound webhook contract a customer endpoint can verify
Medium · webhooks · hmac · at-least-once
Egress delivery sends resource.created, resource.updated and resource.archived to roughly 40k customer endpoints at about 1.5k deliveries/second, with a per-destination concurrency cap of 4 and a 10 second connect-plus-read timeout, at-least-once from the relay. The receiver is code you do not control, often behind a proxy that may re-encode JSON. Specify the payload envelope, how the receiver verifies authenticity and rejects replays, what ordering it may assume, how it deduplicates, which status codes it should return and what each means to us, and the retry, circuit and replay behaviour.
Approach
- Send the fact, not a pointer: event_id, event_type, aggregate_type, aggregate_id, aggregate_version, occurred_at and the payload copied from outbox_event. A receiver told only that resource 42 changed has to call back, and the call back returns a newer state than the event describes - that is precisely how a consumer applies changes out of order while believing it is following them.
- Sign the exact bytes on the wire: HMAC-SHA256 over the timestamp, a separator, and the raw body, in a header, compared in constant time before the body is parsed. Instruct receivers to verify the raw bytes, never a re-serialised copy, since a proxy that reorders keys or changes whitespace invalidates a signature computed over re-encoded JSON. Reject a timestamp outside a tolerance - five minutes is typical - and keep a short seen-set of event_ids, because a capture replayed inside the tolerance still verifies.
- Support key rotation by sending several signature values in one header during the overlap window and publishing that any one that verifies is sufficient. Without that rule a key rotation is a synchronised outage across every destination at once.
- State the guarantee as it actually is: at-least-once and unordered, including within a single aggregate_id. The outbox is ordered per aggregate at the source, but the relay holds up to 4 requests in flight per destination and backs off each retry independently, so a version 6 delivery that needs one retry lands after a version 7 that succeeded on its first attempt. Publishing per-aggregate ordering we do not enforce is worse than publishing none, because a receiver that trusts it overwrites new state with old. Receivers deduplicate on event_id and apply conditionally - UPDATE ... WHERE aggregate_version < :incoming, or the same comparison under a row lock on the aggregate - which discards stale redeliveries and stays correct when two deliveries for one aggregate are processed at the same time. A duplicate and an out-of-order delivery then have the same outcome: no-op.
Follow-up
- A customer insists an event never arrived. What do you check, in order, and which table settles the argument?
- A receiver cannot store a version and asks us to deliver their events in order. What would the relay have to give up to offer that, and what does one failing event then do to the rest of their stream?
Make resource creation safe for a client that retried a timeout
Medium · idempotency · retries · race conditions
POST /v1/resources creates a resource row and, in the same transaction, a resource_revision and an outbox_event. A partner's HTTP client times out at 2 seconds and retries twice; under load the first request is still executing when the retry arrives, and duplicate resources are appearing. Using idempotency_key (tenant_id, idempotency_key, request_fingerprint, state, response_status, response_body, resource_id, locked_until, expires_at), specify the whole contract: first call, concurrent retry, retry after completion, a key reused with a different body, and what the client does with each response.
Approach
- Name why the plain REST shape fails here. POST is not idempotent, a timeout leaves the client unable to distinguish a lost request from a lost response, so it must retry - and the retry is a second create. Checking whether the key exists and then inserting it is check-then-act: under exactly the concurrency that caused the timeout, both attempts look, both see nothing, both proceed.
- Let the unique constraint arbitrate instead. INSERT the key row with state 'in_flight' and locked_until set to now plus the request deadline, and commit that reservation on its own before any work is attempted - an uncommitted row is invisible to the concurrent retry, which is the entire point of writing it first. One caller wins the insert and proceeds; the other takes the unique violation and reads the row rather than falling through to the work.
- Branch the loser on what it found. 'succeeded' replays response_status and response_body verbatim, with a header marking it a replay. 'in_flight' with locked_until in the future returns 409 and a Retry-After - do not park a request thread waiting on another request, that is how a concurrency bound is consumed by retries. 'in_flight' past locked_until is reclaimable by a conditional UPDATE predicated on the old locked_until, so exactly one reclaimer wins. A fingerprint mismatch is 422 and never the stored response: a different body under the same key is a client defect, and serving the old response hides it forever.
- Commit the effect and the key's completion together - resource insert, revision row, outbox row, and the UPDATE of the reserved key to 'succeeded' with the stored response, in one transaction. That makes a crash binary: either all of it is durable and the key reads 'succeeded', or the transaction rolls back and the only durable trace is the reservation row, still 'in_flight', with no resource behind it. The reclaim branch finishes that case - once locked_until passes, one retry takes the row by conditional UPDATE and runs the work for the first time. Splitting the two commits is what creates the window with no recovery: the effect durable, the key still 'in_flight', and a reclaimer that cannot distinguish that from work never started, so it creates a second resource.
Follow-up
- Response bodies are pruned at 24 hours but resource_id is kept. What do you return to a replay that arrives after pruning?
- The endpoint now also sends a welcome email. Which part of this contract prevents a second send, and which part cannot?
Every query on one table stalls for forty seconds mid-deploy
Medium · lock contention · ddl · migrations
During a release on PostgreSQL, every query touching resource times out for about 40 seconds and then recovers with no intervention. The release ran one migration, ALTER TABLE resource ADD COLUMN archived_reason TEXT, and the migration log shows it completing in 6 ms. Unrelated tables showed no change in error rate. Explain how a 6 ms statement caused a 40-second stall, give the ordered checks you would run on a live system to confirm it, and give the migration procedure that prevents a repeat.
Approach
- Separate the statement's duration from the lock's duration. ADD COLUMN with no default is a catalogue-only change and genuinely runs in milliseconds, but it requires ACCESS EXCLUSIVE, and it cannot acquire that until every transaction already touching the table has finished.
- Account for the queueing, which is the part that surprises people. A lock request that is waiting blocks later requests for conflicting modes behind it rather than letting them overtake, so one long-open transaction holds the DDL and the DDL holds all the traffic. The stall length is set by the longest open transaction, not by the size of the change.
- Confirm on a live system in this order: pg_stat_activity for that table ordered by xact_start, looking for the oldest transaction and specifically for state = idle in transaction; then pg_locks where granted = false to find the waiter; then join them on pid to name blocker and blocked. pg_blocking_pids() does that join for you and is the fastest single call.
- Prevent rather than merely time it better. Set lock_timeout to a second or two on the migration session so the DDL abandons the queue after a bounded wait and is retried, instead of holding it for as long as the oldest transaction lives. Be exact about what that buys: queries arriving during the wait still queue behind the pending ACCESS EXCLUSIVE request, so each attempt costs them up to one lock_timeout of added latency. The outage goes from 40 seconds to about one second per attempt, not to zero. Also run migrations away from deploy-time peaks, and put a statement timeout and an idle-in-transaction timeout on the analytics role that opens the long transactions.
Follow-up
- The same release also wants NOT NULL on that column. What is the sequence that gets there without a long lock?
- Your lock_timeout retry fails ten times in a row because the analytics transaction is always open. What do you change?
Suggested practice checklist
Each day targets one cluster of reported questions or the core subjects candidates say come up, ending in something you can read back: code with stated complexity, a comparison sheet, or a rehearsed spoken answer.
Day 1 — Fix the format and your language
- Ask the recruiter for the stage order, which stages involve online or written coding versus live screen-sharing, and whether the HR discussion comes last
- Choose one of Python, Java or C++ for the whole loop and write a one-page list of the collection types, sort calls and string utilities you will use in it, with their complexities
Deliverable: The recruiter's answers on format written in one place, plus a one-page cheat sheet for your chosen language.
Day 2 — Two Sum and complexity statements
- Implement Two Sum with nested loops, a one-pass hash map and a sorted two-pointer version, and test duplicates, a negative target and no-solution input
- For each version write the time and space cost in one sentence and say which constraint makes the brute-force version too slow
- Rehearse the answer to describing the time and space complexity of a solution you just wrote, aloud and without notes
Deliverable: Three working Two Sum implementations, each with a one-sentence complexity statement, and one spoken complexity explanation.
Day 3 — Linear-time string problems
- Solve a sliding-window problem such as the longest substring without a repeated character, and an anagram check using a fixed-size count array
- Rewrite one string solution that was quadratic because of concatenation or repeated slicing, and note exactly which line caused the extra cost
- State the final time and space of each solution before you run it
Deliverable: Three string solutions in linear time, plus a note on the quadratic version you removed and why it was quadratic.
Day 4 — HashMap versus TreeMap, and search edge cases
- Write a comparison sheet of HashMap and TreeMap covering lookup cost, ordering, floor and ceiling queries, range views and null keys, and attach a task where each wins
- Implement binary search for the first and last occurrence of a value with an explicit invariant, and write a table of edge-case inputs (empty, one element, absent target, duplicates) that you run against it
- Choose one real search you have written at work or in a project and rehearse the edge-case answer as a short story with the bug you caught
Deliverable: A HashMap versus TreeMap sheet, a tested leftmost/rightmost binary search with an edge-case table, and one rehearsed spoken story.
Day 5 — OS, DBMS and Networks fundamentals
- Explain process versus thread aloud: address space, context-switch cost, shared state, crash isolation, and one race condition with its fix
- Write a short account of what happens from a browser request to a response over TCP and HTTP
Day 6 — Live-session mock with a forced pivot
- Share your screen with a friend or record yourself, solve a medium or hard problem from the reported topics (a string or array task, dynamic programming, or graph traversal) while narrating complexity
- Halfway through, declare your approach wrong and say what you are switching to and why, then finish the new version
- Review the recording for stretches of silence and unclear complexity claims
Deliverable: One recorded mock session with a worked pivot, and a list of the moments you went silent or lost the invariant.
Day 7 — Product context, behavioral stories and dry run
- Write a short paragraph on how a forecasting product could use LLM-based features, and one example from your own work where a data or latency constraint drove a technical decision
- Run a full dry run of one coding question, one fundamentals question and one story, then write two questions to ask the team
Deliverable: A product-context paragraph, two rehearsed stories with measured numbers, and two questions for the interviewers.
Behavioural preparation
Candidates report an HR discussion after the technical rounds, and no specific behavioral questions are reported for it. The one reported question that candidates file here asks how you handle edge cases when implementing complex search algorithms, which is really a story about your engineering habits. Prepare examples that show how you test and verify, how you recover from a wrong approach, and how you connect your work to what the product does for sales and revenue teams.
- Tell me about a time you went down the wrong path on a technical problem. What made you realise, and how did you change course?
- Describe a time you took feedback or a hint on your approach and it changed the outcome.
- Give an example of a technical decision you made because of a data volume, latency or accuracy need, and what you measured.
- Why do you want to work on AI-driven forecasting software, and what would you want to learn from data scientists on the team?
How do you handle edge cases when implementing complex search algorithms?
Medium · Data Structures and Algorithms
Approach
- Treat it as a story-plus-technique answer. Pick one real search you wrote (binary search over sorted data, BFS or DFS over a graph, a lookup in an index) and open with what the function had to guarantee, then walk the edge cases that mattered for that specific search instead of reciting a generic list.
- Start from an invariant. For binary search, decide whether the answer lives in [lo, hi] or [lo, hi), write that down, and make every branch preserve it. Most off-by-one bugs (lo <= hi versus lo < hi, mid versus mid + 1) disappear once the invariant is explicit.
- Name the concrete edge cases for your search: empty input, one element, target smaller or larger than everything, target absent, duplicates (and whether you need the first, last or any occurrence), the mid computation overflowing in a fixed-width int (use lo + (hi - lo) / 2), and for graph search cycles, disconnected components and a start node equal to the target. Say how each is handled in the code, for example a visited set to prevent infinite loops.
- Show how you verified it: list the edge cases as test inputs before coding, run them, and compare against a brute-force linear scan on random small inputs. If you have a bug you actually caught this way, tell that story, including what it would have cost in production, and the mistake that sinks the answer is claiming you never hit one.
- Close with complexity and the trade-off: binary search is O(log n) time and O(1) space on sorted data but needs the sort or an index maintained; BFS and DFS are O(V + E) with O(V) extra space for visited and the frontier.
Follow-up
- How would you change your binary search to return the leftmost occurrence of a duplicate value, and how do you prove the loop terminates?
- Tell me about a search bug that reached a test or production that your edge-case list missed, and what you changed in your process afterwards.
- How would you test a search over a very large dataset where a brute-force comparison is too slow to run?
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
- 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.
- 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.
- 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.
- 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?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?