Design a coupon redemption system
Company: Plaid
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in data structures and algorithms for building an in-memory service, including efficient lookup/update strategies, expiration handling, limit enforcement, idempotency, concurrency considerations, and time/space complexity reasoning.
Part 1: In-Memory Coupon Redemption Engine
Constraints
- 0 <= len(operations) <= 200000
- code, userId, and requestId are strings without spaces
- 0 <= expiresAt, now <= 10^9
- For a valid ADD, discount, totalLimit, and perUserLimit are positive integers
- If a requestId repeats, it represents a retry of the same logical redeem request
Examples
Input: ([('ADD', 'SAVE10', 10, 100, 2, 1), ('GET', 'SAVE10', 50), ('REDEEM', 'u1', 'SAVE10', 60, 'req1'), ('REDEEM', 'u1', 'SAVE10', 70, 'req1'), ('GET', 'SAVE10', 80), ('REDEEM', 'u1', 'SAVE10', 90, 'req2'), ('REDEEM', 'u2', 'SAVE10', 90, 'req3'), ('GET', 'SAVE10', 90)],)
Expected Output: [True, 2, (True, 10), (True, 10), 1, (False, 0), (True, 10), 0]
Explanation: The second redeem with requestId 'req1' is an idempotent retry, so it returns the cached result and does not consume another use.
Input: ([('ADD', 'BAD', 0, 50, 3, 1), ('ADD', 'A', 5, 10, 1, 1), ('ADD', 'A', 7, 20, 2, 2), ('REDEEM', 'u1', 'MISSING', 5, 'r1'), ('GET', 'MISSING', 5)],)
Expected Output: [False, True, False, (False, 0), 0]
Explanation: Invalid discount makes the first ADD fail; duplicate code makes the third ADD fail.
Hints
- Use one hash map from coupon code to coupon state, and a second hash map from requestId to the previously returned redeem result.
- Store per-user redemption counts inside each coupon so checking the per-user limit stays O(1) average.
Part 2: Coupon Engine with Category Rules, Minimum Spend, and Bulk Parsing
Constraints
- 0 <= len(commands) <= 200000
- Total length of all command strings <= 10^6
- Codes, user IDs, request IDs, and category names contain no spaces
- A repeated requestId indicates an idempotent retry of the same logical redeem request
- Malformed commands should not stop processing; they produce 'ERROR'
Examples
Input: (['ADD SAVE10 10 100 3 2 50 grocery,electronics', 'REDEEM u1 SAVE10 60 30 grocery r1', 'REDEEM u1 SAVE10 60 80 fashion r2', 'REDEEM u1 SAVE10 60 80 grocery r3', 'GET SAVE10 70'],)
Expected Output: [True, (False, 0), (False, 0), (True, 10), 2]
Explanation: The first redeem fails minSpend, the second fails category restriction, and the third succeeds.
Input: (['ADD ANY5 5 20 1 1 0 *', 'REDEEM u1 ANY5 10 0 books x1', 'REDEEM u1 ANY5 10 0 books x1', 'REDEEM u2 ANY5 15 100 books x2', 'GET ANY5 15'],)
Expected Output: [True, (True, 5), (True, 5), (False, 0), 0]
Explanation: Wildcard categories allow any category. The second redeem is an idempotent retry and does not consume another use.
Hints
- Split and validate each command before touching state. Keep parse errors separate from business-rule failures.
- Store allowed categories as a set for O(1) average membership checks; use None or a special marker for '*' meaning all categories.