A backend engineer interviewing for a data platform role in early 2026 got asked a question that sounds simple until you try to answer it out loud: "your MongoDB replica set just lost its primary, what happens to writes in the next twelve seconds?" She knew MongoDB was "eventually consistent" as a buzzword. She had never actually traced through what a replica set does during an election, so she guessed. The honest answer is that writes block until a new primary is elected, and the median election takes about 12 seconds under MongoDB's default settings (MongoDB Manual, Replica Set Elections). That's the kind of detail that separates someone who has read a NoSQL comparison chart from someone who has actually operated one of these systems in production.
NoSQL interview questions cluster around a handful of ideas that show up again and again regardless of which specific database the job description names: the CAP theorem and what it actually trades off, the difference between BASE and ACID, how sharding and replication distribute data, and the modeling decisions that come from designing around your queries instead of around your entities. MongoDB remains the most widely deployed NoSQL system by DB-Engines ranking, and it's genuinely possible to configure multi-document ACID transactions in it now, which surprises candidates who memorized "NoSQL means no ACID" a few years ago and never revisited it.
This page covers 50 questions across data models and when to pick each one, the CAP theorem and tunable consistency, replication and sharding mechanics, schema design patterns specific to document and wide-column stores, and the edge cases that come up once a system is actually under load. Where it matters, questions reference MongoDB or Cassandra by name, since most interviewers ground abstract NoSQL questions in whichever system their team runs.
NoSQL fundamentals: what problem it actually solves
Every loop starts here, and a wobbly answer to "why NoSQL at all" tends to color how the interviewer reads everything that follows.
Easy questions
14NoSQL originally stood for "non SQL" and now more commonly gets read as "not only SQL." Most modern NoSQL databases support some query language, MongoDB has its own query language and an aggregation pipeline, Cassandra has CQL which looks almost exactly like SQL syntactically. The real distinguishing trait isn't the absence of a query language, it's the absence of a fixed relational schema and, in most cases, the absence of cross-table joins as a first-class operation.
The honest framing: NoSQL is a category of databases that traded some of the relational model's guarantees, strict schemas, joins, and often strong consistency, for horizontal scalability and flexible data shapes. Whether that trade is worth it depends entirely on the workload, not on which technology is trendier.
Document databases store JSON-like documents with a flexible per-document schema, MongoDB and Couchbase are the common examples, and they're a good fit for semi-structured data with natural nesting, a user profile with embedded addresses, say. Key-value stores are the simplest model, a unique key mapped to an opaque value, Redis and DynamoDB are the standard examples, and they excel at caching and session storage where you always look things up by a single known key.
Wide-column stores, Cassandra and HBase, organize data into tables where each row can have a different set of columns, and they're built for write-heavy workloads at massive scale with predictable query patterns. Graph databases, Neo4j and Amazon Neptune, model data as nodes and edges and are the right tool when the relationships between records matter more than the records themselves, fraud rings, social graphs, recommendation paths.
The honest reasons, in rough order of how often they're the real driver: the data doesn't fit a rigid tabular shape well (deeply nested, variable-shape documents), the write volume or read volume needs horizontal scaling across many commodity servers rather than vertical scaling of one big box, or the access patterns are simple and key-based enough that relational joins were never buying much anyway.
The reasons that get cited but usually aren't the real driver: "NoSQL is faster" (it depends entirely on the workload, a well-indexed Postgres table beats a poorly modeled MongoDB collection on plenty of queries), and "we don't need a schema" (you still have an implicit schema, you've just moved enforcement from the database to your application code, which is a real cost even when it's the right call).
Every document in a document database is indeed stored under a key internally, so the distinction isn't about storage mechanics, it's about what the database can see and act on. A key-value store treats the value as an opaque blob, it can retrieve it by key but can't query, filter, or index on anything inside it. A document database parses the document's internal structure, so you can query on a nested field, index a specific property, or update just one field without rewriting the whole document. That extra capability is the entire reason document databases exist as a separate category rather than everyone just using Redis for everything.
Not quite the same. In Cassandra specifically, the primary key is made up of a partition key (which decides physical placement across the cluster) plus an optional clustering key (which decides sort order within that partition). A relational primary key just guarantees uniqueness and gets its own index, it says nothing about physical data placement. Conflating the two is a common mistake for candidates coming from a relational background, since the term "primary key" sounds identical but does meaningfully more work in a wide-column store.
CAP says a distributed data store can provide at most two of three guarantees at the same time: consistency (every read gets the most recent write or an error), availability (every request gets a non-error response), and partition tolerance (the system keeps working when network messages between nodes are dropped or delayed).
The detail people usually get wrong: partition tolerance isn't really optional in practice. Real networks partition. So CAP in practice reduces to a choice between consistency and availability during the specific window a partition is actually happening, not a permanent architectural label you slap on a database once and never revisit.
Strong consistency guarantees that once a write completes successfully, every subsequent read, from any node, sees that write. Eventual consistency guarantees only that if no new writes happen, all replicas will converge to the same value eventually, but there's a window, possibly milliseconds, possibly longer under load, where different replicas can return different answers to the same read.
The practical question to ask about any given feature isn't "is eventual consistency bad," it's "does this specific read tolerate staleness." A view count on a video doesn't need strong consistency. An account balance during a transfer generally does.
BASE stands for Basically Available, Soft state, Eventually consistent. It's the informal counterpart to ACID (Atomicity, Consistency, Isolation, Durability) that many NoSQL systems are described as following. Basically available means the system responds to requests even under failure, possibly with degraded or partial data. Soft state means the stored data can change over time even without new input, as replicas converge. Eventually consistent means, given enough time without new writes, all replicas agree.
The comparison isn't "ACID good, BASE bad." ACID guarantees matter enormously for a bank ledger. BASE guarantees are a legitimate, deliberate choice for a system where availability under partition matters more than every reader seeing the exact same value at the exact same millisecond, a shopping cart item counter, a social media like count.
A replica set is a group of MongoDB nodes holding the same data, with one primary that accepts all writes and one or more secondaries that replicate the primary's operations via an internal log called the oplog. A replica set can also include an arbiter, a member that holds no data and exists purely to vote in elections when you need an odd number of voters but don't want to pay for another full data-bearing node (MongoDB Manual, Replica Set Elections).
Sharding splits a dataset across multiple machines, each shard holding a different subset of the data, so the total dataset and total write load get distributed rather than living on one machine. Replication copies the same data onto multiple machines for redundancy and read scaling. They solve different problems and most large NoSQL deployments use both together: each shard is itself a replica set, so you get both horizontal scale and failover.
Redis is an in-memory key-value store, data lives in RAM by default (with optional persistence to disk), which makes it extremely fast for reads and writes but limits total dataset size to what fits in memory across the cluster, unlike MongoDB or Cassandra, which are built around data primarily living on disk with memory as a cache layer. That trade makes Redis a poor fit as a system of record for large datasets, but an excellent fit for caching, session storage, rate limiting, and leaderboards, workloads that are naturally small and latency-sensitive.
Generally no, and this is one of the cleaner factual differences to know cold. Relational databases enforce referential integrity at the database layer, insert an order with a customer_id that doesn't exist and the insert fails. Most NoSQL databases have no equivalent mechanism, a document or row can reference an ID that was deleted or never existed, and the database won't stop you or even notice. That responsibility moves entirely to the application layer, which is a real cost, not just a footnote, since it means every place that writes a reference has to get the validation right on its own.
Horizontal scaling means adding more machines to handle more load, as opposed to vertical scaling, making one machine bigger (more CPU, more RAM, faster disks). Relational databases can scale horizontally too, through read replicas and increasingly through native sharding in some engines, but it's historically been harder to do well because of the transactional and join guarantees they provide across the whole dataset. Most NoSQL databases were designed from day one assuming they'd run across many commodity machines, so horizontal scaling is closer to a first-class, built-in capability rather than something bolted on afterward.
Not quite, and the precise answer matters. MongoDB's $lookup aggregation stage does perform joins. Cassandra genuinely has no join operation of any kind, by design, since its whole modeling philosophy assumes you denormalize instead. The more accurate statement is that join support varies a lot by which NoSQL database you're talking about, and even where it exists, it's usually less optimized and less idiomatic to lean on heavily than in a relational system built around joins from the ground up.
Medium questions
22Schema-on-write means the database enforces a structure at insert time, a relational database rejects a row missing a required column. Schema-on-read means the structure isn't enforced by the storage layer at all, it's interpreted by whatever application reads the data back, so two documents in the same MongoDB collection can have entirely different fields and both are valid.
Most NoSQL document and key-value stores default to schema-on-read, which is where the flexibility comes from, but it pushes validation responsibility onto the application (or an optional schema-validation layer MongoDB itself now supports). That flexibility is a genuine tradeoff, not a free win. A malformed document that would have been rejected at write time in Postgres will happily sit in a MongoDB collection until something tries to read a field that isn't there.
Denormalization means duplicating data across multiple documents or rows instead of storing it once and referencing it by key, the way a normalized relational schema would. In a relational database, denormalization is usually a deliberate performance optimization applied after modeling the normalized version first. In most NoSQL document databases, it's the default modeling approach, because joins are either unavailable or expensive, so you model around your read patterns and duplicate whatever data those reads need together.
The tradeoff is real: updating a piece of duplicated data now means updating it everywhere it's duplicated, which is more write complexity in exchange for read simplicity and speed. Whether that trade is worth it depends on your read-to-write ratio. A product catalog read a thousand times for every write is a great candidate. An order ledger updated as often as it's read is not.
MongoDB is generally described as CP by default: a replica set with a lost primary blocks writes rather than serving them from a stale secondary, prioritizing consistency over availability during that gap. Writes are unavailable for roughly the median 12-second election window rather than being served incorrectly (MongoDB Manual, Replica Set Elections).
That said, it's tunable, not fixed. Read preferences let you read from secondaries and accept staleness in exchange for availability during a partition, which pushes a given deployment closer to the AP side for reads specifically, even while writes stay CP. The honest answer names the default and then explains that the actual position depends on the read preference and write concern configured for that deployment.
Cassandra defaults toward AP. It has no single primary node, any node can accept a write, and during a partition it keeps accepting reads and writes on whichever nodes are reachable rather than blocking, which is the availability half of the tradeoff. Consistency is tunable per operation through named consistency levels rather than fixed at the cluster level.
That tunability is the real point interviewers are testing for. Cassandra isn't "an AP database," it's a database that lets you dial the consistency-availability tradeoff per query, from ONE (fast, weakly consistent) up through QUORUM to ALL (slow, strongly consistent, and unavailable if even one replica in the set is down) (Apache Cassandra documentation, Dynamo architecture).
They can, and the "NoSQL has no ACID" line is outdated. MongoDB supports multi-document ACID transactions, has since version 4.0 for replica sets and 4.2 for sharded clusters, contradicting the common misconception that document databases inherently lack transactional guarantees (MongoDB, NoSQL Explained).
The honest nuance: multi-document transactions in MongoDB come with a real performance cost compared to single-document writes (which are always atomic on their own), and most well-modeled document schemas try to keep related data inside a single document specifically to avoid needing a multi-document transaction in the first place. The feature existing doesn't mean reaching for it should be the default.
Secondaries detect the primary is unreachable once the configured heartbeat timeout passes, ten seconds by default. An election starts: eligible members (those with the highest replication position visible to the cluster) request votes from the rest of the set. The first candidate to receive a majority of votes becomes the new primary.
During that window, writes are rejected or blocked, the old primary role is gone and no new one exists yet. Reads can continue uninterrupted if the driver is configured to allow reads from secondaries. The whole process has a median duration of about 12 seconds under default settings, tunable via the electionTimeoutMillis setting, and this window is exactly the kind of detail worth having memorized rather than approximated when an interviewer asks what "highly available" actually means in practice.
Synchronous replication waits for one or more replicas to confirm they've received a write before the write is considered complete, guaranteeing those replicas are current at the cost of added write latency. Asynchronous replication lets the write return as soon as the local (or coordinator) node has it, propagating to other replicas in the background, which is faster but opens a window where a replica can be behind.
Cassandra's replication is tunable rather than fixed to one mode: the write consistency level you choose (ONE, QUORUM, ALL) effectively decides how many replicas must synchronously acknowledge before the write returns to the client, while any replicas beyond that count receive the write asynchronously. It's not "sync or async," it's "how many replicas synchronously, and the rest catch up."
A shard key is the field, or combination of fields, used to decide which shard a given piece of data lives on. A good shard key spreads both data volume and query load evenly across all shards. A bad one, a shard key that's monotonically increasing like a timestamp or an auto-incrementing ID, routes almost all new writes to whichever shard currently holds the highest key range, creating a hotspot where one shard is overloaded while the rest sit idle.
This is one of the most common real-world NoSQL production incidents, and it's usually invisible in development because a small dataset spread across two shards doesn't reveal the imbalance the way a billion-document collection across twenty shards does. Choosing a shard key is a decision you often can't easily reverse after the fact, which is exactly why it comes up in interviews as a "what would you actually do" question rather than a definitions question.
Range-based sharding assigns contiguous ranges of the shard key's values to each shard, which makes range queries (all orders from March) efficient because they hit a small number of shards, but it's exactly what creates hotspots with monotonically increasing keys, since new data always lands at the edge of the range. Hash-based sharding runs the shard key through a hash function first and distributes based on the hash output, which spreads writes evenly regardless of the key's natural ordering, but turns a range query into a scatter-gather across every shard, since consecutive original values no longer land near each other.
Neither is universally correct. The choice depends on whether your dominant query pattern is range-based lookups or point lookups, and whether your write pattern is naturally ordered (timestamps, sequential IDs) or already well distributed.
Embed when the related data is almost always read together with the parent, has a bounded size that won't grow unbounded over the document's lifetime, and doesn't need to be queried or updated independently very often, a blog post's comments if the post has a handful of them, an order's line items. Reference (store an ID and look it up separately, or denormalize a copy of just the fields you need) when the related data is large, grows unboundedly, is shared across many parents, or is updated independently on its own schedule, a user's full order history referenced from their profile rather than embedded in it.
MongoDB's own guidance is blunt about the failure mode here: an embedded array that grows without bound is the single most common modeling mistake in production MongoDB collections, since a document has a hard 16MB size limit and unbounded embedded arrays eventually hit it, usually well after the design decision that caused it is buried in months-old code.
The aggregation pipeline is MongoDB's framework for multi-stage data processing, each stage takes the output of the previous stage and transforms it, filtering, grouping, reshaping, joining. It's the mechanism you reach for once a query needs more than a plain find(), computing aggregates, joining across collections, or reshaping output for the client.
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customerId", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
])
// top 10 customers by completed order total$lookup is an aggregation pipeline stage that performs a left outer join against another collection in the same database, matching a field in the current collection against a field in the target collection and attaching the matches as an array. Mechanically it does resemble a SQL left join.
db.orders.aggregate([
{
$lookup: {
from: "customers",
localField: "customerId",
foreignField: "_id",
as: "customer"
}
}
])The practical difference is performance and intent. Relational databases are built around joins as a core, heavily optimized operation. MongoDB's $lookup works, and is genuinely useful for reporting queries and admin tooling, but reaching for it on your hot path for every request usually signals the schema should have embedded or denormalized that data instead, rather than that $lookup itself is slow.
A single-field index speeds up queries and sorts on one field. A compound index covers multiple fields together, and field order inside it matters enormously, MongoDB can use a compound index for a query on a prefix of its fields (the first field alone, or the first two together) but not for a query that only touches a later field in isolation.
db.orders.createIndex({ customerId: 1, status: 1, createdAt: -1 })
// serves: query on customerId alone
// serves: query on customerId + status
// serves: query on customerId + status, sorted by createdAt
// does NOT serve: query on status aloneThe general rule for ordering is equality fields first, then sort fields, then range fields, and it's a genuinely testable detail, an interviewer can hand you a query and a candidate index and ask whether it's actually used.
The partition key determines which node (or nodes, under replication) physically stores a given row, all rows sharing the same partition key live together on the same set of replicas. The clustering key sorts rows within a single partition on disk, it doesn't affect which node the data lives on, only the order rows come back in when you query that partition.
CREATE TABLE events_by_user (
user_id uuid,
event_time timestamp,
event_type text,
PRIMARY KEY (user_id, event_time)
);
-- user_id is the partition key: decides which node stores the row
-- event_time is the clustering key: sorts rows within that user's partitionCassandra has no efficient ad-hoc join and limited support for filtering on non-key columns without a secondary index (which itself has real performance caveats at scale). A query that doesn't specify the partition key generally has to scan every node in the cluster, which defeats the entire point of partitioning. So the standard practice, sometimes called "query-first design," is to design a separate, denormalized table for each distinct query pattern your application actually needs, rather than one normalized entity table you query flexibly.
Concretely: if you need to look up orders by customer and also look up orders by product, you don't build one orders table and query it two ways, you build orders_by_customer and orders_by_product, each with the appropriate partition key, and you write to both on every insert. It's a genuine mental shift for anyone coming from relational modeling, where you'd normalize once and query however you needed afterward.
Redis supports strings, lists, sets, sorted sets, hashes, and a few more specialized types like streams and bitmaps, all as first-class values a key can point to, not just opaque blobs. A sorted set, for example, gives you an efficient leaderboard for free, insert scores as members and Redis maintains the ranking, no application-side sorting needed.
ZADD leaderboard 1500 "alice"
ZADD leaderboard 2200 "bob"
ZREVRANGE leaderboard 0 2 WITHSCORES
# returns top 3 members by score, already sortedThis is what separates Redis from a truly minimal key-value store like a plain memcached, the rich data structures let you push logic that would otherwise live in application code down into the database itself, at the cost of a slightly more specific mental model to learn per structure.
When the query you actually need to answer is about the relationships themselves, not just the entities, and especially when the depth of that relationship traversal is variable or unknown ahead of time. "Find all products this customer bought" is a simple join, a relational database handles it fine. "Find everyone within three degrees of this person who's also connected to someone flagged for fraud" is a variable-depth traversal that gets exponentially more expensive to express as nested joins in SQL as the depth grows, but stays roughly the same cost in a graph database, since traversing an edge is a constant-time operation regardless of how many hops deep you already are.
A write conflict happens when two clients write to the same key at roughly the same time, on a system without a single node that serializes all writes, and the system has to decide which value survives. Last write wins is the simplest resolution strategy: each write carries a timestamp, and whichever write has the later timestamp is kept, the earlier one is silently discarded.
It's simple and it's the default in several systems, Cassandra included, but it has a real failure mode: clock skew between nodes can make an objectively earlier write look later if that node's clock is running ahead, silently discarding the "correct" update. Systems that care about this use logical clocks (vector clocks, hybrid logical clocks) instead of, or alongside, wall-clock timestamps specifically to avoid that failure mode, though last-write-wins on wall-clock time remains extremely common in practice because it's cheap and good enough for most workloads.
An idempotent operation produces the same end result no matter how many times it's applied, applying it twice does the same thing as applying it once. It matters in distributed NoSQL systems because network partitions and retries are a normal, expected part of operation, not an edge case, a client that doesn't get an acknowledgment for a write genuinely can't tell whether the write succeeded and the ack was lost, or the write itself never landed, so it has to retry, and that retry needs to be safe.
Not idempotent: "increment counter by 1" (retry double-counts)
Idempotent: "set counter to 5" (retry is a no-op)
Idempotent: "set counter to 5 if version = 3" (conditional write, safe to retry)Designing writes to be idempotent, using a set-with-version-check instead of a blind increment, using an upsert keyed on a client-generated ID instead of an auto-generated one, is one of the more underrated practical skills in working with any distributed NoSQL system.
When the data genuinely is relational and the queries need to slice it in ways you can't fully predict up front, ad hoc reporting, complex multi-entity joins, a finance or inventory system where referential integrity actually matters and you don't want to reimplement it in application code. Also when the team already knows SQL well and the dataset size doesn't come close to needing horizontal scale, since a well-indexed Postgres instance handles a surprising amount of real-world load on a single, well-specced machine.
The honest failure mode to watch for, on both sides, is picking a database because of what's trendy or what a past project used, rather than because the actual read and write patterns for this project were analyzed first. That analysis, not brand preference, is what should drive the decision.
Horizontal partitioning, which is what sharding is, splits a table or collection by rows, different rows live on different machines, but every machine has the same columns or fields. Vertical partitioning splits by columns instead, different fields of the same logical record live in different places, a user's frequently-accessed profile fields in one store and their rarely-accessed audit history in another. They solve different problems and can be combined, a system can vertically partition data across two different databases and then horizontally shard within each.
A time-series workload is dominated by writes that arrive in roughly chronological order, sensor readings, metrics, logs, and queries that mostly ask for a range of time plus some aggregation, average CPU over the last hour, say. General-purpose NoSQL databases can store this data fine, but purpose-built time-series databases (InfluxDB, TimescaleDB) add compression schemes tuned for sequential numeric data and retention policies that automatically expire old data, both of which matter enormously at the volume real monitoring systems generate, and both of which you'd otherwise have to build yourself on top of a general-purpose store.
Hard questions
12Cassandra derives its consistency model from Dynamo's approach: for a given replication factor N (how many copies of each piece of data exist), you choose how many replicas must acknowledge a write (W) and how many must respond to a read (R). If R + W is greater than N, the read set and write set are guaranteed to overlap on at least one replica, which means any read is guaranteed to see the most recent acknowledged write.
RF = 3
Write at QUORUM -> W = 2
Read at QUORUM -> R = 2
R + W = 4 > RF(3), so every read overlaps at least one node from the writeDrop either side to ONE and you get faster, more available operations, but you lose that overlap guarantee, a read might land entirely on replicas that haven't received the latest write yet (Apache Cassandra documentation, Dynamo architecture). This is the mechanism, not a metaphor, and being able to draw it out is what separates a candidate who's actually configured Cassandra from one who's memorized the term.
When a node that should receive a replica of a write is temporarily unreachable, the coordinator node handling that write stores a "hint," essentially a note saying "this data belongs on node X, deliver it once X comes back." Once the target node rejoins the cluster, the hint gets replayed to it, bringing it back in sync without requiring a full repair.
Hinted handoff is a genuine availability win, it lets writes succeed even when a target replica is briefly down, but it has real limits: hints expire after a configurable window (three hours by default in many Cassandra deployments), and a node down longer than that needs a full anti-entropy repair to catch back up, which is a heavier, manual-ish operation most teams schedule rather than rely on happening automatically.
A scatter-gather query is one that has to be sent to every shard in the cluster because the query doesn't include the shard key, so the database can't route it to a specific subset of shards, it has to ask all of them and merge the results. It works, but it scales the query's latency and load with the total number of shards rather than staying constant as the cluster grows, which is the opposite of what sharding is supposed to buy you.
The practical implication for schema design: your most frequent, latency-sensitive query pattern should be reflected in your shard key choice. If you shard a multi-tenant system by document ID but the app almost always queries by tenant ID, nearly every query becomes scatter-gather, and adding more shards makes that specific query slower, not faster, which is a genuinely counterintuitive result worth having a real answer for.
A covered query is one where every field the query needs, both for filtering and for the projection returned to the client, exists in the index itself, so MongoDB never has to fetch the actual document from disk (or from the storage engine's cache) at all, it answers entirely from the index structure. That's meaningfully faster than a normal indexed query, which still uses the index to find matching documents but then has to go fetch each full document.
db.users.createIndex({ email: 1, name: 1 })
db.users.find({ email: "a@b.com" }, { _id: 0, name: 1 })
// covered: email is the filter, name is the only field projected,
// both exist in the index, no document fetch neededIt's a niche optimization most teams never bother chasing, but it's a good marker of depth in an interview, since answering it correctly requires actually understanding how an index and a document are stored separately, not just that "indexes make things faster."
The classic mistake is picking a low-cardinality partition key, status, a boolean flag, a small fixed set of categories, because it feels like the natural "grouping" column the way a foreign key would in a relational schema. Every row sharing that value lands in the same partition, and a small number of huge partitions defeats the purpose of a distributed cluster just as badly as a monotonic shard key does in a sharded document store, one or two nodes end up holding almost the entire dataset while the rest of the cluster sits nearly empty.
The fix is almost always compositing a higher-cardinality field into the partition key alongside the low-cardinality one, partitioning by (status, bucket_of_user_id) instead of status alone, for instance, so the data actually spreads. It's the wide-column-store version of the shard-key hotspot problem, and interviewers who've operated Cassandra in production tend to ask about it specifically because it's the mistake almost everyone makes exactly once.
A native graph database, Neo4j is the standard example, stores relationships as physical pointers between nodes on disk, so traversing an edge is a direct pointer dereference regardless of overall graph size, which is what gives it that constant-per-hop traversal cost. A graph layer built on top of a different engine, a graph API sitting on top of a document store or wide-column store, has to compute relationships at query time from indexes or join-like operations, which tends to degrade as the graph grows or as traversal depth increases, even if the API on top looks identical.
The practical implication: for a genuinely deep, unpredictable traversal workload, the underlying storage architecture matters more than the query language sitting on top of it, and it's a fair follow-up question if a candidate claims graph database experience without knowing which kind they used.
Pessimistic concurrency control locks a resource before modifying it, so no other writer can touch it until the lock releases, guaranteeing no conflicts but adding contention and latency, and requiring the system to actually support locks in the first place. Optimistic concurrency control doesn't lock anything up front, it lets the write proceed and checks, at write time, whether the data has changed since it was last read, typically via a version number or timestamp, rejecting the write if it has.
Optimistic concurrency is far more common across NoSQL systems, most of which weren't built with cross-request locking as a primitive at all. MongoDB's typical pattern is a conditional update matching on a version field, if the match fails because someone else updated it first, the application retries the whole read-modify-write cycle. It works well when conflicts are genuinely rare, and gets expensive fast under high contention on the same document, which is a real signal that document isn't modeled at the right granularity.
First option: move the unbounded array out into its own collection, referenced by the parent document's ID, which is the standard fix and usually the correct one, since an array that grows without bound was almost certainly the wrong modeling choice in the first place. Second option, if you genuinely need the embedded shape for read performance and the array has a natural bound you can enforce, cap it and archive older entries elsewhere, a document keeping only the most recent 100 events embedded, with full history in a separate time-series collection.
What doesn't actually work as a long-term fix: increasing some setting to raise the limit, MongoDB's 16MB document limit isn't configurable, it's a hard constraint of the BSON format, so any answer that starts with "just increase the limit" is a signal the candidate hasn't actually hit this in production.
What went wrong is almost certainly a low-cardinality or naturally skewed partition key, one customer, one tenant, or one category that's simply much larger than the others, growing without any compensating distribution in the key. This is the wide-column equivalent of a hot shard, and it degrades gradually enough that it often isn't caught until that one partition is measurably slower to query than the rest of the table.
The fix without downtime generally means introducing a composite or "bucketed" partition key going forward, splitting the oversized logical partition into several physical ones by adding a derived field, a hash of the row's own ID, a time bucket, to the partition key, and dual-writing to both the old and new table shape during a migration window, then cutting reads over once the new table is caught up. There's no in-place repartitioning operation that does this for you, which is exactly why getting the partition key right at design time matters so much more in Cassandra than an equivalent decision does in a relational schema.
A Cassandra secondary index on a non-key column has to be maintained locally on every node, since the base table's data is already partitioned by the partition key, not by the indexed column. A query using that secondary index still has to fan out to every node in the cluster to check each node's local index, which is the scatter-gather problem again, it works, but its cost scales with cluster size rather than staying flat.
That's the opposite of what a relational secondary index buys you, where the index lives centrally and a lookup through it is cheap regardless of table size. The practical guidance most experienced Cassandra users converge on is to treat secondary indexes as fine for low-cardinality columns on small clusters or for exploratory queries, and to model a proper query table (a la the query-first design pattern) for anything on a genuinely hot path.
A few concrete techniques, layered depending on how strict the guarantee needs to be. Read-your-own-writes consistency routes a user's reads immediately following their own write to the same node (or a strongly consistent read path) that handled the write, so they never see their own change appear to vanish, even while other users might briefly see a stale value. Session consistency extends that same idea across a whole session rather than a single request.
Beyond that, application-level patterns fill the gap: optimistically updating the UI with the value the user just submitted rather than waiting for a fresh read, and, for genuinely critical paths, using a strongly consistent read (MongoDB's read concern "majority," or Cassandra's QUORUM) specifically for that one operation rather than for the whole application. The mature answer isn't "pick one consistency level for everything," it's matching the guarantee to each specific read's actual requirement.
DynamoDB trades flexibility for operational simplicity, it's a managed key-value and document store with no servers to patch, tune, or fail over yourself, but its query flexibility is intentionally narrower, you generally need to know your access patterns up front and design your keys and indexes around them before you write any data, since ad hoc querying is expensive or outright unsupported. Self-managed MongoDB or Cassandra gives you more query flexibility and more control over tuning, at the real cost of someone on the team owning replication, sharding, upgrades, and capacity planning.
The honest deciding factor for most teams isn't a technical capability gap, it's whether the team has, or wants to build, the operational expertise to run a distributed database well. A small team without dedicated infrastructure engineers is usually better served by a managed option even if it means designing access patterns more rigidly up front.
How to prepare for a NoSQL interview
Skip the memorized CAP theorem triangle diagram. Pick one real database, MongoDB is the easiest to get running locally in ten minutes, and build something with a genuine one-to-many relationship, then deliberately model it two ways: fully embedded, and fully referenced. Query both under a realistic amount of test data and watch what actually breaks first, an unbounded array hitting the document size limit, or a $lookup that gets slow once the referenced collection grows past a few hundred thousand rows. That single exercise teaches the embed-versus-reference tradeoff faster than any comparison chart.
Across NoSQL-focused mock interviews run through LastRoundAI, the CAP theorem question itself rarely trips candidates up, most people can recite consistency, availability, partition tolerance without hesitation. The follow-up, "so which one does the specific database in this job posting default to, and why," is where the conversation stalls more often. Reciting the theorem is table stakes. Being able to place a specific real system on it, and explain what changes when you tune a specific setting, is what the interview is actually probing for.
If you're prepping for a role that names a specific NoSQL system in the job posting, spend disproportionate time on that one system's actual documentation over generic comparison articles. A candidate who can correctly describe MongoDB's default read concern, or Cassandra's LOCAL_QUORUM behavior in a multi-datacenter deployment, reads as meaningfully more senior than one who can only speak in CAP-theorem generalities, even when both candidates technically know the same theory underneath.
Get the reps in before the real thing
Reciting the CAP theorem alone in your head is not the same as defending a shard key choice out loud when an interviewer asks what happens once your traffic pattern changes. LastRoundAI's mock interview mode runs live system design and coding rounds with real-time follow-up questions in your browser, and the free plan includes 15 credits a month that reset monthly rather than piling up unused. Starter is $19/mo if a handful of sessions isn't enough runway.
Once your answers hold up under a follow-up, the slower part of the job hunt is usually just getting in front of enough backend and data platform roles that actually test this material instead of treating "NoSQL experience" as a checkbox on a resume screen. Auto-Apply queues tailored applications for your review, 10 a month on the free plan, up to 400 a month on the Ultimate plan, and nothing goes out until you approve it.
Questions about either product go to contact@lastroundai.com. That's the only inbox we check.
How this list was built
Worth being straight about where these questions come from, because plenty of pages in this category are not. The set was compiled from a research pass across official documentation, vendor release notes, published engineering writing and public discussion of hiring processes, then cross-checked against the current version of each technology so nothing here describes behaviour that has since changed.
What that means in practice: these are the questions the material supports as reasonable and current for this role, not a transcript of any one company's loop. We have not sat in on your interview and we are not going to claim we have. Treat the list as well-sourced preparation rather than a leaked question bank, and expect your panel to phrase things their own way.
If you spot something out of date, tell us at contact@lastroundai.com and we will fix it.
Frequently asked questions
Do I need hands-on NoSQL experience to pass?
It shows quickly either way. Textbook answers hold up until the interviewer asks what you did when it broke, and that is usually the question that separates candidates. A small real project you can discuss honestly beats a longer list of familiarity claims.
Is NoSQL still worth learning in 2026?
For interview purposes the question is really whether the teams you are targeting use it, which is worth checking against their actual job postings rather than general popularity rankings. Where it is in use it tends to be deeply embedded and slow to replace.
Should I memorise NoSQL syntax for the interview?
Rarely worth it. Most interviewers care that you know what to reach for and why, and will not fail you for forgetting an exact flag. Being confidently wrong about behaviour costs far more than admitting you would check the documentation.
What is the most common mistake in NoSQL interviews?
Answering the question that was asked and stopping there. The strongest candidates add the trade-off or the failure mode without being prompted, which is what signals real use rather than revision.
