How to Pass the Blinkit SDE-2 LLD Interview

AAcePrompt Team·September 18, 2026·10 min read
How to Pass the Blinkit SDE-2 LLD Interview

Quick-commerce operates on razor-thin margins and unforgiving timelines. When a customer confirms a cart on Blinkit, the backend has exactly a 10-minute window to route the order to the nearest dark store, verify real-time stock, reserve the items, batch the delivery, and dispatch a partner. For an SDE-2 candidate, the Low-Level Design (LLD) and machine coding round is the ultimate test of translating this operational intensity into robust, thread-safe code. You are not just sketching high-level boxes on a whiteboard. You are writing the core execution engine that prevents the system from overselling when fifty people simultaneously attempt to buy the last available packet of milk during a flash sale. Mastering this round requires a deep, practical understanding of concurrency, lock management, object-oriented design, and the ability to write executable code under intense time pressure.

The Blinkit SDE-2 Interview Process

Before diving into the architecture, you need to understand the gauntlet you are running. The SDE-2 process at Blinkit heavily indexes on your ability to write clean, concurrent, and extensible code. While the High-Level Design (HLD) round focuses on cloud architecture and database choices, the LLD and machine coding round requires you to produce a working, compile-ready application (usually in Java, Go, or C++) that solves a specific domain problem.

Interview RoundFocus AreaDuration
DSA & Problem SolvingLeetCode Medium/Hard, Trees, Graphs, DP45-60 mins
Machine Coding / LLDConcurrency, OOP, Thread-safety, Design Patterns90-120 mins
System Design (HLD)Scalability, Microservices, Database Choice60 mins
Hiring ManagerBehavioral, Past Projects, Culture Fit45 mins

Deconstructing the Dark Store Data Model

A dark store is not a traditional retail environment; it is a hyper-optimized micro-warehouse designed exclusively for rapid picking and packing. Your object-oriented design must reflect this reality. In the machine coding round, interviewers look for a clear separation of concerns. Cramming all your logic into a single God Class like `OrderProcessor` is an immediate red flag. Instead, you need to define precise, single-responsibility entities.

  • Store: Represents the physical dark store. It contains attributes like storeId, location coordinates, and operational status (e.g., active, offline for restocking).
  • Product: The global catalog item, agnostic of location. It holds the productId, name, weight, and category.
  • Inventory: The intersection of a Store and a Product. This is where the actual quantity lives. It requires a compound key of storeId and productId.
  • Reservation: A temporary lock on specific inventory items. It must contain a reservationId, userId, a list of reserved items, and a strict timestamp for expiration.
  • Order: The final immutable record created only after payment succeeds and the reservation is permanently committed.
How to Pass the Blinkit SDE-2 LLD Interview

By establishing these entities early, you create a solid foundation for the complex concurrency logic that follows. The relationships are straightforward: A Store has many Inventory records; an Inventory record belongs to one Store and references one Product. Building these models with private fields, appropriate getters, and constructor-based dependency injection demonstrates immediate maturity in your code.

Designing the Dark Store Inventory

Building a concurrent inventory reservation system

State management is your primary hurdle. You will be asked to build an API like `reserveInventory(String userId, String storeId, Map<String, Integer> items)` that executes atomically. Because you are implementing an in-memory datastore during the interview, relying on standard collections like `HashMap` or `ArrayList` will lead to disastrous race conditions. You must leverage concurrent data structures.

A common approach is utilizing a nested `ConcurrentHashMap`. For instance, `Map<String, Map<String, Inventory>>` where the outer map is keyed by `storeId` and the inner map by `productId`. Inside your `Inventory` class, avoid primitive integers for stock counts. Instead, use an `AtomicInteger`. This allows you to perform basic read and update operations safely using hardware-level Compare-And-Swap (CAS) instructions, entirely skipping heavy, class-level synchronization for single-item updates. However, as we will see, `AtomicInteger` alone is not enough when a user's cart contains multiple different items.

Handling race conditions to prevent overselling

Overselling is the cardinal sin of quick-commerce. Imagine a scenario where a dark store has exactly 1 packet of milk left. User A and User B both hit the checkout button at the exact same millisecond. If two threads read the available stock as '1', and both decrement it, you have just created a race condition that leads to negative inventory and a cancelled order, ruining the customer experience.

While `AtomicInteger.decrementAndGet()` is thread-safe for a single item, real-world carts have multiple items. You need transactional guarantees across the entire cart. If a user wants Milk and Bread, you cannot successfully decrement the Milk, fail to decrement the Bread, and leave the system in a partial state. To solve this, you need explicit locking mechanisms. In Java, this means attaching a `ReentrantLock` to every single `Inventory` object.

When the `reserveInventory` method is called, your code should iterate through the requested items and attempt to acquire the lock for each one using `lock.tryLock(timeout, TimeUnit.MILLISECONDS)`. Using `tryLock` with a timeout is vastly superior to a standard `lock.lock()` or a `synchronized` block because it prevents thread starvation. If a thread cannot acquire all necessary locks within 50 milliseconds, it fails fast, releases any locks it already holds, and returns an error to the user. This fail-fast mechanism is critical for maintaining high throughput in a 10-minute delivery system.

The Anatomy of a Thread-Safe Checkout

To guarantee atomicity and thread safety, your checkout logic must follow a strict, unyielding sequence of operations. Skipping any of these steps in your machine coding round will immediately expose your design to concurrency bugs.

  1. Validation: Check if the store exists and if all requested products are actively cataloged. Do this before acquiring any locks to keep critical sections as fast as possible.
  2. Sorting: Sort the requested product IDs lexicographically. (More on why this is critical in the Deadlocks section below).
  3. Lock Acquisition: Iterate through the sorted items and call tryLock. If any lock fails, immediately enter a finally block to unlock all previously acquired locks and throw a ReservationFailedException.
  4. Stock Verification: Once all locks are held, verify that the requested quantity for every item is less than or equal to the available stock.
  5. Deduction: Decrement the stock counts.
  6. Reservation Creation: Generate a unique reservationId, store the transient state, and return success.
  7. Lock Release: Always release locks in a finally block to ensure they are freed even if an unexpected runtime exception occurs during deduction.

Designing the order batching strategy

After successfully reserving inventory and confirming payment, the system must batch orders for delivery partners. Dark stores operate efficiently because delivery partners rarely take just one order at a time; they take batches of 2-3 orders heading in the same general direction. This is exactly where behavioral design patterns shine in an LLD interview.

You should define a `BatchingStrategy` interface with a method like `List<Batch> createBatches(List<Order> unassignedOrders)`. By doing this, you can implement multiple concrete strategies. A `TimeBasedBatchingStrategy` might group any orders placed within the same 90-second window, prioritizing speed. A `GeoSpatialBatchingStrategy` might group orders whose delivery coordinates fall within a 500-meter radius of each other, prioritizing partner efficiency. Your `OrderManager` class accepts this interface via its constructor, adhering perfectly to the Open/Closed Principle. If the business decides to switch batching logic during peak hours, your core order processor doesn't change a single line of code.

Once a batch is finalized, use the Observer Pattern to alert available delivery partners. The `BatchManager` acts as the Subject, publishing a `BatchReadyEvent`. The `DeliveryPartnerRoutingService` acts as an Observer, listening for these events and pinging the nearest idle drivers. This neatly decouples your core order processing logic from the downstream notification and fleet assignment systems.

Tip: Always implement a TTL (Time-To-Live) for reservations. If a user reserves an item but their payment fails or times out, you must release that stock back to the pool. In Java, implement a background daemon thread utilizing a `DelayQueue`. Wrap your Reservation object in a class that implements the `Delayed` interface, setting the delay to 5 minutes. The daemon thread continuously polls the queue, automatically expiring abandoned reservations and incrementing the stock back up.

Edge Cases and Trade-Offs

Writing the happy path will get you a passing grade, but handling the edge cases is what secures an SDE-2 offer. Interviewers will actively try to poke holes in your concurrency model. You need to anticipate these attacks and build defenses directly into your architecture.

  • Deadlocks: This is the most common trap. If User A tries to buy Milk (ID: 10) and Bread (ID: 20), and User B tries to buy Bread (ID: 20) and Milk (ID: 10), naive locking will cause a deadlock. Thread A locks Milk, Thread B locks Bread, and both wait indefinitely for the other. You solve this by enforcing a strict global ordering. Always sort the product IDs before acquiring locks. Both threads will attempt to lock Milk (ID: 10) first, entirely eliminating the circular wait condition.
  • Memory Footprint: Holding millions of ReentrantLocks in memory will easily trigger OutOfMemory errors. If the interviewer presses you on scale, discuss lock striping. Instead of a lock per item, you can create a fixed array of locks (e.g., 256 locks) and assign items to locks based on a hash of their productId. This dramatically reduces memory overhead while maintaining an acceptable level of concurrency.
  • Idempotency: Mobile networks are flaky. A user's app might retry the checkout API three times if the first response drops. Your reserveInventory method must accept an idempotencyKey (usually a UUID generated by the frontend). Store these keys in a ConcurrentHashMap with a TTL. If a request arrives with a key you have already processed, return the cached success response instead of deducting the inventory a second time.

Surviving the 90-Minute Machine Coding Window

Knowing the theory is vastly different from writing it under the gaze of a ticking clock. When the 90-minute machine coding round begins, do not waste time scaffolding a massive web framework like Spring Boot or setting up REST controllers unless explicitly instructed to do so. The interviewer wants to evaluate your core domain logic, not your ability to write boilerplate HTTP endpoints.

Structure your project as a simple console application. Spend the first 15 minutes defining your interfaces and entities. Spend the next 45 minutes writing the core concurrency logic inside your `InventoryManager`. Reserve the last 30 minutes for writing a robust driver class. To prove your code works, instantiate an `ExecutorService` with a fixed thread pool of 50 threads. Submit 100 concurrent `Callable` tasks that all attempt to purchase the same highly-contested item. Print the final inventory count to the console. If your locking logic is sound, the inventory will hit exactly zero, the remaining 50 requests will gracefully fail with a custom exception, and your console output will prove to the interviewer that your system is bulletproof.

Passing the Blinkit LLD round demands a delicate balance between clean object-oriented abstractions and gritty, low-level concurrency controls. You are proving that you can write code that is highly extensible for future product requirements, yet tightly optimized for multi-threaded execution. By structuring your entities clearly, proactively defending against race conditions and deadlocks, and confidently articulating the real-world trade-offs of your in-memory design, you will demonstrate the exact engineering rigor required to build systems at quick-commerce scale.

Frequently asked questions

What is the primary focus of the Blinkit SDE-2 LLD round?

The interview leans heavily into concurrency, multithreading, and clean OOP principles. Expect to simulate real-world quick-commerce problems like inventory management or delivery routing, and you'll need to do it in a completely thread-safe way.

Should I write production-ready code in the interview?

Aim for modular code that handles threads safely. It doesn't necessarily have to compile perfectly inside a plain text editor, but interviewers will penalize you heavily for logical flaws like race conditions, deadlocks, or tight coupling.

Which programming language is best for the Blinkit LLD round?

Java, Go, and C++ are incredibly popular choices because they offer robust multithreading libraries and strong typing. Python is perfectly acceptable too, though you'll need to clearly explain how you plan to handle concurrency alongside the Global Interpreter Lock (GIL).

How important are design patterns in this round?

They're incredibly important. Leveraging patterns like Strategy, Observer, and Factory proves your code is both extensible and maintainable, which is vital since business requirements inevitably change over time.

Related comparisons

See AcePrompt in action

Watch how AcePrompt supports a real technical round - structured answers, tuned to your resume, in real time.

Ace your Blinkit LLD round with AcePrompt's real-time AI interview copilot.

Get started

See pricing →

Keep reading

Blinkit SDE-2 LLD Interview Guide: Dark Store Inventory