cswork / libraryOpen practice workspace

Interview guides · Cohesity

Cohesity Software Engineer Interview Guide

Cohesity Software Engineer interview guide: five reported rounds, coding, system design, OS and storage questions, a 7-day plan and behavioral prep.

The 60-second brief

Cohesity builds data management software. Candidate reports place the work around data protection, backup, recovery and analytics, and name deduplication, tiering and hybrid cloud integration as example problem areas. For a Software Engineer, the reported day-to-day is building features from user requirements, reviewing teammates' code, joining system design discussions, fixing defects, and working with product managers and UX designers inside agile sprints. The languages listed for the role are Java, Python and C++, with cloud technologies, distributed systems and data management concepts listed as nice-to-have.

This guide is for engineers preparing for a Software Engineer loop at Cohesity. It covers the five reported rounds, the 25 questions candidates report (core CS and OS theory, coding, system design, production debugging and behavioral prompts), and a seven-day schedule that turns them into practice sessions. Reports do not tie individual questions to individual rounds, so prepare every question group for every technical stage.

Candidates report five rounds spread over roughly 4-6 weeks: Phone Screening, Coding Assessment, Technical Interviews, Behavioral Interviews and Final Assessment.

Focus areas

  • DSA (Data Structures and Algorithms)
  • System Design (High-Level Design)
  • Python

Overview

The reported question set has two halves. One half is fundamentals: process versus thread, the principles of object-oriented programming, garbage collection in Java, the CAP theorem and the software development lifecycle. The other half is hands-on: five short coding problems (longest substring without repeating characters, binary search, reversing a linked list, merging two sorted arrays, two-sum) and five design prompts (a URL shortener, a file storage system, data replication in a distributed system, a real-time chat application, a load balancer). Candidates also list DSA, system design, Python, operating systems fundamentals and data or file storage systems as the topics they met.

Because the product area is data management, the storage-flavoured prompts deserve extra time: how a file storage system lays out data, how replication keeps copies in step, and what the CAP theorem forces you to give up during a partition. Those three connect, so learn them as one story rather than three separate answers. The nice-to-have list (distributed systems, cloud technologies, data management concepts) points the same way.

The coding questions are short and well known, so the differentiator is delivery: state the brute force, upgrade it, write complete code in the language you choose (Java, Python or C++ are the listed options), and test it on empty input, duplicates and boundaries. Spend the remaining time on a story bank for the behavioral prompts and on the production-minded questions (debugging a complex issue in production, improving an existing product's performance, evaluating a new technology), which sit between technical and behavioral.

The Cohesity interview process

Candidates describe five stages over roughly 4-6 weeks. Stage descriptions are brief, so the preparation below is built around the question types each stage's name and description imply.

Phone Screening

Candidates describe the Phone Screening as an initial conversation about your background and fit for the role. Reports give no format details, so treat it as the stage where your CV and motivation have to hold up and where you can ask the recruiter what the later stages involve.

What to demonstrate

  • Your background and how it maps to a Software Engineer role
  • How clearly you summarise past projects
  • Motivation for the role, a reported behavioral prompt

How to prepare

  • Rehearse a two-minute walkthrough of your CV that names the languages you have shipped in (Java, Python or C++ are the ones listed for the role).
  • Choose one project you can discuss in depth, including data volumes, concurrency or storage choices if you have them.
  • Write a short, specific answer to what motivates you to perform at your best.
  • Ask the recruiter what format the Coding Assessment takes, since reports do not describe it.

Coding Assessment

Reports describe the Coding Assessment as coding challenges that test problem-solving and algorithmic thinking; the platform and length are not described. The reported coding questions are longest substring without repeating characters, binary search, reversing a linked list, merging two sorted arrays and two-sum.

What to demonstrate

  • Correct, complete code in one language
  • Choice of data structure and the resulting time and space complexity
  • Handling of edge cases such as empty input and duplicates

How to prepare

  • Solve the five reported coding problems from a blank file in your chosen language, with no autocomplete.
  • For each, say the brute-force complexity first, then the improved approach: a hash map for two-sum, a sliding window with a last-seen index for the substring problem.
  • Write binary search with a loop invariant and check it on one-element, empty and not-found cases.
  • Run each solution on edge cases before you say it is done.

Technical Interviews

Candidates describe the Technical Interviews as in-person or virtual sessions covering coding and system design. This is the stage for the fundamentals (process versus thread, Java garbage collection, CAP, object-oriented principles) and for the design prompts: URL shortener, file storage system, replication, real-time chat and load balancer.

What to demonstrate

  • Coding fluency in a live setting
  • Ability to structure a system design and defend trade-offs
  • Depth on fundamentals such as threads, memory management and consistency

How to prepare

  • Practise each design prompt aloud with a fixed order: requirements, API, data model, components, scaling, failure handling.
  • Prepare a file-storage answer that covers chunking, metadata versus data, replication and what happens when a node is lost.
  • Write one-page answers for process versus thread, generational garbage collection and the CAP theorem, each with a concrete example.
  • Practise talking while you code or draw, since the stage can be in person or remote.

Behavioral Interviews

Behavioral Interviews are described by candidates as a look at past experience and culture fit. Six behavioral prompts are reported: a challenging project, prioritisation across projects, team conflict, motivation, remote communication and a quality-versus-speed trade-off.

What to demonstrate

  • Your own contribution in past projects
  • How you handle disagreement and competing priorities
  • Communication habits, including in a remote setting

How to prepare

  • Build a story bank of four to five real episodes and map each to at least two of the six reported prompts.
  • Write each story with situation, your decision, the alternatives you rejected, the measured result and what you would change.
  • Say I for your own actions and name teammates for theirs, so your role is visible.
  • Prepare a concrete example for the quality-versus-speed trade-off and one for adapting to stakeholder feedback.

Final Assessment

Candidates describe the Final Assessment as a closing evaluation of overall fit and readiness; the content is not detailed in reports. Since it follows the technical and behavioral stages, keep both kinds of preparation fresh rather than assuming a particular format.

What to demonstrate

  • Overall fit and readiness for the role as a closing judgment
  • Consistency between your technical and behavioral answers across the loop

How to prepare

  • Re-solve two of the reported coding problems and re-sketch one design so earlier work stays sharp.
  • Reread your story bank and make sure the numbers and roles in each story still match your CV.
  • Prepare questions about the team's day-to-day work: code review, sprint planning and the systems you would own.
  • Ask the recruiter what the final stage contains and who you will meet.

Where candidates lose points

Stopping at the brute force on two-sum, longest substring or the sorted-array merge

Say the brute-force complexity, then name the improvement before coding: a hash map for two-sum, a sliding window with a last-seen index for the substring problem, two pointers for the merge. Finish with a test on empty input and duplicates.

Reciting definitions for process versus thread or Java garbage collection with no mechanism

Anchor each answer in what the runtime does: separate address spaces versus shared heap and context-switch cost; reachability from roots, generations and why short-lived objects are cheap to collect. Add one failure mode each, such as a race condition or a long pause.

Designing the file storage or replication question as a generic web app

Start from the data: how files are chunked, where metadata lives, how many copies exist and what happens when a node dies. Tie the consistency choice back to the CAP theorem rather than listing a database and a cache.

Telling behavioral stories in the first-person plural so your own decisions disappear

Write each story with an I-statement for every decision you made, the alternatives you rejected and a measured result, then rehearse out loud so the stories are not read from notes.

Preparing only for coding because the process lists a Coding Assessment

The loop also has Technical Interviews that include system design, Behavioral Interviews and a Final Assessment. Reserve plan days for design, fundamentals and your six stories, not just algorithm practice.

Questions & practice

Explain the differences between a process and a thread.

Medium · Technical / Domain Questions

Approach

  1. A process owns its own virtual address space, file descriptor table and other OS resources. Threads inside one process share the heap, globals, code and open files, but each thread has its own stack, registers, program counter and thread-local storage.
  2. Cost: a new process needs its own page tables (Linux fork uses copy-on-write, so pages are copied only when written). Switching between processes changes the address space and can flush TLB entries; switching between threads of one process keeps it. On Linux both are tasks created by clone() with different sharing flags.
  3. Isolation versus communication: a segfault in one thread takes down the whole process, while a crashed child process leaves its parent running. Processes talk through IPC (pipes, sockets, shared memory, message queues); threads share memory directly, so they need mutexes, condition variables or atomics to avoid data races.
  4. Language angle: in the default CPython build the GIL stops CPU-bound threads from running bytecode in parallel, so I use multiprocessing for CPU-bound work and threads or asyncio for I/O-bound work. Java platform threads map one-to-one onto OS threads, and Java 21 adds virtual threads scheduled by the JVM.

Follow-up

  • What happens to the other threads when a multithreaded process calls fork(), and why can that deadlock the child?
  • For a service that does a lot of disk and network I/O, would you pick a pool of processes, threads or an event loop, and why?
  • What exactly is saved and restored during a context switch?

How does garbage collection work in Java?

Medium · Technical / Domain Questions

Approach

  1. Java's tracing collectors find live objects by reachability from GC roots: local variables on thread stacks, static fields, JNI references and active monitors. Anything unreachable is garbage, so reference cycles are collected too; the JVM does not use reference counting.
  2. Generational layout: new objects go into Eden. A minor GC copies the survivors between two survivor spaces and promotes objects that pass the tenuring threshold to the old generation. Card tables and write barriers record old-to-young pointers, so a minor GC does not scan the whole old generation.
  3. Algorithms and collectors: mark-sweep, mark-compact and copying, each with stop-the-world phases. Serial and Parallel aim for throughput. G1, the default since JDK 9, is region-based and works toward a pause goal (-XX:MaxGCPauseMillis). ZGC and Shenandoah do most of their work concurrently for very short pauses. CMS was removed in JDK 14.
  4. Leaks still happen when references stay reachable: static collections, unbounded caches, listeners that are never deregistered, ThreadLocals in pooled threads. Diagnose with GC logs (-Xlog:gc*), a heap dump from jcmd or jmap, and the dominator tree in a heap analyser.
  5. Mention reference types: soft references are cleared under memory pressure, weak references at the next GC once only weakly reachable, and phantom references support cleanup through Cleaner. finalize() is deprecated.

Follow-up

  • A service shows a growing old generation and repeated full GCs. What do you check, in what order?
  • When would you choose ZGC over G1, and what do you give up?
  • Why might lowering the heap size make GC pauses better or worse?

Write a function to find the longest substring without repeating characters.

Medium · Coding / Algorithms

Approach

  1. Use a sliding window with two pointers and a map from character to last-seen index. For each r: if s[r] was last seen at an index >= left, move left to last[s[r]] + 1. Record last[s[r]] = r and update best = max(best, r - left + 1).
  2. Complexity: O(n) time, because each pointer only moves forward. Space is O(min(n, alphabet size)); for ASCII input a 128-entry array can replace the dict.
  3. Mention the baselines: checking every substring is O(n^3), and growing a set from each start is O(n^2). Both rule themselves out for long strings.
  4. Common bug: jumping left backwards when the repeated character's last index is before the current window. The check last[s[r]] >= left (or left = max(left, last[s[r]] + 1)) prevents it.
  5. Edge cases: empty string returns 0, all the same character returns 1, all distinct returns n. To return the substring itself, store the start index whenever best improves.

Follow-up

  • 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?
  • What changes for Unicode input with combining characters or surrogate pairs?

Implement a binary search algorithm.

Medium · Coding / Algorithms

Approach

  1. Write the iterative version on a closed interval: lo = 0, hi = n - 1, while lo <= hi, mid = lo + (hi - lo) // 2. Return mid on a match; otherwise set lo = mid + 1 or hi = mid - 1. Return -1 if not found. O(log n) time, O(1) space.
  2. Explain why mid is lo + (hi - lo) / 2: in Java or C++, (lo + hi) / 2 can overflow int on very large arrays. Python integers do not overflow, but writing it this way shows the habit.
  3. Write the lower-bound variant on a half-open interval: lo = 0, hi = n, while lo < hi, if a[mid] < target then lo = mid + 1 else hi = mid. This returns the first index >= target and handles duplicates. It matches Python's bisect_left; Java's Arrays.binarySearch returns -(insertion point) - 1 when the key is missing.
  4. Edge cases: empty array, a single element, a target below or above every value, and duplicates when the first or last occurrence is required. The infinite-loop trap is setting lo = mid while mid rounds down; with that update, round mid up.
  5. Generalise: binary search works on any monotonic predicate, so it can also search an answer space (for example, the smallest capacity that ships everything within D days).

Follow-up

  • Search a sorted array that has been rotated at an unknown pivot.
  • Return the first and last positions of a target in an array with duplicates.
  • How would you search a sorted collection whose size you do not know?

How would you reverse a linked list?

Medium · Coding / Algorithms

Approach

  1. Iterative, three pointers: prev = None, curr = head. While curr: nxt = curr.next; curr.next = prev; prev = curr; curr = nxt. Return prev as the new head. O(n) time, O(1) extra space.
  2. Recursive version: reverse the rest, then set head.next.next = head and head.next = None, and return the new head from the base case. It uses O(n) stack, so a long list can hit Python's default recursion limit of 1000 or overflow the stack in Java.
  3. Usual mistakes: overwriting curr.next before saving it, which loses the rest of the list, and returning the old head instead of prev.
  4. Trace the edge cases out loud: an empty list, a single node and two nodes. For a doubly linked list, swap prev and next on every node and 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, leaving a short final group as it is.
  • Check whether a linked list is a palindrome using O(1) extra space.

Solve the "two-sum" problem.

Medium · Coding / Algorithms

Approach

  1. Clarify first: return indices or values, exactly one answer or possibly none, can the same element be used twice, is the input sorted?
  2. One-pass hash map from value to index: for i, x in enumerate(nums), if target - x is in seen, return [seen[target - x], i]; otherwise store seen[x] = i. O(n) time and O(n) space. Checking before inserting stops an element pairing with itself while still handling [3, 3] with target 6.
  3. Baselines and alternatives: the nested-loop brute force is O(n^2). Sorting plus two pointers is O(n log n) with O(1) extra space if only values are needed; for indices, sort (value, index) pairs.
  4. Edge cases: negative numbers, duplicates, no valid pair, and in Java or C++ the possibility that a + b overflows int; compare a against target - b or use long.

Follow-up

  • Return all unique pairs that sum to the target, without duplicates.
  • Extend it to 3Sum. What complexity can you reach?
  • Design a class with add(number) and find(value) for a stream. Which operation do you make cheap?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

What are the principles of object-oriented programming?

Medium · Technical / Domain Questions

Approach

  1. Name the four principles and give each a code-level meaning. Encapsulation hides state behind methods that keep invariants true. Abstraction exposes what an object does, not how. Inheritance reuses and specialises behaviour. Polymorphism lets one call site work on many types.
  2. Ground the answer in one example, such as a StorageBackend interface with write/read/delete and two implementations (local disk, remote object store). The caller codes against the interface, and dynamic dispatch picks the implementation at runtime.
  3. Separate the kinds of polymorphism: subtype (overriding, resolved through a vtable in C++/Java), ad hoc (method overloading, resolved at compile time) and parametric (generics/templates).
  4. Add the judgement: prefer composition to deep inheritance, because subclasses couple tightly to base-class internals (the fragile base class problem). Respect Liskov substitution, and mention SOLID as the guidelines built on these ideas.
  5. Language differences: Java has single class inheritance plus multiple interfaces. C++ allows multiple inheritance and needs a virtual destructor on polymorphic bases. Python relies on duck typing and abc.ABC, and resolves methods through the C3 MRO.

Follow-up

  • Give an example that violates Liskov substitution, such as Square extending Rectangle. What breaks?
  • When would you replace an inheritance hierarchy with composition or the strategy pattern?
  • How does a virtual method call work under the hood in C++ or Java?

Describe the CAP theorem and its implications.

Medium · Technical / Domain Questions

Approach

  1. State it precisely. When a network partition happens, a replicated system must choose between consistency (linearizability: every read sees the latest acknowledged write) and availability (every request to a non-failed node gets a non-error response). Real networks partition, so in practice the choice is C or A during a partition, not 'pick any two of three'.
  2. CP example: etcd or ZooKeeper with majority quorums. In a 5-node cluster split 3/2, the 3-node side keeps a leader and commits writes; the 2-node side rejects them. AP example: Dynamo-style stores such as Cassandra accept writes on both sides and reconcile later.
  3. Reconciliation in AP systems: last-write-wins (simple, but loses concurrent updates and depends on clock skew), version vectors that expose conflicts to the application, or CRDTs that merge deterministically. Tunable quorums with R + W > N make read and write sets overlap.
  4. Extend it with PACELC: even without a partition, a system trades latency against consistency, which often drives the design more than the rare partition does.
  5. Choose per operation, not per system: lock services, metadata and balances need CP behaviour, while counters, carts and presence can tolerate AP. Point out that the C in CAP is not the C in ACID.

Follow-up

  • Walk through what clients on each side of a partition see in a 5-node Raft cluster.
  • Does R + W > N give you linearizability? What can still go wrong?
  • How would you resolve two conflicting updates to the same record in an AP store?

Can you walk us through the software development lifecycle?

Medium · Technical / Domain Questions

Approach

  1. Name the phases and the artifact each one produces: requirements (a spec with acceptance criteria), design (a design doc and review), implementation (code plus code review), testing (unit, integration and end-to-end suites), release (a build and rollout plan) and operations (monitoring, incidents, maintenance).
  2. Contrast the models: waterfall runs each phase once in sequence, while iterative models such as Scrum sprints or Kanban repeat the cycle in small increments. Say which one your teams used and what that changed in practice.
  3. Walk one real feature through it in first person: 'I wrote the design doc, got review on the API, built it behind a feature flag, CI ran the unit and integration tests, I rolled it out to a canary, watched error rates, then ramped up.'
  4. Cover the quality gates: the testing pyramid, static analysis, security review, and a rollback plan written before release. For software that customers install and upgrade themselves, add backward-compatibility and upgrade-path testing.

Follow-up

  • How did your team decide a feature was done?
  • A serious bug reaches production after release. What does the hotfix path look like?
  • How do you keep the CI suite fast as the codebase grows?

Design an algorithm for merging two sorted arrays.

Medium · Coding / Algorithms

Approach

  1. Two pointers i and j: append the smaller of a[i] and b[j], advance that pointer, then copy whatever remains of the other array. O(m + n) time and O(m + n) output space. Taking from a on ties (<=) keeps the merge stable.
  2. In-place variant, where the first array has spare capacity m + n: fill from the back with k = m + n - 1, comparing a[i] and b[j] from their ends. Nothing unread gets overwritten, extra space is O(1), and once b is used up the rest of a is already in place.
  3. Edge cases: either array empty, duplicates across both, every element of one array smaller than the other, negative values.
  4. Scale it up: merging k sorted arrays uses a min-heap of (value, array index, element index) in O(N log k). Python's heapq.merge does this lazily. For data larger than memory, an external merge sort streams sorted runs from disk through the same k-way merge.

Follow-up

  • Merge k sorted linked lists. What is the complexity, and why use a heap rather than merging pairwise in sequence?
  • The two arrays are files too large for memory. How do you merge them?
  • Find the median of two sorted arrays in O(log(min(m, n))) time.

Design a URL shortening service. What components would you include?

Medium · System Design / Architecture

Approach

  1. Scope the requirements: create a short link, redirect, optional custom alias and expiry, click analytics. The workload is read-heavy, so estimate the read/write ratio, the storage per link and the code length needed: 62^7 is about 3.5 trillion codes with 7 base62 characters.
  2. ID generation: base62-encode a unique counter, with ranges handed out to each app server so no single counter is a bottleneck, or hash the long URL and truncate it, checking for collisions. Sequential codes can be guessed; shuffle the bits or add randomness if that matters.
  3. Storage and reads: a key-value store keyed by short code holding the long URL, owner, created_at and expiry, sharded by code. A cache in front absorbs hot links. A 301 redirect is cached by browsers and cuts load but hides repeat clicks from analytics; a 302 sends every click to the service.
  4. Components: a load balancer, stateless API servers, the ID allocator, the key-value store, the cache, and a queue feeding an asynchronous analytics pipeline so redirects never wait on analytics writes.
  5. Edge cases: malicious URLs (scan them on create), rate limiting per user, expired codes returning 404 or 410, and whether one long URL submitted twice gets the same code.

Follow-up

  • How do you run this in several regions without handing out duplicate codes?
  • One link goes viral. What gets hot, and how do you protect the store?
  • How would you delete expired links at scale?

How would you structure a file storage system?

Medium · System Design / Architecture

Approach

  1. Fix scope first: a single-node filesystem (inodes, directory entries, block allocation, a journal for crash consistency) or a distributed file or object store. Then choose one and design it, usually the distributed one.
  2. Split metadata from data. A metadata service holds the namespace, permissions and the file-to-chunk map; chunk servers store fixed-size chunks. Clients ask metadata for placement and then read or write chunks directly, so file bytes never pass through the metadata tier.
  3. Durability: three replicas across failure domains (3x storage) or erasure coding. For example, Reed-Solomon 6+3 survives any three lost fragments at 1.5x overhead but makes reads and repairs costlier. Per-chunk checksums and background scrubbing catch silent corruption.
  4. Deduplication: address chunks by a content hash such as SHA-256 so identical chunks are stored once. Content-defined chunking with a rolling hash (Rabin-style) keeps boundaries stable when bytes are inserted. Shared chunks need reference counts or mark-and-sweep garbage collection.
  5. Snapshots and tiering: copy-on-write metadata gives cheap point-in-time snapshots. Moving cold chunks to cheaper media or cloud object storage keeps hot data on fast disks; state the access-latency cost of reading a cold file.

Follow-up

  • The metadata service becomes the bottleneck. How do you partition the namespace?
  • A chunk server dies halfway through a replicated write. What does the client see, and how is the chunk repaired?
  • How do you garbage-collect deduplicated chunks safely while new backups are still writing references to them?

Discuss how you would handle data replication in a distributed system.

Medium · System Design / Architecture

Approach

  1. Pick a topology: single-leader (one node takes writes and ships its log to followers), multi-leader (writes in several regions, so conflicts are possible) or leaderless (Dynamo-style quorums with N, R and W).
  2. Choose the acknowledgement mode. Synchronous replication does not lose acknowledged writes, but it adds latency and stalls when a replica is slow. Asynchronous replication is fast but can lose recent writes on failover. Semi-synchronous (one synchronous follower) sits between the two.
  3. Handle replication lag explicitly: read-your-writes (route a user's reads to the leader for a short time after they write), monotonic reads (pin a session to one replica), and bounded-staleness reads where the product allows them.
  4. Failover without split brain: elect leaders with a consensus protocol (Raft or Paxos, majority quorum, so 3 nodes tolerate 1 failure and 5 tolerate 2), and fence the old leader with epoch numbers or fencing tokens so its late writes are rejected.
  5. Repair and conflicts: anti-entropy with Merkle trees, read repair and hinted handoff for leaderless stores. Resolve conflicts with last-write-wins (which loses data), version vectors or CRDTs, and say which one fits the data.

Follow-up

  • A follower has been offline for a day. How does it catch up without overloading the leader?
  • What changes when the replicas are in different regions with high round-trip latency?
  • How would you detect and recover from a split brain that has already happened?

What considerations would you make for building a real-time chat application?

Medium · System Design / Architecture

Approach

  1. Requirements: 1:1 and group chat, delivery and read receipts, presence, message history, and push notifications for offline users. Estimate concurrent connections and messages per second.
  2. Transport: persistent WebSocket connections to a fleet of gateway servers (long polling or SSE as fallbacks). A connection registry maps each user to their gateway, and gateways exchange messages through pub/sub or direct RPC.
  3. Message path: the client attaches a client-generated message ID for idempotent retries. The server assigns a per-conversation sequence number for ordering, persists the message, acknowledges the sender and then fans it out. Delivery is at-least-once, and clients dedupe by message ID.
  4. Storage: partition messages by conversation ID, ordered by sequence number; a wide-column store fits that access pattern. History loads with cursor pagination. On reconnect, the client asks for everything after the last sequence number it saw.
  5. Fan-out and presence: small groups fan out on write to each member, while very large channels are better read on demand. Presence uses heartbeats with a TTL and throttled broadcasts, because presence updates can outnumber messages.

Follow-up

  • How do you keep message order consistent for a user on three devices at once?
  • A gateway server crashes. What happens to its connections and to messages in flight?
  • How does end-to-end encryption change search, history sync and moderation?

Explain how you would design a load balancer.

Medium · System Design / Architecture

Approach

  1. Choose the layer: an L4 balancer forwards TCP/UDP by address and port without reading payloads, so it is fast and protocol-agnostic. An L7 balancer parses HTTP, can route by path or header, terminate TLS, retry and add headers, at higher CPU cost.
  2. Algorithms: round robin, weighted round robin, least connections, least response time, power-of-two random choices (cheap and close to least-loaded), and consistent hashing when the same key should reach the same backend, for example for cache locality.
  3. Health and lifecycle: active health checks plus passive ejection on errors, slow-start for new backends, and connection draining during deploys so in-flight requests finish.
  4. The balancer cannot be a single point of failure: run an active-passive pair with a floating IP (VRRP) or many nodes behind ECMP or anycast, with DNS-based global balancing across regions.
  5. Trade-offs: sticky sessions simplify stateful backends but skew load and complicate failover. Long-lived connections such as WebSockets balance poorly with round robin, so least connections suits them better.

Follow-up

  • How many keys move when you add a backend under consistent hashing, compared with hash mod N?
  • The balancer itself is saturated. How do you scale it out?
  • How do you deploy new backends without dropping long-lived WebSocket connections?

If given a new technology to implement, how would you evaluate its fit for your project?

Medium · Problem-Solving / Case Studies

Approach

  1. Start from the problem, not the tool: write the requirements and constraints the technology must meet (functional fit, latency or throughput targets, scale, security and compliance, licence, cost) before looking at features.
  2. Compare it against real alternatives, including extending what the team already runs. Score each against the same criteria, plus maturity, community and vendor support, and how well it integrates with the existing stack.
  3. Run a time-boxed proof of concept on a realistic workload and data volume, with numbers: benchmark results, failure tests (kill a node, fill a disk) and the effort to operate it on call.
  4. Weigh adoption risk: lock-in, the migration and rollback path, the team's learning curve, and who owns upgrades. Adopt it in stages behind an interface so it can be swapped out.
  5. Record the decision in a short decision record with the trade-offs, and tell one real example in first person: 'I benchmarked X against what the team had, found Y, and recommended Z because ...'.

Follow-up

  • Tell me about a technology you evaluated and rejected. What decided it?
  • How would you persuade a team that is attached to the current tool?
  • What would you measure in the proof of concept, and what result would change your mind?

Present a scenario where you had to adapt your plans due to stakeholder feedback.

Medium · Problem-Solving / Case Studies

Approach

  1. Structure it as situation, original plan, feedback, re-plan, result. Open with the original plan and the reasoning behind it, so the change reads as a decision rather than a reversal.
  2. State the feedback concretely: who gave it, what they needed and why, for example a support team that needed an export format, or a product manager who moved a deadline.
  3. Show how you evaluated it instead of simply complying: what you checked (usage data, cost, risk to the schedule), the options you laid out, and what you kept from the original plan.
  4. Explain how you communicated the new plan: the revised scope and timeline, who you told, and how you handled what was dropped. End with a measurable outcome and what you would do differently.
  5. The mistake that sinks this answer is blaming the stakeholder or describing the change as something that happened to you. The story has to show your own judgement.

Follow-up

  • What did you push back on, and how did that conversation go?
  • How did the change affect the schedule, and who did you have to tell?
  • What would you set up earlier next time so the feedback arrived sooner?

What steps would you take to improve an existing product's performance?

Medium · Problem-Solving / Case Studies

Approach

  1. Define performance first: which metric (p50 and p99 latency, throughput, startup time, cost per request), for which user path, and the target. Capture a baseline under a reproducible load before changing anything.
  2. Measure to find the bottleneck: end-to-end tracing to locate the tier, then a profiler or flame graph for CPU, slow-query logs and EXPLAIN for the database, and allocation and GC profiles for memory. By Amdahl's law, speeding up a part that takes 5% of the time cannot gain more than 5%.
  3. Fix in order of payoff: algorithmic complexity, N+1 queries and missing indexes, batching and pagination, caching with a clear invalidation rule, async or parallel I/O, connection pooling, and payload size (compression, CDN for static assets).
  4. Validate each change on its own: rerun the same load test, roll out behind a canary or flag, watch the tail as well as the mean, and add a performance check to CI to catch regressions.
  5. Name the trade-offs: caches add staleness and memory cost, denormalisation complicates writes, and more replicas add cost. Sometimes scaling the hardware is cheaper than engineering time, and you should say when.

Follow-up

  • The p50 is fine but the p99 is poor. What are the likely causes?
  • How do you decide between optimising code and adding hardware?
  • How would you stop the improvement from regressing six months later?

How would you approach debugging a complex issue in production?

Medium · Problem-Solving / Case Studies

Approach

  1. Mitigate before diagnosing if users are affected: roll back the last deploy, turn off the feature flag, shift traffic or scale up. Then say how you would confirm the mitigation worked.
  2. Scope it: when did it start, what changed around that time (deploys, config, traffic shape, a dependency), and which hosts, regions, versions or customers are affected? A sharp start that lines up with a change narrows things fast.
  3. Use the telemetry in order: metrics to localise the failing tier (request rate, errors and latency per service; utilisation and saturation per resource), traces to find the slow or failing span, then logs filtered by request ID. Test the cheapest hypothesis that rules out the most.
  4. Go deeper when needed: thread dumps for deadlocks or pool exhaustion (jstack, py-spy dump), CPU profiles or flame graphs, heap dumps for memory growth, strace or tcpdump for syscalls and the network, and checks for exhausted file descriptors, disk or connection pools.
  5. Close the loop: reproduce it in a test environment if possible, fix it, add a regression test and an alert that would have caught it earlier, and write a blameless postmortem with a timeline.

Follow-up

  • The issue happens only under peak load and never in staging. What do you do?
  • How do you tell whether a deploy caused the problem or only coincided with it?
  • You cannot attach a debugger in production. What do you instrument instead?

Suggested practice checklist

The plan starts with a baseline on the reported coding problems, moves through OS and language fundamentals, then storage-oriented system design and production judgment, and ends with the behavioral stories.

Day 1 — Baseline and loop map

  • Write the five reported rounds on one page with the question types each can include.
  • Solve two-sum and binary search from a blank file in your chosen language, noting where you hesitate.
  • List the nice-to-have areas you are weakest in: distributed systems, cloud, data management.

Deliverable: A one-page loop map, two working solutions (two-sum and binary search) and a ranked list of weak areas.

Day 2 — Arrays, strings and linked lists

  • Write longest substring without repeating characters with a sliding window and a last-seen map, and state its complexity.
  • Reverse a linked list iteratively, then recursively, and note the space difference.
  • Merge two sorted arrays with two pointers, then handle in-place merging into a larger buffer.

Deliverable: Three tested solutions (substring, linked-list reversal, sorted merge) with a complexity note and an edge-case list for each.

Day 3 — OS and language fundamentals

  • Write a one-page comparison of process and thread: memory, scheduling, communication and failure isolation.
  • Write a short note on Java garbage collection: reachability, generations, and the difference between minor and full collections.
  • List the object-oriented principles with a small code example for each and one misuse.

Deliverable: Three one-page notes (process versus thread, Java garbage collection, object-oriented principles), each with an example.

Day 4 — Design: URL shortener and load balancer

  • Design the URL shortener: id generation, storage, redirect path, caching, and collision handling.
  • Design a load balancer: algorithms (round robin, least connections), health checks and what happens when the balancer itself fails.
  • Say aloud where each design bottlenecks at ten times the traffic.

Deliverable: Two diagrams with a component list and a scaling note each (URL shortener, load balancer).

Day 5 — Design: storage, replication and consistency

  • Design a file storage system covering chunking, metadata store, replication and node loss.
  • Write how replication works in a distributed system, comparing leader-follower with quorum approaches, and what each gives up.
  • State the CAP theorem with a worked partition scenario for your storage design.
  • Outline a real-time chat design: connection handling, message ordering, delivery and storage.

Deliverable: A storage design, a replication comparison, a CAP scenario and a chat outline, each on one page.

Day 6 — Production judgment and delivery process

  • Write a production debugging runbook: what changed, how you narrow, what you check first.
  • List five steps to improve an existing product's performance, starting with measurement.
  • Write a checklist for evaluating a new technology: fit, risk, cost, exit plan.
  • Outline the software development lifecycle with where you add reviews and tests.

Deliverable: A debugging runbook, a performance checklist, a technology-evaluation checklist and an outline of the development lifecycle.

Day 7 — Behavioral stories and full mock

  • Write four or five stories in situation, decision, result form and map them to the six reported behavioral prompts plus the stakeholder-feedback scenario.
  • Rehearse each story aloud, using I for your own decisions and naming teammates for theirs.
  • Run one mock loop: a coding problem, a design prompt and two behavioral answers back to back.

Deliverable: A story bank mapped to the reported prompts and a note on what failed in the mock run.

Behavioral stories for the Cohesity loop

The Behavioral Interviews are described as covering past experience and fit with the team. The reported prompts mix classic questions about projects, conflict and motivation with ones tied to day-to-day engineering: prioritising across projects, remote communication and trade-offs between quality and speed. Prepare stories that show your own decisions and results.

Describe a challenging project you worked on. What was your role?

Medium · Behavioral / Leadership

Approach

  1. Pick a project where you owned a decision, and say your role in the first sentence. A project involving concurrency, storage, performance or a distributed component maps naturally onto this loop's design topics, but only use it if it is true.
  2. State the situation in two sentences, then name what made it hard: a data size, a deadline, a flaky dependency or an unclear requirement.
  3. Spend most of the answer on decisions: the options you considered, the one you chose, what evidence decided it (benchmark, prototype, incident data) and what you rejected.
  4. Close with a measured result and one thing you would change, such as a test you would add earlier or a design you would simplify.

Follow-up

  • Which part of that project did you personally write or design?
  • What would you do differently if you ran it again?
  • What was the biggest technical risk, and how did you retire it?

How do you prioritize tasks when managing multiple projects?

Medium · Behavioral / Leadership

Approach

  1. Describe a real period with at least three competing items, not a general philosophy. Name what they were: a feature, a production bug and a review backlog, for example.
  2. State your ranking rule: impact on users, deadline, dependencies that block other people, and cost of delay. Show the rule applied to your example.
  3. Say what you dropped, delayed or handed off, and how you told the people affected. A story with no explicit no is weak.
  4. Mention how you re-ranked when something new landed, for instance an urgent defect mid-sprint, since sprint planning and defect fixing are both part of the reported role.

Follow-up

  • Tell me about a time you got the priority wrong.
  • What do you do when two stakeholders each say theirs is the top priority?
  • How do you protect time for work that is important but not urgent?

Can you give an example of how you handled conflict within a team?

Medium · Behavioral / Leadership

Approach

  1. Choose a disagreement about something technical and concrete, such as a design choice, an API shape or a code review comment, rather than a personality clash.
  2. Present the other person's position fairly, in terms they would accept, then your own.
  3. Show how you moved from opinion to evidence: a benchmark, a small prototype, a written comparison of options or an agreed decision owner.
  4. Give the resolution, including if you were wrong or if you compromised, and the state of the working relationship afterwards. Avoid a story where the other person is simply a villain.

Follow-up

  • What would you have done if the evidence had not settled it?
  • How did you keep working with that person afterwards?
  • Have you ever changed your mind in such a disagreement?

What motivates you to perform at your best?

Medium · Behavioral / Leadership

Approach

  1. Give two specific drivers, each backed by an episode: for example, tracking a hard defect to its cause, or owning a system end to end and seeing it run reliably.
  2. Connect them to the work in this role honestly. Candidates report data protection, backup, recovery, deduplication and tiering as problem areas, so say which of these kinds of problems genuinely pulls you in, if any.
  3. Show the driver in behaviour: what you did on a project because of it, not an adjective about yourself.
  4. Avoid generic answers such as growth and challenge with no proof, and avoid drivers that clash with the job, such as wanting only greenfield work when the role involves maintaining and fixing existing software.

Follow-up

  • What drains your motivation, and how do you handle it?
  • Tell me about a time your motivation dropped on a project.
  • What kind of work would you pick if you could choose freely?

How do you ensure effective communication in a remote work environment?

Medium · Behavioral / Leadership

Approach

  1. Lead with concrete practices, not a slogan: written design notes before meetings, pull request descriptions with context and test evidence, short written status updates, and a decision log others can find later.
  2. Cover time zones and hand-offs: agreed overlap windows, hand-off notes that say what is done, what is blocked and what to do next.
  3. Say when you leave text: once a thread has gone back and forth without converging, you move to a call and write the outcome down afterwards.
  4. Add one short story where a miscommunication cost something and what you changed, so the answer shows behaviour rather than good intentions.

Follow-up

  • How do you handle a teammate who does not respond in writing?
  • How do you give critical code review feedback remotely?
  • How do you keep a decision visible to people who missed the meeting?

Discuss a time when you had to make a trade-off between quality and speed.

Medium · Problem-Solving / Case Studies

Approach

  1. Pick a real deadline and say what was at stake. Be explicit that you traded scope or polish, not correctness on the critical path.
  2. Name what you protected, such as tests on core logic, data integrity or rollback ability, and what you consciously deferred.
  3. Say how you recorded the deferred work: a ticket with an owner, a note in the review, or an agreement with the product manager.
  4. Report the outcome honestly, including whether the shortcut caused trouble later and whether you paid it back. A story that claims there was no trade-off is not credible.

Follow-up

  • When would you refuse to ship faster?
  • How did you get the stakeholder to accept the reduced scope?
  • How did you decide the shortcut was safe?