Interview guides · Cognistx
Cognistx Software Engineer Interview Guide
Cognistx Software Engineer interview guide: four reported rounds, coding, system design and storage questions, behavioral stories and a 7-day plan.
The 60-second brief
Cognistx builds software aimed at helping clients make decisions through analytics and artificial intelligence, and candidates describe its products as serving clients across a range of industries. Reports for the Software Engineer role mention work that touches machine learning, data analysis and system design, working alongside data scientists, product managers and other engineers. The engineering problems described are about taking analytical ideas into working applications, scaling them, and keeping them fast on large datasets when results are needed in real time. That is why the reported questions lean on data storage, performance and architecture as well as plain coding.
This guide is for candidates interviewing for a Software Engineer position at Cognistx. It covers the four reported stages, the coding, storage, design, debugging and data-handling questions candidates report, and the behavioral prompts around conflict, deadlines, leadership and creative problem solving. Requirements candidates describe list Python, Java or C++, databases, cloud technologies and Agile or Scrum practice, so the preparation advice assumes one of those languages.
Candidates report four stages over roughly 3-5 weeks: a screening call, technical assessments, coding challenges and behavioral interviews. The last stage is described as run by product managers and technical leaders.
Focus areas
- General System Design Discussion
- Data Persistence / Data Storage Design
- Tradeoff Analysis (Design Tradeoffs)
Overview
The work described for this role sits between analytics and production software. Candidates report building and maintaining applications, debugging and tuning them, reviewing code and shipping features with cross-functional teams. The problems mentioned include scaling applications and optimizing performance on large datasets under real-time constraints. For preparation, that means you should be able to talk about where data lives, why a slow path is slow, and how a design changes when volume grows, not only solve isolated puzzles.
The reported question set splits into five groups. Technical questions cover object-oriented versus functional programming, trade-offs between data storage options, and debugging a performance problem in a large application. Design questions cover a URL shortening service, a microservices layout for an e-commerce platform, and monolith versus microservices. Problem-solving questions cover optimizing an existing algorithm and handling a dataset with missing values. Coding questions cover reversing a linked list, binary search and the longest substring without repeating characters. Other reported examples include merging two sorted arrays, designing a scalable chat application and optimizing a database query.
The topics candidates associate with this loop are general system design discussion, data persistence and storage design, trade-off analysis, architecture decision-making and scalability. Treat every design answer as a chain of decisions: name the option, the alternative, and the condition under which you would switch. A storage answer that ends in a product name without an access pattern, or a microservices answer that never mentions the cost of the network hop, will sound thin however much you know.
Behavioral stages are described as run by product managers and technical leaders, and the reported prompts concern team conflict, tight deadlines, leading a project, thinking outside the box, and working with non-technical stakeholders. Prepare four or five real stories from your own work, each with a decision you made, the alternatives, and a result, so that one story can be bent to answer several of these prompts.
The Cognistx interview process
Candidates describe four stages with the names below and a total span of about 3-5 weeks. Reports do not say which question lands in which stage, so the preparation items cover the question types each stage's format implies.
Screening Call
Candidates describe a short opening call that precedes the technical stages. No question list is attached to it, so treat it as the place where you explain your background, the language you work in and why this role, and where you learn how the remaining stages are arranged. The role lists Python, Java or C++, databases and cloud, so know which of those you would code in and what you have shipped with it.
What to demonstrate
- How clearly you summarise your recent work and the languages you use
- Whether your experience lines up with the listed stack: Python, Java or C++, databases, cloud
- Your reasons for wanting an engineering role close to analytics and AI products
How to prepare
- Write a short walkthrough of your last two projects that names the language, the datastore and one measurable result for each
- Choose the one language among Python, Java and C++ you will code in and be ready to say why
- Prepare questions about the team, the stages that follow and how the technical assessment is run
- Read the nice-to-have list candidates describe (machine learning, data processing frameworks, CI/CD, React or Angular) and mark honestly which you have touched
Technical Assessments
Candidates describe technical assessments that test technical skills, with topics reported as general system design discussion, data persistence and storage design, trade-off analysis, architecture decisions and scalability. The reported technical and design questions fit this format: object-oriented versus functional programming, storage trade-offs, debugging a performance issue, a URL shortener, microservices for e-commerce and monolith versus microservices. Answer with decisions and alternatives rather than lists.
What to demonstrate
- Understanding of storage and architecture options and their trade-offs
- Ability to reason about scale: what breaks first as load or data grows
- A structured method for locating a performance problem before changing code
- Clarity in explaining design choices and the reasons behind them
How to prepare
- Rehearse the storage trade-offs question by comparing a relational database, a key-value store and a document store against two access patterns each
- Outline a URL shortener end to end: ID generation, storage, redirect read path, caching and what happens on collisions
- Practise a debugging sequence for slow requests: confirm the symptom, measure, find the dominant cost, fix one thing, re-measure
- Prepare a one-minute answer on monolith versus microservices that names team size, deployment independence and the cost of distributed calls
Coding Challenges
Candidates describe coding challenges in which you solve algorithm and data structure problems on the spot. The reported coding questions are reversing a linked list, implementing binary search and finding the longest substring without repeating characters, with merging two sorted arrays and optimizing a database query appearing among other examples. Pick one language from the listed stack and code in it fluently.
What to demonstrate
- Working code for standard data structure and algorithm problems
- Choosing a data structure that fits the access pattern and stating the complexity
- Handling edge cases such as empty input, single elements and duplicates
- Ability to improve a first solution to a better one
How to prepare
- Write the linked-list reversal iteratively and recursively in your chosen language, and run it on empty, one-node and two-node lists
- Write binary search with a loop invariant, then variants for first occurrence and insertion point, and check the mid calculation
- Solve the longest-substring problem with a sliding window and a map of last-seen positions, and state why it is linear
- Practise talking while you code: state the brute force, its cost, then the improvement before typing
Behavioral Interviews
Candidates describe behavioral interviews about working style and fit, conducted by product managers and technical leaders. The prompts reported are about resolving team conflict, prioritizing under tight deadlines, leading a project and thinking outside the box, plus conflicting ideas and collaborating with non-technical stakeholders. Because product managers are part of the panel, include at least one story where you worked with someone outside engineering.
What to demonstrate
- How you handle disagreement and conflicting ideas within a team
- How you set priorities and communicate trade-offs when time is short
- Evidence that you have led work and can describe the outcome
- How you explain technical problems and solutions to non-technical stakeholders
How to prepare
- Write five stories from your own work covering conflict, a deadline squeeze, leading a project, an unconventional technical fix and a product-manager collaboration
- For each story, write the decision you personally made, the main alternative and one number or concrete result
- Cut each story to a two-sentence setup so the reasoning gets most of the time
- Prepare two or three questions for a product manager and for a technical lead about how the team works together
Where candidates lose points
Reversing a linked list while losing the rest of the chain, or failing on an empty or single-node list
Save the next pointer before you rewire, keep a previous pointer that starts empty, and run the code by hand on empty, one-node and two-node inputs before you say it is done.
Answering the storage trade-offs question with product names and no access pattern
Start from the reads and writes: what is the key, how large is the data, do you need joins or transactions, and how fresh must reads be. Then compare a relational store, a key-value store and a document store on those points.
Drawing boxes for the URL shortener or e-commerce microservices without making any decision, or splitting everything into services by default
Pick one or two decisions per design, such as how IDs are generated or where service boundaries sit, and give the alternative and what would make you switch. Mention the cost of network calls and data consistency when you split services.
Treating a performance debugging question as a request for a list of optimizations
Describe the order: reproduce and measure, locate the dominant cost with profiling or query plans, change one thing, then re-measure. Say what each measurement would rule out.
Telling behavioral stories entirely in 'we', so your own decision never appears, or skipping any story that involves a non-engineer
For each story, state the decision you personally made and the alternative you rejected. Prepare one story with a product manager or other non-technical stakeholder, since product managers are among the reported interviewers.
Questions & practice
How would you approach optimizing an existing algorithm for better performance?
Medium · Problem-Solving / Case Studies
Approach
- Measure before changing anything. Build a correctness test suite and a benchmark on realistic input sizes, then profile to find where the time actually goes. Optimising code that is not on the hot path changes nothing.
- Attack the complexity of the hot spot first. Replace nested O(n^2) membership loops with a hash set or map for O(n). Sort once and use two pointers for O(n log n). Memoise repeated subproblems (dynamic programming). Use a heap for repeated min/max and prefix sums for repeated range sums.
- Then cut constant factors. Avoid allocations inside loops. Build strings with a list and join, since repeated concatenation can go quadratic. Batch database and network calls instead of one call per item. Use vectorised libraries such as NumPy for numeric loops. Keep data contiguous for cache locality.
- Scale out only after that. Parallelise independent work, using multiprocessing rather than threads for CPU-bound Python because of the GIL. Stream or chunk the input when it does not fit in memory.
- State the trade-off of each change: memory for speed, precomputation cost against query count, and readability. Re-run the tests and the benchmark after every step so each gain is measured and nothing breaks.
Follow-up
- The profiler shows time spread evenly with no single hot spot. What do you do next?
- When can an O(n log n) algorithm run slower than an O(n^2) one in practice?
- How would your approach change if the input arrived as an unbounded stream?
Write a function to reverse a linked list.
Medium · Coding / Algorithms
Approach
- Iterative version: set prev = None and curr = head. While curr is not None: save nxt = curr.next, set curr.next = prev, set prev = curr, then curr = nxt. Return prev as the new head. This is O(n) time and O(1) extra space.
- Recursive version: reverse the rest of the list from head.next, then set head.next.next = head and head.next = None, and return the new head from the recursion. It is O(n) time but uses O(n) stack space, so very long lists can exceed Python's default recursion limit (about 1000).
- Edge cases to say aloud and test: an empty list (head is None), a single node, and two nodes. Make sure the old head's next ends up None; otherwise the result contains a cycle.
- Verify by walking the result and comparing it to the reversed input values. For a doubly linked list, swap each node's prev and next pointers, then return the last node visited.
Follow-up
- Reverse only the nodes between positions m and n, in one pass.
- Reverse the list in groups of k nodes, leaving any final partial group as it is.
- Check whether a linked list is a palindrome using O(1) extra space.
How would you implement a binary search algorithm?
Medium · Coding / Algorithms
Approach
- State the precondition first: the array is sorted, or more generally the predicate is monotone. Use lo = 0 and hi = n - 1. While lo <= hi: mid = lo + (hi - lo) // 2; return mid on a match, set lo = mid + 1 if a[mid] < target, else hi = mid - 1. Return -1 if the loop ends. This is O(log n) time and O(1) space.
- Explain the midpoint formula: (lo + hi) / 2 can overflow a 32-bit int in Java or C++, and lo + (hi - lo) / 2 cannot. Python integers do not overflow, but writing it the safe way shows you know the issue.
- Know the lower-bound variant, which returns the first index with a[i] >= target. Use a half-open range [0, n): while lo < hi: mid = (lo + hi) // 2; if a[mid] < target then lo = mid + 1, else hi = mid; return lo. With duplicates, this finds the first occurrence. Python's bisect_left and bisect_right implement both bounds. Java's Arrays.binarySearch returns -(insertion point) - 1 when the target is missing and makes no guarantee which duplicate it finds.
- Edge cases: an empty array, one element, a target below the minimum or above the maximum, and runs of duplicates. Watch for infinite loops: if a branch sets lo = mid while lo < hi, mid must round up.
- Extend it to binary search on the answer. When feasibility is monotone in a value (minimum capacity, integer square root), search over that value range and test feasibility at each mid.
Follow-up
- Search for a target in a rotated sorted array.
- Return the first and last positions of a target in an array with duplicates.
- Find the minimum ship capacity that delivers all packages within D days.
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?
Explain the difference between object-oriented programming and functional programming.
Medium · Technical / Domain Questions
Approach
- Define both precisely. Object-oriented code bundles state and the methods that change it into objects, and gets reuse from encapsulation, composition or inheritance, and polymorphic dispatch. Functional code builds programs from pure functions over immutable values, combined with higher-order functions such as map, filter and reduce.
- Show the same small task both ways. Applying a discount as an Order.apply_discount() method mutates the object in place. A pure function takes a list of orders and returns a new list, and leaves its input untouched.
- Name the expression problem as the real trade-off. In OOP, adding a new type is one new subclass, but adding a new operation means touching every class. In FP, adding an operation is one new function, but adding a new variant means touching every function that pattern-matches on it.
- List what purity buys: functions you can test without mocks, safe memoisation, and no shared mutable state to lock when you parallelise. List what it costs: copying immutable data (persistent structures reduce this through structural sharing), and deep recursion in CPython, which has no tail-call optimisation.
- Close with how real code works. Python, Java 8+ streams, Kotlin and Scala all mix the two styles. A common rule is a functional core (pure transformations) inside an imperative or object shell that handles I/O and state.
Follow-up
- What makes a function pure? Is a function that writes a log line still pure?
- How would you make a class immutable in Java or Python, and what does that change for thread safety?
- Where should mutable state live in a mostly functional design?
What are the trade-offs between different data storage solutions?
Medium · Technical / Domain Questions
Approach
- Begin from the access pattern, not from product names. Ask about the read/write ratio, the query shape (point lookup by key, range scan, joins, full-text search, aggregation), the consistency each operation needs, the data volume and the latency target.
- Relational databases (PostgreSQL, MySQL) give ACID transactions, joins, enforced schemas and secondary indexes. They usually scale up first, then add read replicas. Sharding writes is harder and makes cross-shard joins and transactions expensive.
- Key-value stores (Redis, DynamoDB) give fast lookups by key and partition horizontally, but they only answer the queries the key design allows. Redis keeps data in memory, so RAM caps its capacity, and durability depends on the RDB/AOF settings. Document stores (MongoDB) fit nested records you read whole, but cross-document joins are weaker.
- Wide-column stores (Cassandra) handle heavy writes through LSM-tree storage and offer tunable consistency, but you design one table per query around the partition key. Columnar warehouses and Parquet files are built for analytic scans over a few columns of many rows. Object storage (S3) is cheap for blobs and files, with high per-request latency and no in-place updates.
- Name the cross-cutting trade-offs. B-tree engines favour reads, while LSM engines favour writes at the cost of compaction and read amplification. Every index speeds reads and slows writes. Under a network partition, CAP forces a choice between consistency and availability, and PACELC adds the latency-versus-consistency choice when there is no partition.
Follow-up
- Which store would you pick for user sessions, a product catalogue and payment records, and why each one?
- What does adding a secondary index cost on writes and storage?
- How would you migrate when the access pattern outgrows the store you chose?
Design a URL shortening service. What considerations would you take into account?
Medium · System Design / Architecture
Approach
- Scope it first. The core is create-short-link and redirect. Custom aliases, expiry and click analytics are optional extras. State the traffic as an assumption: redirects far outnumber creates, so the read path drives the design. For key length, 7 base62 characters give 62^7, about 3.5 trillion codes.
- Compare ways to generate keys. Hashing the long URL and keeping 7 characters can collide, so the code needs a uniqueness check and a retry. A counter encoded in base62 never collides, but its codes are sequential and guessable, and the counter needs coordination. Handing each server a block of IDs from a database sequence or a coordinator avoids a per-request round trip.
- Storage is a point lookup from short code to long URL. A key-value store, or a relational table with the code as primary key, both work. Put a unique constraint on custom aliases, and shard by the short code when one node is no longer enough.
- Read path: put a cache (Redis, LRU) in front of the store and optionally a CDN. Choose the redirect status on purpose. A 301 is permanent and browsers can cache it, so repeat clicks may never reach the service and go uncounted. A 302 sends every click through the service.
- Cover the edge cases. Decide whether the same long URL gets one code or many. Rate-limit link creation and screen URLs for malware. Clean up expired links. Write click events to a queue asynchronously so analytics never adds latency to the redirect.
Follow-up
- How do you guarantee that two servers never hand out the same code?
- How would you count clicks at very high volume without slowing redirects?
- What changes if users can choose custom aliases?
How would you structure a microservices architecture for an e-commerce platform?
Medium · System Design / Architecture
Approach
- Split by business capability, and give each service its own data store: catalogue, search, cart, orders, payments, inventory, accounts, shipping and notifications. Shared tables between services bring back the coupling the split was meant to remove.
- Put an API gateway at the edge for routing, authentication and rate limiting, and optionally a backend-for-frontend per client. Use synchronous REST or gRPC only where the caller needs the answer immediately, such as a price or stock check at checkout. Use events on Kafka or RabbitMQ for fan-out, for example an OrderPlaced event consumed by inventory, notifications and analytics.
- Run checkout as a saga, because there is no ACID transaction across services. The steps are: reserve inventory, charge payment, confirm the order. Each step has a compensating action, such as releasing the reservation if the payment fails. Use idempotency keys on payment calls, and the transactional outbox pattern so the database write and the event publish cannot diverge.
- Separate the read side. Search keeps its own index (for example Elasticsearch), fed by catalogue events, and product pages are cached. Displayed stock can be eventually consistent, but the final reservation must be an atomic conditional decrement (stock > 0) so a flash sale cannot oversell.
- Add the operational layer: per-call timeouts, retries with backoff and jitter, circuit breakers, service discovery, distributed tracing with correlation IDs, and an independent CI/CD pipeline per service.
Follow-up
- The payment succeeds but the order service crashes before recording it. What happens, and how do you recover?
- How do you stop inventory from overselling during a flash sale?
- Which of these services would you keep inside one deployable at the start, and why?
Discuss the trade-offs of using a monolithic vs. microservices architecture.
Medium · System Design / Architecture
Approach
- Monolith strengths: one deployable, in-process calls with no network failure modes, a single database with ACID transactions across modules, and simple local development, debugging and end-to-end testing.
- Monolith costs: any change redeploys everything, scaling is all-or-nothing even when one module is hot, a memory leak or crash in one module takes the whole app down, and module boundaries erode unless something enforces them.
- Microservice strengths: independent deploys and scaling per service, fault isolation, team ownership and a free choice of technology per service. Microservice costs: network latency and partial failures, cross-service consistency through sagas and eventual consistency, plus the operational load of tracing, service discovery and one pipeline per service. Integration testing also gets harder; contract tests help.
- Decide on concrete factors: how many teams are blocked on one deploy train, whether components have very different scaling or reliability needs, and how stable the domain boundaries are. A modular monolith with enforced module boundaries is a common middle step. The strangler fig pattern extracts services one at a time behind a routing layer.
- Name the failure mode to avoid: a distributed monolith, where services share a database or must deploy in lockstep, pays the costs of both architectures and gets the benefits of neither.
Follow-up
- Which module would you extract from a monolith first, and how would you do it without downtime?
- Two modules shared a database transaction. How do you keep them consistent once they are separate services?
- How would you test a change that crosses three service boundaries?
You are given a dataset with missing values; how would you handle it?
Medium · Problem-Solving / Case Studies
Approach
- Quantify before deciding. Compute the missing rate per column and per row (df.isna().sum() in pandas), and look for disguised missing values: empty strings, 'N/A', -1, 0 or out-of-range dates.
- Ask why the values are missing. MCAR (completely at random) makes deletion safe. MAR (depends on observed columns) supports model-based imputation. MNAR (depends on the missing value itself) means the missingness carries signal and needs domain input or an explicit indicator.
- Choose per column. Drop rows when few are affected and the values are MCAR; otherwise deletion biases the sample. Drop a column that is mostly empty and low value. Impute numerics with the median when they are skewed (the mean is sensitive to outliers), and categoricals with the mode or an explicit 'Unknown' level. Forward-fill time series. Use KNN or iterative (MICE-style) imputation when columns are correlated. Add a was-missing indicator column.
- Avoid leakage. Fit the imputation statistics on the training split only and apply them to validation and test, for example with scikit-learn's SimpleImputer inside a Pipeline. Note that mean imputation shrinks variance and weakens correlations.
- Cover the engineering side. Validate the schema at ingestion and decide whether to reject, default or flag each field. Some models handle NaN natively (XGBoost, LightGBM, scikit-learn's HistGradientBoosting), so imputation is not always needed.
Follow-up
- The missing values are concentrated in one customer segment. Does that change your approach?
- How would you check that imputation did not distort the downstream result?
- What happens at serving time when a field arrives missing that was always present in training?
Design an algorithm to find the longest substring without repeating characters.
Medium · Coding / Algorithms
Approach
- Start from brute force. Checking every substring for uniqueness is O(n^3), or O(n^2) if a set grows as each substring extends. That motivates a sliding window.
- Optimal version: keep a left pointer and a map from each character to its last index. For each right index with character c: if c is in the map and map[c] >= left, set left = map[c] + 1. Then set map[c] = right and best = max(best, right - left + 1). This is O(n) time and O(min(n, alphabet size)) space.
- Explain why the map[c] >= left check matters. On 'abba', the second 'a' was last seen at index 0, which is already outside the window, so left must not jump back. Without the check the answer comes out as 3 instead of 2.
- A set-based window also works: advance left one step at a time, removing characters until the duplicate is gone. It stays O(n) amortised because each character enters and leaves the set at most once.
- Edge cases: the empty string returns 0; 'bbbb' returns 1; a string with no repeats returns n. For a fixed ASCII alphabet, a 128-entry array can replace the map. To return the substring itself, record the start index whenever best improves.
Follow-up
- Return the longest substring itself, not just its length.
- Find the longest substring with at most k distinct characters.
- How does the solution change if the input is a stream you can only read once?
How would you approach debugging a performance issue in a large-scale application?
Medium · Technical / Domain Questions
Approach
- Pin down the symptom in numbers before forming any theory: which endpoint or job, p50 versus p99 latency, throughput, error rate, and when it started. Line that start time up against deploys, config changes, traffic shifts and data growth.
- Narrow it to a component. Use RED metrics (rate, errors, duration) per service and the USE method (utilisation, saturation, errors) per resource: CPU, memory, disk I/O, network, and thread and connection pools. Use distributed tracing to find the span that dominates slow requests.
- Profile the component you found. Use a sampling profiler and flame graph for CPU (py-spy, async-profiler, perf). For the database, check the slow-query log and EXPLAIN ANALYZE, and look for N+1 query loops, missing indexes and lock waits. Also look at GC pause logs and pool exhaustion, where requests queue for a connection.
- Change one thing at a time against a reproducible load (a load test or a replay in staging), and compare to the baseline at the same percentile. Averages hide tail latency, so judge the fix on p95/p99. Roll back first if a recent deploy is the clear trigger.
- Close the loop: add the metric or alert that would have caught it earlier, plus a benchmark or regression test. Also say whether the trigger, such as traffic growth, differs from the root cause, such as a query that scales linearly with table size.
Follow-up
- p99 doubled while p50 stayed flat. What does that point to?
- A query is slow in production but fast when you run it by hand. What could explain the difference?
- How would you profile a live production service without adding much overhead?
Suggested practice checklist
The plan starts with the loop and coding fundamentals, then moves through performance, storage, design and behavioral stories, ending with a full mock run.
Day 1 — Map the four stages and set your language
- List the four reported stages (screening call, technical assessments, coding challenges, behavioral interviews) and write which question types you will prepare for each.
- Choose Python, Java or C++ for coding and write a short project walkthrough you could give on a screening call, naming language, datastore and result.
- Write down which nice-to-have areas from the role (machine learning, data processing frameworks, CI/CD, React or Angular) you have used.
Deliverable: A one-page loop map, a chosen coding language, and a written project walkthrough for the screening call.
Day 2 — Coding questions: list, search and window
- Write reversing a linked list iteratively and recursively in your chosen language, then test empty, one-node and two-node lists.
- Write binary search with the lower and upper bounds as an invariant, then change it to return the first occurrence of a duplicate.
- Write the longest substring without repeating characters with a sliding window and a map of last-seen indexes, and state its complexity.
Deliverable: Three tested solutions, each with a written complexity line and a list of edge cases checked.
Day 3 — Optimizing algorithms and finding slow paths
- Take a quadratic solution of your own, such as nested loops for matching two lists, and rewrite it using a hash map or sorting; also write merging two sorted arrays with two pointers.
- Write the debugging sequence you would use for a slow large application: reproduce, measure, find the dominant cost, change one thing, re-measure.
- Answer aloud how you would find out whether a slowdown is CPU, memory, I/O or a database query.
Deliverable: A before-and-after complexity note for the rewritten algorithm and a written debugging checklist you can say in your own words.
Day 4 — Data storage, queries and missing data
- Compare a relational database, a key-value store and a document store for two workloads, writing the access pattern and the deciding trade-off for each.
- Write how you would handle a dataset with missing values: measure how much and why it is missing, choose delete, impute or flag, and note the risk of each.
Deliverable: A storage comparison table with two workloads, solved SQL practice questions, and a short written plan for missing values.
Day 5 — System design: shortener, e-commerce and monolith choices
- Outline a URL shortening service with ID generation, storage, the redirect read path, caching and collision handling.
- Outline microservices for an e-commerce platform, choosing service boundaries and saying which data each service owns.
- Write a short answer on monolith versus microservices that names team size, deployment, data consistency and network cost, and practise saying it aloud.
Deliverable: Two design outlines and one spoken trade-off answer, each with at least two named decisions and their alternatives.
Day 6 — Programming paradigms and behavioral stories
- Explain object-oriented versus functional programming with a code example in your chosen language, covering state, mutation and when each fits.
- Write five stories from your own work for conflict, prioritizing under a deadline, leading a project, an unconventional technical fix and working with a non-technical stakeholder.
- Reduce each story to a two-sentence setup, the decision you made, the alternative and one concrete result.
Deliverable: A short worked paradigm comparison and five story outlines, each naming a personal decision and a result.
Day 7 — Full mock loop
- Run a mock coding session on one of the reported problems, speaking the brute force, the improvement and the edge cases while you write.
- Answer one storage question and one design question aloud, then a debugging question, giving a decision and an alternative in each.
- Tell two of your stories aloud and trim anything that does not show your own decision.
Deliverable: A recording or written self-review listing what to fix before the real stages.
Stories for the product and tech-lead rounds
Candidates describe the final stage as behavioral interviews run by product managers and technical leaders, about working style and fit. The reported prompts are about conflict, deadlines, leading work, unconventional technical thinking and collaboration with non-technical stakeholders. Prepare stories that show a personal decision, an alternative and a result.
- How do you handle conflicting ideas in a team?
- Describe a project where you had to collaborate closely with non-technical stakeholders.
Can you describe a time when you had to resolve a conflict within your team?
Medium · Behavioral / Leadership
Approach
- Choose a conflict with a real substance, such as two designs for the same service or a disagreement over scope with a product manager, and state it in two sentences with both positions described fairly.
- Show your own moves in order: you spoke to the person directly first, found the shared goal or constraint, then turned the argument from opinion into something checkable such as a small benchmark, a spike or a written comparison of options.
- Say what the other side got right and what you changed because of it; a story where you were simply correct sounds like a story with no learning.
- Finish with the outcome and the working relationship afterwards, for example how you collaborated on the next task, and name the result with a concrete fact from your own project.
- The mistake that sinks it is a story where you escalated to a manager without trying to resolve it, or one told entirely with 'we' so no personal action is visible.
Follow-up
- What would you have done if the evidence had not settled the disagreement?
- What did the other person's view change about your own approach?
- How would that conversation differ if the other person were a product manager rather than an engineer?
How do you prioritize tasks when faced with tight deadlines?
Medium · Behavioral / Leadership
Approach
- Answer with one real deadline squeeze rather than a framework alone: list what was due, who depended on each item and what each item was worth.
- Rank by impact and by cost, and be explicit about the order you chose and why, for example fixing a blocker for another team before a polish task.
- Name what you cut, deferred or reduced in scope, and who agreed to it; reducing scope in the open is stronger than silently working longer hours.
- Describe how you communicated the plan to the product manager or stakeholders with options and a recommendation, and how you re-planned when new work arrived.
- The mistake is a generic answer such as 'I make a list and work hard' with no example, no trade-off and no mention of what was dropped.
Follow-up
- Which item did you drop, and how did the people affected react?
- What would you do if two stakeholders each insisted their task came first?
- How did you notice early that the deadline was at risk?
Tell me about a project where you took the lead and the outcome.
Medium · Behavioral / Leadership
Approach
- Choose a project where you made decisions and set direction, even without a manager title: define the goal, the scope and what success meant before describing the work.
- Explain how you broke the work into pieces, who took which part, how you tracked progress and how you unblocked others, using 'I' for your own decisions and 'the team' only for shared effort.
- Include one thing that went wrong and the correction you made; a lead who never met a problem sounds unconvincing.
- State the outcome with something concrete from your own project, such as a shipped feature, a latency change or a reduced error rate, and say how you know the result came from the project.
- The mistake is listing activities as a participant, or claiming results you cannot support when asked for the numbers.
Follow-up
- How did you handle a teammate who was falling behind?
- What would you change about how you planned the project if you ran it again?
- How did you keep product managers and other stakeholders informed as scope changed?
Discuss a time when you had to think outside the box to solve a technical problem.
Medium · Problem-Solving / Case Studies
Approach
- Start with the constraint that made the standard approach fail, with a number if you have one, such as a query that missed its latency target or a job that did not fit in memory.
- List the conventional options you considered and why each fell short, then explain the unconventional idea and what made it plausible.
- Describe how you validated it before trusting it: a prototype, a benchmark against realistic data, a feature flag or a limited rollout.
- Be honest about the cost, such as extra complexity or maintenance, and say whether it stayed in production or was later replaced.
- The mistake is presenting a clever hack with no evidence that it worked, or choosing a problem so small that no creativity was needed.
Follow-up
- Why did the standard approach not work in your case?
- What was the riskiest part of your solution and how did you contain it?
- Would you take the same approach again, and did it hold up in production?