Netflix Java Developer Interview Questions & Answers

netflix icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

11. How would you design ad frequency and order tracking?System DesignMediumNetflix

Question Details

Design a low-latency, high-throughput system that tracks ad frequency and delivery order across requests. Cover data flow, storage, freshness, and how you would keep reads fast under load.

Short Interview Answer (30-60 seconds)

At a high level, this system must decide how often a user can see an ad and which ad comes next. The main challenge is keeping that decision fast while the delivery state keeps changing. I would explain it in three flows: the request path, the state lookup path, and the background update path. Reads use a Hot State Cache first, while delivery events update durable state later. The trade-off is that cached state can become stale, so freshness checks and conservative decisions are important.

Detailed Explanation

The system must quickly decide whether a user can receive another ad and which ad should come next. Every delivery changes the user's frequency count and delivery order. Requests still need to stay fast under load. The diagram separates the work into a fast request path and a background update path. The request path reads rules and current state, then returns a decision. The background path records deliveries, updates saved state, and keeps the cache fresh.

Useful Questions to Ask the Interviewer
  1. Should frequency limits be tracked per user, per household, or both?
  2. How fresh must the frequency and order state be before serving an ad?
  3. What should happen when the cache or current-state store is temporarily unavailable?
How would you design ad frequency and order tracking? diagram
How to Explain It in an Interview
1. Start with the request path

I would say that an ad request first goes to the API / Edge. It handles Authentication, Validation, and Rate Limiting. Then it forwards the request to the Java Ad Frequency & Order Tracking Service.

The Java service is stateless across separate JVM replicas. Each JVM Replica runs Java 21/25 and contains a Request Handler, Eligibility / Order Engine, and Response Builder. Virtual Threads can help while waiting on blocking I/O. Separate JVM replicas do not share memory, so shared state stays outside them.

2. Read rules and current state

The service reads the Campaign Rules / Config Store for frequency caps, delivery order rules, and creative pools. It then checks the Hot State Cache first.

The cache stores state by user or household plus campaign. That includes count, next order, version, and timestamp. Cache-first reads keep requests fast.

If the cache misses or the entry is stale, the service reads the Durable Current-State Store. This store keeps materialized current state, meaning the useful counters and order values are already prepared. The service does not scan raw delivery events on each request.

3. Decide and return the response

The Eligibility / Order Engine uses the rules and current state. It checks the frequency cap and decides the next ad order.

The Response Builder creates the ad decision. The Java service sends that result back through the API / Edge to the Client / Ad Request Source.

4. Record deliveries in the background

After delivery or acknowledgement, the Java service writes a Delivery Event to the append-only Event Log / Queue. The queue is partitioned by user or household plus campaign.

The replicated Java Consumer / Updater Service reads those events. It performs idempotent per-key updates, meaning retries do not apply the same state change twice. It updates the Durable Current-State Store, then refreshes or invalidates the Hot State Cache.

5. Handle freshness, failures, and operations

The stored state carries a version or timestamp so the service can judge freshness. If the cache is unavailable, the service reads the current-state store. If freshness is uncertain, the engine makes a conservative cap or order decision.

Failed background processing goes to the Retry Queue with backoff. After retries are exhausted, the event moves to the Dead Letter Queue. Metrics, Logs, and Traces watch latency, cache hit rate, errors, queue lag, retries, and end-to-end behavior.

The trade-off is speed versus freshness. Caching keeps reads fast, but stale state must be handled carefully.

Engineering Considerations / Design Trade-offs

The benefit is that normal ad decisions stay fast because the service reads the Hot State Cache first. Materialized current state also keeps fallback reads simple and avoids scanning old delivery events. The downside is that cached state can become stale after a recent delivery. Versions and timestamps help detect that risk. The background event path adds more moving parts, including the Event Log / Queue, consumer, Retry Queue, and Dead Letter Queue. Partitioning helps the system grow, but updates for the same user or household plus campaign must stay logically consistent. We accept this extra complexity because it keeps the main request path fast.

Why Interviewers Ask This

Interviewers ask this question to see whether you can separate a fast request path from reliable background state updates. They want good judgment about caching, durable state, freshness, partitioning, retries, and failure handling. They also want to see whether you understand that separate Java JVM replicas do not share memory. Most importantly, they are checking whether you can explain the trade-off between fast reads and fresh, correct delivery state.

Interviewer may ask next
What would you change if the interviewer required much stricter frequency-cap freshness so a user should almost never receive an extra ad after reaching the cap?

I would keep the same basic design, but I would make the freshness check more conservative. The affected parts are the Hot State Cache, the Durable Current-State Store, and the Eligibility / Order Engine.

Each state record already carries a version or timestamp. Before making a decision, the service can reject a cache entry when its freshness is uncertain and read the Durable Current-State Store instead. If the service still cannot confirm fresh state, the Eligibility / Order Engine should choose the conservative result and avoid serving another ad when the cap may already be reached.

The background Java Consumer / Updater Service would still process Delivery Events and refresh or invalidate the cache after updating durable state. The Retry Queue and Dead Letter Queue behavior would stay the same.

This keeps the existing architecture and gives stronger protection against over-delivery. The downside is higher latency and more load on the Durable Current-State Store because more requests will skip or distrust cached data.

How would this design handle a failure in the background Java Consumer / Updater Service while delivery events continue arriving?

I would keep accepting Delivery Events into the Event Log / Queue because that path is already separate from the request decision path. The Java Consumer / Updater Service can recover and continue reading events later.

If processing one event fails, the design sends it to the Retry Queue. Backoff means the system waits before trying again, which avoids retrying the same failing work too aggressively. The consumer uses idempotent per-key updates, so a retry should not apply the same state change twice.

If retries are exhausted, the event moves to the Dead Letter Queue. That keeps one bad event from blocking normal processing and allows later inspection or reprocessing.

During a long consumer outage, queue lag grows and cached state can become less fresh. Metrics, Logs, and Traces should expose that problem. The downside is that ad decisions may need to become more conservative until background processing catches up.

12. How would you design a thread-safe key-value store?System DesignMediumNetflix

Question Details

Design an in-memory key-value store that is safe under concurrent access. Explain synchronization strategy, read and write behavior, contention, failure modes, and how you would test correctness.

Short Interview Answer (30-60 seconds)

At a high level, the goal is to keep one in-memory key-value store correct when many threads use it at the same time. The main challenge is allowing fast reads without letting writes corrupt shared data. I would explain the read path, the write path, and how keys are routed to shards. Each shard has its own HashMap and ReadWriteLock, so different shards can work in parallel. The trade-off is that hot keys can still create contention, and a JVM crash loses the in-memory data.

Detailed Explanation

The system must store key-value pairs in memory and stay correct when many threads use it at the same time. The hard part is shared data. Two threads may touch the same key almost together, so reads and writes need clear safety rules. The diagram solves this by splitting the store into shards. A key is hashed to one shard. Each shard has its own HashMap and ReadWriteLock. This keeps locking local and lets unrelated shards work in parallel.

Useful Questions to Ask the Interviewer
  1. Is this store only inside one JVM, or must data be shared across processes?
  2. Must data survive a JVM crash, or is in-memory loss acceptable?
  3. Are get, put, and delete enough, or do we need multi-key operations?
How would you design a thread-safe key-value store? diagram
How to Explain It in an Interview
1. Explain the main idea

I would keep the store inside one JVM and split the data into several shards. A shard is one smaller part of the key space. Reader and writer threads enter through the Public API. It supports get(key), put(key, value), and delete(key). The Request Router runs hash(key) and chooses the shard.

I avoid one global lock around the whole store. A global lock would make unrelated keys wait. Sharding keeps most locking inside one shard.

2. Explain the GET path

For a read, the client sends a GET request to the Public API. The Request Router hashes the key and sends it to one shard. That shard acquires its read lock and reads the value from its HashMap.

Multiple readers can hold a shard's read lock together when no writer holds its write lock. Reads on different shards can also run together. The value, or a not-found result, returns through the API to the reader thread.

3. Explain the PUT and DELETE path

For put or delete, the router hashes the key and selects one shard. That shard acquires its write lock before changing the HashMap.

The write lock gives one writer exclusive access to that shard during the change. Other shards can continue serving reads and writes. After the map changes, the lock is released and an acknowledgement returns to the writer thread.

4. Explain synchronization and contention

The key rule is that each lock protects one shard, not the whole store. This reduces contention because unrelated keys often use different locks. Lock and unlock also give safe visibility between threads. A later thread that acquires the lock sees changes made before the earlier thread released it.

A hot key or hot shard is still a weak point. Many requests mapped there compete for the same lock. A writer also blocks readers on that shard while it holds the write lock.

5. Explain testing, failures, and operations

I would stress test the store with ExecutorService or virtual threads. I would mix get, put, and delete calls and look for races. I would also run linearizability checks, meaning completed operations should look as if they happened in one valid order.

I would test hot shards, contention, starvation risk, and process crashes. Metrics and logs help show lock pressure. The main failure limit is durability. If the JVM crashes, the in-memory data is lost because this design has no persistent storage.

Engineering Considerations / Design Trade-offs

The benefit is that different shards can work at the same time. Reads do not need to stop because another shard is being updated. Writes also block only the shard they change, not the whole store. The downside is that the design is still sensitive to hot keys. If many requests use the same shard, they still wait on the same lock. ReadWriteLock also adds some locking work around every operation. The store has another important limit: it only lives in memory. If the JVM crashes, the data disappears. We accept that here because the design is focused on thread safety inside one JVM, not durability across failures.

Why Interviewers Ask This

The interviewer wants to see whether you understand shared memory and concurrency inside one JVM. They want to know if you can avoid one large lock, keep reads and writes correct, and explain where contention still happens. They also want to see whether you can separate thread safety from durability and describe how you would test concurrent behavior instead of only saying that the code uses locks.

Interviewer may ask next
What would you change if one hot key receives most of the traffic?

I would keep the same basic design, but I would focus on the shard that contains the hot key. The current design routes every operation for that key to one shard, so all of those requests share the same ReadWriteLock. That means the hot key can become a bottleneck even though the rest of the store is well sharded.

For reads, multiple reader threads can still share the read lock when no writer is active. The real problem appears when frequent writes arrive, because the write lock blocks other reads and writes on that shard.

I would first measure this with the existing metrics and logs. That tells me whether the problem is one key or a wider shard imbalance. If allowed by the requirements, I could increase the number of shards so unrelated keys are spread more evenly. That does not remove contention for one single hot key, because that key must still have one synchronization point.

The main downside is that very hot single-key traffic cannot be fully solved by sharding alone while keeping one correct value in this design.

How would the design change if the data must survive a JVM crash?

I would keep the same in-memory locking design for thread safety, but the current diagram would no longer be enough for durability. Right now, each shard stores data only in a HashMap inside the JVM. If the process crashes, that memory is lost.

The part affected is the storage behavior behind each shard. The existing ReadWriteLock would still protect concurrent access inside the JVM. However, each successful write would also need a durable storage step before we claim the update is safely saved.

That durability mechanism is not shown in the current diagram, so I would treat it as a new requirement rather than pretending the current HashMap already provides it. I would also define the write order carefully so a client does not receive success before the durable copy is complete, unless the interviewer accepts possible data loss.

The main downside is higher write latency and more system complexity. Durable storage also introduces new failure cases that the current single-JVM design does not need to handle.

13. How would you design an ad pacing system?System DesignHardNetflix

Question Details

Design an advertising pacing system that spreads campaign delivery over time instead of exhausting budget early. Cover pacing algorithms, budget tracking, concurrency control, traffic swings, and observability.

Short Interview Answer (30-60 seconds)

At a high level, the goal is to spread campaign delivery across its full time window without spending the budget too early. The hard part is making fast decisions while many ad requests arrive at once. I would explain the design in three flows: campaign setup, real-time pacing decisions, and background delivery tracking. The Pacing Decision Service uses cached settings, counters, traffic forecasts, and an atomic budget reservation before serving. The main trade-off is that fast counters may be slightly approximate, while the Budget Ledger / Reservation Store protects exact spending.

Detailed Explanation

The goal is to spend each campaign budget gradually instead of using too much money early. This is difficult because traffic can rise or fall quickly, and many ad requests can arrive at the same time. Each pacing decision must be fast, but concurrent requests must not spend the same remaining budget. The design separates campaign setup from the runtime path. The Pacing Decision Service makes fast delivery decisions. Background processing records what was actually delivered and keeps counters and committed spending aligned.

Useful Questions to Ask the Interviewer
  1. Should pacing aim for a smooth delivery curve, or can campaigns catch up later?
  2. How strict must the system be about never spending above the campaign budget?
  3. How fresh do traffic forecasts and delivery counters need to be?
How would you design an ad pacing system? diagram
How to Explain It in an Interview
1. Set up and update campaigns

For campaign setup, the Advertiser / Campaign Manager UI sends create or update requests through the API Gateway / Auth / Validation / Rate Limits layer. The Campaign Management Service then saves budget, start and end time, targeting, pacing mode, and caps in the Campaign Config Store.

The Campaign Management Service also warms or updates the Config Cache inside the Pacing Decision Service. This cache keeps campaign settings close to the fast request path.

2. Make each pacing decision quickly

For runtime delivery, an Ad Request / Auction Path reaches the Ad Server / Auction Service. That service calls the Pacing Decision Service.

Each Java service replica has its own JVM-local Config Cache. Replicas do not share that memory. The Pace Controller uses remaining budget, remaining time, current spend, and the Forecast / Traffic Model. It calculates a target spend rate and applies smooth pacing, catch-up, or slow-down adjustments.

The service also reads the Real-Time Counters Store for recent spend, impressions, and clicks. These counters are fast enough for the decision path.

3. Prevent concurrent requests from overspending

Before serving an ad, Token / Budget Reservation reserves budget in the strongly consistent Budget Ledger / Reservation Store. The reservation uses a compare-and-swap or conditional update, so competing requests cannot reserve the same budget.

Throttle / Bid Multiplier Logic then decides whether to allow or throttle the request and returns a pacing factor to the Ad Server / Auction Service. The key rule is to reserve before serving.

4. Record delivery in the background

If the ad is served, the Ad Server / Auction Service sends an impression, click, or spend event to the Event Stream / Queue. The Delivery Event Consumer / Aggregator consumes these events at least once.

It removes duplicate effects, updates the Real-Time Counters Store, and commits or releases the reservation in the Budget Ledger / Reservation Store. This background path keeps event processing away from the main pacing response.

5. Handle traffic changes and operational errors

When traffic changes, the Pace Controller adjusts throttling using recent forecasts and guardrails. If the forecast becomes stale, the diagram uses conservative throttling instead of taking more spending risk.

The scheduled Reconciliation Job compares billing or event data with the counters and ledger. It detects drift, corrects or backfills data, and raises alerts.

Observability collects metrics, logs, traces, and alerts. It tracks delivery rate, pacing error, overspend risk, consumer lag, reservation failures, and forecast drift. The main trade-off is that counters may have small short-term error, but overspending is not acceptable.

Engineering Considerations / Design Trade-offs

The benefit is that the main pacing path stays fast because it uses a local Config Cache, Real-Time Counters Store, and traffic forecast. The downside is that the counters may be slightly behind the exact committed spend. We accept that small pacing error because the Budget Ledger / Reservation Store protects the budget with an atomic reservation before serving. Forecasts can also become stale when traffic changes quickly. The system then uses conservative throttling. Background event processing keeps the request path fast, but consumer lag can delay counter updates. The Reconciliation Job later compares records and fixes drift.

Why Interviewers Ask This

Interviewers ask this question to see whether you can balance speed with spending correctness. They want to know if you can separate campaign setup, real-time decisions, and background processing. They also test whether you understand concurrency, atomic budget reservation, fast approximate counters, changing traffic, and operational monitoring. The important skill is explaining why each part exists and what trade-off it creates.

Interviewer may ask next
What would you change if traffic suddenly becomes much higher than the forecast for several minutes?

I would keep the same design, but the Pace Controller would react to the newer traffic forecast and recent delivery counters. It already uses remaining budget, remaining time, current spend, and the Forecast / Traffic Model. When traffic becomes much higher, it can lower the pacing factor or throttle more requests so the campaign does not spend too quickly.

The Budget Ledger / Reservation Store would still protect correctness. Every allowed request must reserve budget before the ad is served. Concurrent replicas therefore cannot spend the same remaining budget even while traffic is changing quickly.

I would watch pacing error, overspend risk, and forecast drift through Observability. If the forecast becomes stale, the existing fallback is conservative throttling.

The downside is that the campaign may deliver fewer ads than necessary for a short time. That is acceptable because the diagram treats small short-term pacing error as safer than overspending.

How would the system handle duplicate delivery events or a consumer retry after a failure?

I would keep the same Event Stream / Queue and Delivery Event Consumer / Aggregator. The diagram shows at-least-once consumption, so the same delivery event can be processed again after a retry. The consumer therefore removes duplicate effects before updating spend or counters.

For each impression, click, or spend event, the consumer updates the Real-Time Counters Store and commits or releases the related reservation in the Budget Ledger / Reservation Store. Processing the same event again must not add the same spend twice or release the same reservation twice.

The scheduled Reconciliation Job gives us another safety check. It compares billing or event data with the counters and ledger. It can detect drift, correct or backfill data, and raise alerts. Observability also tracks consumer lag and reservation failures.

The downside is more bookkeeping in the consumer. We accept that because duplicate delivery is possible with at-least-once event processing.

14. How would you design publisher configuration rules?System DesignHardNetflix

Question Details

Design a supply-side configuration system that manages publisher-specific rules across many properties. Cover rule storage, validation, updates, propagation, rollback, and production safety.

Short Interview Answer (30-60 seconds)

At a high level, this system lets publisher teams change configuration rules safely without slowing down serving traffic. The main challenge is making rule updates correct while keeping rule reads fast across many JVMs. I would explain it in three flows: creating and publishing a version, spreading that version to caches and serving services, and rolling back safely. The design uses versioned rules, immutable snapshots, an active-version pointer, and local caches. The trade-off is that serving JVMs may switch versions at slightly different times during background propagation.

Detailed Explanation

The system lets publisher teams create rules for many properties, review them, publish a safe version, and quickly undo a bad change. The hard part is keeping rule changes controlled while serving systems need very fast reads. Many Java JVMs must move to the correct version without stopping traffic. The diagram solves this with a controlled write path, a background propagation path, a fast cached read path, and a rollback path.

Useful Questions to Ask the Interviewer
  1. Can one publisher have many properties with different rules?
  2. How quickly must a published version reach serving services?
  3. Is a short period with different versions across JVMs acceptable?
  4. Do rule changes require approval before publishing?
  5. How long should old versions remain available for rollback?
How would you design publisher configuration rules? diagram
How to Explain It in an Interview
1. Start with a controlled write path

For the write path, the Publisher Operations Admin UI sends an HTTPS create or update request to the API Gateway / Edge. The gateway handles authentication, RBAC authorization, and rate limiting. It then sends the request to the Config Management Service Cluster, which runs as Java 21/25 stateless replicas in separate JVMs.

The Rule Validator checks schema, business rules, conflicts, and property ownership. A valid change is saved as a draft in the Versioned Rule Store. The Version Manager tracks versions, concurrency control, metadata, and publisher/property scope.

2. Publish a version safely

The Publish Controller coordinates publishing. The Rule Snapshot Builder computes the effective rule set for that publisher and property, including inheritance, defaults, overrides, and constraints.

The snapshot is immutable, meaning it is not changed after publishing. Its metadata is saved, and active_version moves to the selected version. The Publish Controller writes an append-only Audit Log / Change History record and sends a ConfigVersionPublished event to the Event Bus.

3. Propagate the version in the background

The Event Bus sends the version event to Propagation Workers in separate Java consumer JVMs. They warm or invalidate the Distributed Config Cache and notify serving systems to refresh. They retry failed refresh work with backoff.

This work is asynchronous, meaning publishing does not wait for every serving JVM. The downside is that JVMs may briefly use different versions.

4. Keep serving reads fast

Serving / Decisioning Services keep effective rules in a Local In-Memory Cache keyed by publisher, property, and version. This is the normal fast path.

When a serving JVM needs a snapshot that is not local, it asks the Runtime Config Read Service. That service checks the Distributed Config Cache first. On a miss, it reads the active snapshot from the Versioned Rule Store. It returns the rules with the version, and the serving JVM refreshes its local cache.

5. Roll back safely and watch production

For rollback, the operator selects a known-good version. The system resets active_version and republishes the version event. Caches and serving JVMs refresh back to that version. If refresh fails, serving keeps the last known-good configuration.

The write-side version activation is strongly consistent. Background propagation is eventually consistent, meaning some JVMs can lag briefly. Metrics, logs, tracing, and alerts watch publish failures, propagation lag, cache misses, error rates, and store failures.

Engineering Considerations / Design Trade-offs

The benefit is that published versions never change, so rollback is simple and safe. The active_version pointer lets the system switch which version is live without deleting older versions. Local memory keeps serving reads fast, while the Distributed Config Cache reduces load on the Versioned Rule Store. The downside is that refresh happens in the background. Some serving JVMs may use an older version for a short time after a publish. We accept that because write-side activation stays strongly consistent. The last known-good configuration also protects serving when propagation fails, while RBAC, audit history, and alerts make production changes easier to control.

Why Interviewers Ask This

The interviewer wants to see whether you can separate a safe control path from a fast serving path. They also want to see how you handle versioning, validation, caching, background updates, rollback, and failures. A strong answer shows good judgment about where correctness matters most, where a short delay is acceptable, and how to protect production without making every serving request depend on the main database.

Interviewer may ask next
What would you change if every serving JVM had to use a newly published configuration immediately?

I would keep the same basic design, but I would stop treating background propagation as enough for correctness. The current design lets serving JVMs refresh at slightly different times, so it cannot promise an exact global cutover by itself.

I would make the Runtime Config Read Service verify the active_version before a serving JVM accepts a newly required configuration. A JVM with an older local version would fetch the selected snapshot from the Distributed Config Cache or the Versioned Rule Store, verify the version, and then replace its Local In-Memory Cache. Until that succeeds, it would keep the last known-good snapshot rather than load a partial update.

If the requirement truly means every JVM must switch at the exact same instant, I would tell the interviewer that the current architecture does not provide that guarantee. It would need extra coordination beyond the diagram.

The downside is higher read-path cost and more coordination, which reduces the independence and speed of local caching.

How would the design behave if the Event Bus or Propagation Workers were temporarily unavailable?

I would keep serving traffic on the last known-good configuration. The Event Bus and Propagation Workers are the background path for spreading a published version, so their failure should not make the serving path stop.

The Publish Controller can still create the published version, update active_version, and write the Audit Log / Change History record. Serving / Decisioning Services continue using their Local In-Memory Cache. When the Propagation Workers recover, they retry with backoff, warm or invalidate the Distributed Config Cache, and notify serving JVMs to refresh.

A serving JVM that needs rules before propagation catches up can use the Runtime Config Read Service. That service checks the Distributed Config Cache and falls back to the Versioned Rule Store on a cache miss.

Metrics and alerts should show publish failures, propagation lag, cache misses, and stale behavior. The downside is that some JVMs can remain on the older version longer than normal until background propagation recovers.

15. How would you design demand-side ads relational tables?System DesignHardNetflix

Question Details

Design a normalized relational schema for a demand-side advertising system. Cover tables, keys, relationships, indexing, joins, query patterns, and how the schema would evolve safely.

Short Interview Answer (30-60 seconds)

At a high level, the goal is to keep demand-side ad data organized and easy to query. The main challenge is supporting management writes, fast eligibility joins, and reporting without mixing everything into one large table. I would explain three flows: campaign management, ad eligibility, and reporting. The schema stays normalized with bridge tables for many-to-many targeting. Event facts are stored separately for reporting and backfill work. The trade-off is more joins, but we gain clearer data rules and safer schema changes.

Detailed Explanation

The goal is to store advertiser, campaign, targeting, creative, and ad-event data in a clean structure. The difficult part is that the same data supports different jobs. Campaign managers change configuration. Ad serving must quickly find eligible ads. Reporting must process many impression, click, and conversion records. The diagram handles this by keeping management data normalized, using bridge tables for targeting relationships, and keeping reporting events in separate fact tables. The Java service then uses different query paths for each job.

Useful Questions to Ask the Interviewer
  1. Which eligibility filters are used most often during ad serving?
  2. How much reporting history must remain available?
  3. Can campaign and targeting changes take effect immediately?
How would you design demand-side ads relational tables? diagram
How to Explain It in an Interview
1. Start with the core relational model

I would first model the business hierarchy clearly. An advertiser has many campaigns, and each campaign has many line items. A line item contains bidding, budget, date, status, and version fields.

Creatives belong to advertisers. A creative can have many creative assets. A line item can use many creatives, so line_item_creative connects them with a composite primary key.

This keeps repeated data out of the main tables and gives each table one clear job.

2. Model targeting with bridge tables

For targeting, one line item may match many audience segments, geographies, device types, inventory sources, or deals. Each of those values may also be used by many line items.

I would therefore use bridge tables such as line_item_segment_target and line_item_geo_target. Each bridge uses both IDs as its primary key. A reverse index, such as (segment_id, line_item_id), also helps queries that start from the target value.

inventory_source has many deals, so deal stores its inventory_source_id foreign key.

3. Explain the management and eligibility query paths

For campaign management, the Campaign Manager UI sends an HTTPS request to the Java 21/25 DSP Service. Campaign Management runs SQL INSERT, UPDATE, and SELECT operations across advertiser, campaign, line item, creative, and targeting tables. The service then returns the HTTPS response.

For ad serving, the Ad Serving / Eligibility API sends an HTTPS request to Targeting & Eligibility Query. That path filters active line items by status, dates, and budget. It then intersects the targeting bridge tables and joins through line_item_creative to find creatives. The service returns the eligible ads response.

Indexes on campaign and line-item status and date fields make these common filters cheaper.

4. Keep reporting events separate

I would keep high-volume event records outside the normalized management tables. impression_event records the line item, creative, request, event time, and spend. click_event and conversion_event reference the impression.

Reporting / Backfill Jobs submit asynchronous jobs to the service and receive job status or results. Inside the service, Reporting / Backfill Jobs run aggregate SQL over the event tables and join back to line items and campaigns. This background path does not block the normal eligibility response.

The impression table can be partitioned by event date and indexed by (line_item_id, event_time). Its (request_id, line_item_id) pair is unique. Click and conversion tables are indexed by impression_id.

5. Explain correctness and safe evolution

Foreign keys protect relationships. NOT NULL, UNIQUE, and CHECK rules prevent invalid rows. Status values use controlled checks, time values use UTC, and money uses minor units such as cents.

The version field on line_item supports optimistic locking. This means an update can detect that another request changed the same row first. Threads or virtual threads inside one JVM may share mutable state, so that shared state must be thread-safe. Separate JVM replicas do not share the same heap.

For schema changes, I would use expand, dual write or backfill, switch reads, and then contract. I would also prefer status-based soft deletes instead of destructive deletes when audit history matters.

Engineering Considerations / Design Trade-offs

The benefit is that the data stays clean and each relationship has a clear place. Bridge tables make many-to-many targeting correct and flexible. Separate event tables also keep reporting data away from normal campaign configuration. The downside is that ad eligibility needs several joins. Good indexes are important because those joins happen on the serving path. Optimistic locking protects concurrent line-item updates, but callers must handle a rejected update. Safe schema changes also take more steps because old and new application versions may run together during a migration.

Why Interviewers Ask This

Interviewers use this question to see whether you can turn a real advertising problem into a clean relational model. They want to see how you handle one-to-many and many-to-many relationships, choose useful indexes, separate serving data from reporting facts, and protect concurrent updates. They also want to know whether you can explain safe database changes without breaking running Java service replicas.

Interviewer may ask next
How would you change the design if eligibility queries became much slower as the number of targeting rows grew?

I would keep the same relational model, but I would first improve the indexes and query order used by Targeting & Eligibility Query. The main goal is to reduce how many bridge-table rows must be examined before joining creatives.

I would check that every targeting bridge keeps its composite primary key and its reverse index, such as (segment_id, line_item_id). I would also keep the campaign and line-item indexes that support status and date filtering. The service should remove inactive or out-of-date line items early, before doing more expensive targeting intersections.

I would inspect the database query plan to confirm which indexes are actually used. I would not replace the normalized schema unless measurements show that indexes and query changes are not enough.

The downside is that extra indexes use more storage and make campaign or targeting writes slightly more expensive.

How would you safely add a new targeting dimension while several Java service replicas are still running?

I would use the same safe schema-evolution pattern shown in the diagram. First, I would expand the schema by adding the new dimension table and its line-item bridge table without removing anything existing.

Next, the Java service can start writing the new data while old replicas still work with the previous schema. If existing rows need the new relationship, Reporting / Backfill Jobs can fill that data in the background. Once the new code and data are ready, newer replicas can switch their eligibility reads to include the new targeting bridge.

Only after every active replica no longer depends on the old form would I remove obsolete columns or structures. That final step is the contract phase.

The benefit is safer mixed-version deployment. The downside is that the migration takes longer and may temporarily require dual writes and extra storage.

16. How would you model direct-sold DSP orders?System DesignMediumNetflix

Question Details

Design a data model for tracking direct-sold demand-side platform orders. Cover entities, relationships, state transitions, updates, and how the model supports reporting and downstream workflows.

Short Interview Answer (30-60 seconds)

At a high level, the goal is to keep direct-sold DSP orders correct while many users and downstream systems depend on them. The main challenge is that order updates must be safe, while activation, billing, notifications, and reporting should not slow the user request. I would explain three flows: the synchronous create or update path, the order lifecycle, and the background event path. The relational Orders DB is the source of truth. The trade-off is extra event, audit, and retry machinery.

Detailed Explanation

The system must store direct-sold advertising orders and keep every change correct. Each order belongs to an advertiser, optional agency, and sales contract. An order can contain many line items with budgets, schedules, pricing, targeting, inventory, and creatives. The hard part is that people may edit orders while several downstream systems need those changes. The diagram keeps the core write controlled, then sends background events after a successful commit.

Useful Questions to Ask the Interviewer
  1. Can several users edit the same order at the same time?
  2. Which state changes need approval?
  3. How quickly must DSP activation receive an order change?
  4. Can reporting appear a little later?
How would you model direct-sold DSP orders? diagram
How to Explain It in an Interview
1. Start with the DSP Order aggregate

I would make DSP Order the aggregate root, meaning it controls the main order rules.

Advertiser, Agency, and Sales Contract or Insertion Order can each relate to many DSP Orders. Each order has many Line Items or Flights. A line item connects to Budget, Schedule or Flight Dates, Pricing, Pacing Rules, Targeting Profile, Inventory or Deal, and Creative Assignment. Creative Assignment connects to one or more Creatives.

2. Explain the synchronous create and update path

For the write path, Sales Ops uses the Internal Order UI. The request crosses the API, authentication, authorization, and validation boundary. It also checks an idempotency key, which helps recognize the same request sent again.

The request enters the Java 21/25 Order Service inside the JVM. The Business Rules and State Transition Validator checks limits and state changes. The Order Aggregate Manager loads the order, applies the change, and checks the version.

The service saves the core change in one ACID transaction, so important database changes succeed or fail together. The transactional Orders DB is the source of truth. After the save succeeds, the service returns the updated order and status.

3. Protect updates and lifecycle changes

I would use optimistic locking with the version field. If another user saved a newer version first, the stale update is rejected instead of overwriting newer data.

The lifecycle is Draft, Pending Approval, Approved, Scheduled, Active, Paused, and Completed. Rejected and Cancelled are side outcomes. Every allowed state change writes Status History and an Audit Log or Version Record.

4. Send downstream work through events

After a successful commit, the Outbox or Change Events flow sends OrderCreated, OrderUpdated, StatusChanged, and OrderCancelled events to the Event Bus or Queue.

Consumers handle Trafficking or DSP Activation Sync, Inventory Forecasting or Availability, Billing and Finance, Notifications or Approval Workflow, and Reporting Ingestion. They are idempotent, meaning a repeated event should not repeat the business action.

Failed messages use retry and backoff. Messages that still fail can move to the DLQ for safe reprocessing.

5. Separate reporting and operational work

A Stream Processor or ETL flow builds Analytics Warehouse or Reporting Facts from events. These records are denormalized, meaning useful values are stored together for faster analysis.

Reports use advertiser, contract, order, line item, creative, and date. Metrics include budget, spend, impressions, clicks, conversions, CTR, viewability, and pacing.

Read Models or Materialized Views support dashboards without slowing writes. Soft-delete or retention rules keep history. The trade-off is more background infrastructure, but core order writes stay controlled.

Engineering Considerations / Design Trade-offs

The benefit is that the transactional Orders DB keeps the main order data in one clear source of truth. ACID writes and version checks help prevent partial saves and lost updates. The downside is more background machinery. The event path needs an outbox, a queue, retry rules, and a DLQ for failures. Reporting also uses a separate warehouse and denormalized facts, so reports can appear a little later than the core order. Audit history and retention use extra storage. Read models make dashboards faster, but they create another copy of data that must be kept up to date.

Why Interviewers Ask This

The interviewer wants to see whether you can turn a real business workflow into a clean data model. They also want to see how you handle relationships, state changes, concurrent edits, audit history, background events, and reporting. A strong answer shows good judgment about what belongs in the core transaction and what can happen later. It also shows that you can explain failure handling and trade-offs without making the design unnecessarily complex.

Interviewer may ask next
What would you change if several account managers often edit the same DSP order at the same time?

I would keep the same design, but I would make the version check very clear to the user. Every DSP Order already has a version field. The Internal Order UI sends the version it last read with each update.

The Order Aggregate Manager compares that version with the current version in the transactional Orders DB. If they match, the service validates the change and saves the next version in the same ACID transaction. If they do not match, another user has already changed the order. The service rejects the stale update instead of silently overwriting newer data.

The UI can then load the latest order and show the user the new values. The user reviews them and submits the update again. Status History and the Audit Log still record only successful changes. Downstream events are also sent only after a successful commit.

The downside is that users may need to retry during busy editing periods. The benefit is that we avoid silent lost updates.

How would you handle a failure when the order is saved but the DSP activation system is temporarily unavailable?

I would keep the order saved because the transactional Orders DB is the source of truth. The core write has already succeeded, so a temporary activation failure should not undo that business change.

After the successful commit, the Outbox or Change Events flow sends the event to the Event Bus or Queue. The Trafficking or DSP Activation Sync consumer tries to process it. If that downstream system is unavailable, the consumer retries with backoff, which means it waits longer between repeated attempts.

The consumer should be idempotent. If the same event arrives again, it should not repeat the activation action. If repeated attempts still fail, the message moves to the DLQ for inspection and safe reprocessing.

Metrics, logs, traces, and alerts make the failure visible. The saved order remains correct while activation catches up later.

The downside is that the DSP can temporarily be behind the latest saved order state.

17. How is HttpSession implemented when HTTP is stateless?API DesignMediumNetflix

Question Details

Explain how session state can be supported on top of a stateless HTTP protocol, including where state is stored and how requests are correlated.

Short Interview Answer (30-60 seconds)

At a high level, HTTP stays stateless, but the Java application adds session continuity on top of it. On the first request, the browser has no session cookie. If the application needs a session, the Session Manager creates an HttpSession, generates a session ID, and stores the session data on the server. The response sends JSESSIONID=abc123 back in a cookie. Later requests send that ID, so the server can find the same HttpSession. The trade-off is managing server-side state, especially when separate JVM replicas need shared session data.

Detailed Explanation

The problem is that each web request normally stands alone. The server does not automatically remember what happened before. But many applications need to remember things such as a user, shopping cart, or temporary security value. The goal is to connect later requests to the same stored information without changing how HTTP itself works. In this design, the browser carries a small identifier. The Java application uses that identifier to find the real session data stored on the server. I would explain the flow in the same order shown in the diagram.

Useful Questions to Ask the Interviewer
  • Should I assume a normal Servlet Container using HttpSession?
  • Should I also explain what changes when the application runs on multiple JVM replicas?
  • Should I cover the cookie-disabled URL-rewriting fallback shown in the design?
How is HttpSession implemented when HTTP is stateless? diagram
How to Explain It in an Interview
1. Start with the stateless HTTP idea

I would start by saying that HTTP itself is stateless. Each request is independent. The protocol does not remember an earlier request for us.

The Java application adds session continuity above HTTP. It does this by giving the browser a session identifier. That identifier lets the server connect later requests to stored session data.

The browser normally stores the JSESSIONID cookie. The real HttpSession data stays on the server.

2. Handle the first request

The browser first sends an HTTP request with no session cookie. The request enters the Java Web Application inside the Servlet Container. The Servlet Filter or Controller receives and processes it.

If the application needs session state, the request handling code creates or obtains an HttpSession. The Session Manager owns the session lifecycle and correlation. It generates a session identifier such as abc123 and creates the new session state.

This keeps the HTTP request itself stateless while adding application-level continuity.

3. Store the session state on the server

The Session Manager stores the new session in the server-side Session Store. The diagram describes this store as holding HttpSession objects and attributes.

Example attributes include cart, userId, csrfToken, and lastAccessTime. The store can be server memory or persistent storage, as shown in the diagram.

The key point is that JSESSIONID identifies the session. It is not the session data itself.

4. Return the session ID to the browser

After processing the request, the Java application creates the response. The response returns to the browser with Set-Cookie: JSESSIONID=abc123.

The browser stores that cookie. This gives later requests a way to tell the server which session they belong to.

The request and response remain separate HTTP messages. Session continuity comes from correlation using the identifier.

5. Correlate later requests

On a later request, the browser sends Cookie: JSESSIONID=abc123. The request again enters the Java Web Application.

The Session Manager uses abc123 to look up the matching session in the server-side Session Store. The store returns the session data. The HttpSession and its attributes become available to the request handling code.

The application then creates and returns another HTTP response. The same session continues because the next request carried the same valid session identifier.

6. Explain timeout, fallback, and clustered deployment

A session ends when it times out or is invalidated. A later request without a valid session can cause a new session to be created and a new identifier to be issued.

If cookies are disabled, the diagram shows URL rewriting as a fallback. The session ID can be carried in the URL, for example ;jsessionid=....

For clustered deployment, separate JVM replicas do not automatically share heap state. The diagram therefore shows replication or a shared distributed store. Redis is the example shared Session Store. That lets separate replicas access the same session data while keeping the browser-side correlation model unchanged.

Why Interviewers Ask This

Interviewers ask this to check whether you understand that HTTP and application sessions are different concepts. They want to see if you know where HttpSession data lives and how JSESSIONID connects later requests to that server-side state. They also check request and response direction, session lifecycle, timeout or invalidation behavior, and what changes across separate JVM replicas. A strong answer shows clear state-management reasoning without claiming that HTTP itself becomes stateful.

Interviewer may ask next
What changes if the Java application runs on multiple JVM replicas?

The session storage strategy must change so every replica can reach the needed session data. The browser can keep sending the same JSESSIONID, and the Servlet Filter or Controller can keep using the same request flow. The affected part is the Session Manager and server-side Session Store.

Separate JVM replicas do not automatically share heap state. If the session exists only in one replica's local memory, another replica may not have it. The diagram shows two valid approaches: replicate the session data or place it in a shared distributed store. Redis is the shown example.

The Session Manager still uses JSESSIONID=abc123 to correlate the request. It then loads the matching HttpSession data from the shared or replicated storage. The response path does not change.

The benefit is that session continuity can work across replicas. The downside is extra storage and operational complexity compared with keeping the session only in one JVM.

What happens when the session times out, is invalidated, or cookies are disabled?

The old HttpSession can no longer continue after timeout or invalidation. When a later request needs session state again, the Session Manager creates a new HttpSession, generates a new session identifier, stores the new server-side state, and returns the new identifier to the browser.

The normal flow still uses the JSESSIONID cookie. A later request sends Cookie: JSESSIONID=..., and the Session Manager uses that value to find the matching session. If there is no valid session, the old state is not reused.

If cookies are disabled, the diagram shows URL rewriting as the fallback. The session identifier can be carried in the URL, for example ;jsessionid=.... The Session Manager still uses that identifier for correlation with the server-side session state.

The benefit is that session correlation can still work without cookies. The downside is that the application must support a separate URL-based correlation path instead of relying only on the normal cookie flow.

18. What is String.intern()?API DesignEasyNetflix

Question Details

Explain what string interning does in Java, when it is useful, and the practical tradeoffs for memory and performance.

Short Interview Answer (30-60 seconds)

At a high level, String.intern() returns the canonical pooled String for equal text. In this example, the literal "netflix" is already in the String Pool, while new String("netflix") creates a separate heap object. Calling b.intern() looks for equal content in the pool and returns that canonical reference as c. If no matching entry exists, Java makes that value the canonical pooled entry. This can reduce duplicate String objects, but each lookup adds work. I still use equals() for content comparison because == only checks object identity.

Detailed Explanation

This question asks how Java can avoid keeping many separate copies of the same text. The goal is to understand when one shared copy can be reused, what happens when the program asks for that shared copy, and when this saves memory. The main challenge is that two pieces of text can look identical but still be stored as different objects. I would explain the simple example in the diagram first, then show how Java finds the shared version, returns it, and what performance cost comes with doing that.

Useful Questions to Ask the Interviewer
  • Are we discussing normal Java String.intern() behavior inside one JVM?
  • Should I focus mainly on memory savings, performance cost, or both?
What is String.intern()? diagram
How to Explain It in an Interview
1. Start with the two String objects

I would begin with the example in the diagram. String a = "netflix"; uses a string literal. That literal is already stored as the canonical "netflix" value in the String Pool.

The next line is String b = new String("netflix");. This creates a separate String object on the JVM heap. Its text is still "netflix", but b refers to a different object from the pooled literal referenced by a.

2. Explain the String Pool

The String Pool holds canonical strings. A canonical string is the shared pooled reference Java uses for equal string content.

In the diagram, "netflix" is already present because the application used the literal before calling intern(). The important idea is that equal text can share one pooled instance even when other separate String objects with the same text also exist.

This pool belongs to the JVM shown in the diagram. Separate JVM processes do not share this same in-memory pool.

3. Trace the intern() lookup

Next, I would explain String c = b.intern();. The application calls intern() on the separate heap String object referenced by b.

Java looks in the String Pool for an equal canonical entry. The diagram shows this as the decision "existing canonical entry?" If the answer is yes, Java uses that existing entry. If the answer is no, that string value becomes a canonical entry in the pool.

The canonical pooled reference is then returned to the application as c.

4. Apply the flow to this example

For this exact example, the pool already contains the literal "netflix". Therefore, b.intern() finds that existing canonical entry and returns its reference.

That means a and c refer to the same pooled String object. The variable b still refers to the separate heap String object created by new String("netflix").

All three variables represent equal text, but they do not all have the same object identity.

5. Explain when interning is useful

Interning is useful when an application repeatedly handles the same immutable text values. The diagram gives examples such as parsed tokens, configuration keys, and many duplicate identifiers.

The benefit is that many references can share one canonical pooled String instead of keeping many equivalent String objects. This can reduce duplicate objects and save memory when repeated values are common.

6. Explain the performance trade-off and comparison rule

The trade-off is that intern() performs a pool lookup. Heavy interning can also increase pool and heap pressure. If most strings are unique, the program may pay the lookup cost without getting much memory reuse.

I would therefore use interning selectively when duplication is common and measurable memory savings matter.

Finally, I use equals() for normal String content comparison. The == operator only checks whether two references point to the same object. Pooled strings can make == true in some cases, but that is object identity, not a general content comparison rule.

Why Interviewers Ask This

Interviewers ask this to check whether you understand String content, object identity, and the purpose of the String Pool. They want more than a definition of intern(). A strong answer traces the lookup and returned reference, explains why new String() creates a separate object, and knows when pooling may save memory. It also shows judgment by discussing lookup cost, mostly unique values, and why equals() is the normal choice for comparing String content.

Interviewer may ask next
What would you change if the application receives millions of mostly unique strings?

I would avoid calling intern() on every value. The String Pool still works the same way, but I would make the application selective about which strings enter the intern() lookup flow. Interning is most useful when many values repeat because multiple references can then share one canonical pooled String. If millions of strings are mostly unique, each call adds lookup work while providing little reuse. Those values can also increase pool and heap pressure. I would keep interning for stable, highly repeated values such as common configuration keys, parsed tokens, or identifiers that measurements show are duplicated frequently. Mostly unique application data would remain normal String objects. Correctness would not change because normal content comparisons would still use equals(). The downside of this selective approach is that some duplicate strings may remain separate objects, but that is usually preferable to paying the lookup and pooling cost for values that rarely repeat.

In the diagram, how do a, b, and c compare with equals() and ==?

All three contain the text "netflix", so their equals() comparisons are true. Their object identity is different. The variable a comes from the string literal, so it refers to the canonical pooled "netflix" String. The variable b comes from new String("netflix"), so it refers to a separate heap String object. When c = b.intern() runs, the String Pool already has the canonical "netflix" entry. The lookup therefore returns that existing pooled reference as c. In this exact example, a == c is true because a and c point to the same pooled object. Both a == b and b == c are false because b points to the separate object created with new String(). The important rule remains unchanged: use equals() for normal String content comparison. Use == only when object identity is specifically what you want to test.

19. What is the Spring framework?API DesignMediumNetflix

Question Details

Explain what Spring provides to Java developers, including how its application model, dependency injection, and web abstractions help structure APIs and services.

Short Interview Answer (30-60 seconds)

At a high level, Spring helps Java developers build APIs and services with clear responsibilities. In this design, an HTTPS JSON request enters Spring Web through the DispatcherServlet, then moves to a REST Controller, Service, Repository, and Database. The response returns through those layers in reverse order. The ApplicationContext creates application objects and injects their dependencies, which reduces tight coupling. Spring Web also simplifies HTTP request handling. No separate security or failure mechanism is shown. The main trade-off is extra framework structure in exchange for clearer, more testable code.

Detailed Explanation

This question asks what Spring gives a Java developer when building an API or service. The goal is to keep different jobs separated. One part handles web requests. Another handles business rules. Another handles stored data. Spring also connects these parts instead of making each part create its own dependencies. The diagram shows how a request enters the Java application, moves through these responsibilities, reaches the database, and returns to the client. It also shows why this structure makes application code easier to organize, reuse, and test.

Useful Questions to Ask the Interviewer
  • Should I focus on Spring's core application model and dependency injection?
  • Should I also trace the Spring Web request and response flow?
  • Do you want only the high-level framework concepts, or implementation details too?
What is the Spring framework? diagram
How to Explain It in an Interview
1. Start with what Spring provides

I would start by saying that Spring is a Java framework for organizing application code and connecting application components. The diagram shows one Java application using Spring Framework. Its main responsibilities are separated into Spring Web, a REST Controller, a Service, and a Repository. Spring also provides the ApplicationContext, which is its IoC container. IoC means inversion of control. In simple terms, Spring creates application objects and provides their dependencies instead of making every object construct everything itself.

2. Explain how the web request enters

The request first comes from the Client or API Consumer. It sends an HTTPS API request containing JSON to Spring Web and the DispatcherServlet. The diagram shows Spring Web handling routing, binding, and validation around this request-processing step. The DispatcherServlet dispatches the request toward the REST Controller. Spring Web also provides abstractions for request mapping and HTTP handling. These abstractions remove repetitive web plumbing from application code.

3. Show the controller and service responsibilities

Spring Web dispatches the request to the REST Controller. The controller maps HTTP endpoints. It then calls the Service. The Service owns the business logic. Keeping these responsibilities separate prevents HTTP concerns from becoming mixed with business rules. The diagram represents this application model with annotations such as @RestController and @Service. The ApplicationContext creates these managed components and injects their dependencies.

4. Explain repository and database access

The Service sends a query or persist operation to the Repository. The Repository provides the data access abstraction. It keeps database access concerns separate from business logic. The Repository then performs SQL or data access against the Database. The Database returns a data result to the Repository. The diagram also uses @Repository for this layer. The ApplicationContext manages the Repository as another Spring bean.

5. Trace the complete response path

The response returns through the responsibilities in reverse order. The Database sends the data result back to the Repository. The Repository returns a domain result to the Service. The Service returns a domain result to the REST Controller. The controller returns the response body to Spring Web and the DispatcherServlet. Finally, Spring Web sends the HTTP JSON response back to the Client or API Consumer. Request and response are therefore separate one-way flows.

6. Explain dependency injection and the application model

The ApplicationContext creates beans, manages their lifecycle, and injects dependencies. A bean is simply an object managed by Spring. Instead of application classes manually constructing every dependency, Spring provides the required managed objects. This reduces tight coupling. The diagram highlights reusable, testable layers built around @RestController, @Service, and @Repository. The benefit is clearer responsibilities and easier isolated testing.

7. End with the main trade-off

The benefit is structure, loose coupling, and easier testing. Spring Web removes repetitive HTTP handling, while dependency injection removes much manual object wiring. The downside is another layer of framework abstraction. Developers must understand concepts such as the ApplicationContext, beans, annotations, and request dispatching. This diagram does not show authentication, authorization, retries, caching, or special failure handling, so I would not add those claims. The extra framework structure is useful because it keeps larger Java services easier to organize and maintain.

Practical Complexity & Trade-offs

The main design choice is separation of responsibilities. Spring Web handles the HTTP-facing work. The REST Controller maps endpoints. The Service contains business logic. The Repository separates data access from that logic. The ApplicationContext creates managed objects and injects their dependencies. The benefit is loose coupling, clearer code, and easier testing. The downside is extra framework structure and some wiring that happens through Spring instead of direct object construction. The request also passes through several layers before reaching the Database. This design does not show authentication, authorization, retries, caching, or special failure handling, so those should not be claimed as part of this diagram. We accept the framework abstraction because it reduces repetitive code and makes responsibilities easier to maintain.

Why Interviewers Ask This

Interviewers ask this question to see whether a candidate understands Spring as more than a set of annotations. They want to hear how Spring structures a Java API, how dependency injection reduces tight coupling, and how Spring Web supports HTTP request handling. They also check whether the candidate can trace request and response directions correctly through the controller, service, repository, and database while explaining why those responsibilities are separated.

Interviewer may ask next
What would change if the application became much larger and had many services and repositories?

I would keep the same Spring application model, but I would make the boundaries between responsibilities more disciplined. Spring Web would still receive the HTTPS request through the DispatcherServlet. Controllers would still map HTTP endpoints. Services would still contain business logic, and repositories would still isolate data access. The ApplicationContext would continue creating beans and injecting their dependencies. The main change would be organizing related controllers, services, and repositories so dependencies do not become tangled. I would also avoid very large service classes that own unrelated business rules. The request and response flow would remain the same, so correctness is preserved: client to Spring Web, controller, service, repository, database, then back in reverse. No security mechanism is shown in the original design, so I would not invent one for this follow-up. The downside is that a larger application has more beans and relationships to understand. The benefit is that Spring's application model still gives those relationships a consistent structure.

Why use dependency injection instead of creating the Service and Repository objects directly inside other classes?

I would use dependency injection because it keeps application components less tightly connected. In the diagram, the ApplicationContext creates the REST Controller, Service, and Repository and injects dependencies into managed beans. That means application classes do not need to own all object-construction logic themselves. The request flow does not change. Spring Web still dispatches the request to the controller. The controller calls the service. The service queries or persists through the repository, and the database result returns through the same layers. The difference is object ownership and wiring. Spring owns that work through the ApplicationContext. This makes components easier to test because a different dependency can be supplied without changing the component's business logic. It also keeps construction concerns separate from application responsibilities. No separate security mechanism is shown, so the security behavior remains unchanged rather than being invented here. The downside is that dependency relationships can feel less explicit because Spring performs the wiring instead of direct manual construction.

20. Explain wait and notify.API DesignMediumNetflix

Question Details

Explain how wait and notify coordinate threads, what state changes they depend on, and the main correctness pitfalls when using them.

Short Interview Answer (30-60 seconds)

At a high level, wait and notify let Java threads coordinate around shared state protected by the same monitor. Thread A enters synchronized, checks a condition in a while loop, and calls wait() when the condition is false. wait() releases that monitor and puts Thread A into WAITING. Thread B later acquires the same monitor, changes the shared state, and calls notify() or notifyAll(). The notified thread must still reacquire the monitor before wait() returns. notify() wakes one waiter with less wake-up overhead, while notifyAll() is safer when several waiters or conditions exist.

Detailed Explanation

This question is about helping two threads take turns safely when they share the same data. One thread may need to stop because the data is not ready yet. Another thread later changes that data and tells the first thread that it should check again. The important goal is to avoid using the shared data at the wrong time. The diagram shows one waiting thread, one notifying thread, one shared object, and the order in which ownership of that object changes.

Useful Questions to Ask the Interviewer
  • Should I explain both notify() and notifyAll()?
  • Should I focus on one JVM and one shared monitor object?
  • Do you want the thread state changes explained as well?
Explain wait and notify. diagram
How to Explain It in an Interview
1. Start with the shared monitor and condition

I would start by saying both threads coordinate through the same monitor object. The monitor protects the shared state, such as a ready flag or a queue. A thread must enter synchronized(monitor) before it can safely inspect or change that state. Only one thread owns that monitor at a time. The condition is the real reason a thread waits. wait() and notify() are coordination tools around that condition.

2. Thread A checks the condition and waits

Thread A first enters synchronized(monitor) and acquires the monitor lock. It checks the shared condition using while (!condition). If the condition is false, it calls monitor.wait() while still inside the synchronized section. Calling wait() releases that monitor and places Thread A in the monitor's wait set. Thread A is now WAITING. Releasing the lock is important because Thread B needs that same monitor before it can change the shared state.

3. Thread B changes the state before notifying

Thread B later enters synchronized(monitor) and acquires the same monitor. It changes the shared state first. For example, it may set condition = true or add an item to the queue. Only after making that state change does it call notify() or notifyAll() on the same monitor. This ordering matters because the waiting thread should observe the updated state after it eventually reacquires the monitor.

4. Notification does not immediately give Thread A the lock

notify() selects one arbitrary thread waiting on that monitor. notifyAll() signals all threads waiting on that monitor. A signaled thread does not immediately continue executing. In the diagram, Thread A changes from WAITING to BLOCKED while it tries to reacquire the monitor. Thread B still owns the monitor immediately after calling notify() or notifyAll(). The monitor becomes available only when Thread B exits its synchronized block and releases it.

5. Thread A reacquires the monitor and checks again

After Thread B releases the monitor, Thread A can compete to acquire it. When Thread A successfully reacquires the monitor, wait() returns. Thread A then checks the condition again using the same while loop. If the condition is still false, it calls wait() again. If the condition is true, it proceeds with its work using the updated shared state.

6. Explain the main correctness pitfalls

The most important rule is that wait(), notify(), and notifyAll() must be called while the current thread owns that object's monitor. Otherwise Java throws IllegalMonitorStateException. I would also use while instead of if. A thread may wake spuriously, or the condition may become false again before that thread reacquires the monitor. Shared state should be changed before notification while holding the same monitor. notify() wakes one arbitrary waiter, while notifyAll() is often safer when several waiters or different conditions use the same monitor.

7. Keep the JVM boundary clear

This coordination works only for threads using the same monitor object inside the same JVM. Threads in one JVM can share heap objects and therefore share this monitor and its wait set. Separate JVM processes or application replicas do not share that monitor object or wait set. wait() and notify() therefore do not provide coordination between different service instances or machines.

Practical Complexity & Trade-offs

The benefit of wait and notify is that a waiting thread can sleep instead of repeatedly checking shared state. The monitor also gives a clear rule for protecting that state. The downside is that the code is easy to get wrong. The condition, wait call, state update, and notification must all use the same monitor. Using while instead of if is safer because waking does not guarantee the condition is still true. notify() wakes one arbitrary waiter and can avoid waking every waiting thread. notifyAll() is often safer when several waiters or conditions share the monitor, but it may create extra wakeups and lock contention. We also accept an important limit: this design coordinates threads only inside one JVM sharing the same monitor object.

Why Interviewers Ask This

Interviewers ask this to see whether you understand thread coordination, not just the method names. They want to hear that wait() releases the monitor, notify() does not release it immediately, and a notified thread must reacquire the monitor before continuing. They also check whether you protect shared state correctly, use a while loop, understand notify() versus notifyAll(), and know that one JVM's monitor cannot coordinate separate processes or replicas.

Interviewer may ask next
What changes if several threads are waiting on the same monitor?

I would usually consider notifyAll() instead of notify() when several threads or several conditions share the same monitor. The rest of the design stays the same. Each waiting thread still calls wait() while owning the monitor and enters that monitor's wait set. The notifying thread still acquires the same monitor, updates the shared state first, and signals while holding that lock. With notifyAll(), every waiting thread is signaled, but they do not all continue immediately. They must compete to reacquire the monitor, and only one thread can own it at a time. Each thread must recheck its own condition in a while loop after wait() returns. This preserves correctness because some awakened threads may find that their condition is still false. The benefit is that an eligible waiter is less likely to remain asleep when different conditions share one monitor. The downside is extra wakeups and lock contention because several threads may wake even though only a few can make progress.

What happens if the producer calls notify() before changing the shared condition?

I would keep the shared-state update before the notification. The affected flow is inside Thread B's synchronized(monitor) block. Thread B should first change the state, such as setting ready = true or adding an item to the queue, and then call notify() or notifyAll() while it still owns that same monitor. Thread A cannot continue immediately because Thread B keeps the monitor until it exits the synchronized block. After Thread B releases the monitor, Thread A can reacquire it, wait() returns, and Thread A checks the condition again in its while loop. Keeping the update before the signal makes the intended handoff clear and ensures the awakened thread checks state that was changed under the same lock. Calling notify() first makes the code's coordination order confusing and easier to misuse. The main rule remains: update the condition, signal waiting threads, release the monitor, and then let awakened threads reacquire the monitor and recheck their conditions.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.

Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.