← Adobe Interview Insights

Adobe·Software Engineer·Onsite - System Design / Architecture·Senior

SeniorPrefer not to say
May 2026

Summary

Adobe system design round for a software engineer role. The whole session was essentially one big question about a travel recommendation service, but it branched into a lot of sub-topics fast. More depth than I expected for a single question.

Questions Asked (4)

Q1

Design a service that accepts a zip code and a date, then returns travel recommendations by calling a Weather backend (zip code input, max 10 concurrent connections) and an Event backend (date input, max 100 requests per second). How do you enforce those limits?

System DesignTechnical Trade-offs
Author's notes

The concurrency cap on Weather tripped me up a bit.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements and constraints, then design a service that orchestrates calls to the Weather and Event backends while enforcing their respective rate limits. Use a combination of concurrency limiting for the Weather backend and rate limiting for the Event backend, and discuss trade-offs of different implementation strategies.

Pro tip: Demonstrate awareness of distributed system challenges by mentioning that rate limits may need to be enforced across multiple instances of your service, and discuss options like centralized rate limiting or sticky sessions. Also, highlight the importance of graceful degradation and retries with backoff when limits are exceeded.

1. Clarify Requirements and Constraints

Ask questions to understand expected traffic volume, latency requirements, and whether the limits are per instance or global. Confirm that the Weather backend allows max 10 concurrent connections and the Event backend allows 100 requests per second.

2. High-Level Architecture

Outline a service that receives requests, validates input, and makes parallel calls to the Weather and Event backends. Consider using an API gateway, load balancer, and caching to reduce backend load.

3. Enforcing Weather Backend Limit

Use a semaphore or connection pool to limit concurrent connections to 10. Implement a queue for pending requests and ensure threads/goroutines acquire a permit before calling the Weather backend.

4. Enforcing Event Backend Limit

Implement a rate limiter (e.g., token bucket or sliding window) to allow up to 100 requests per second. Use a distributed rate limiter if the service is scaled horizontally, such as Redis-based token bucket.

5. Handling Failures and Trade-offs

Discuss how to handle rate limit exceeded errors: retry with exponential backoff, return partial results, or degrade gracefully. Compare centralized vs. local rate limiting and their impact on scalability and consistency.

Key Points to Mention

  • Semaphore or connection pool for concurrency limiting (Weather backend)
  • Token bucket or sliding window algorithm for rate limiting (Event backend)
  • Distributed rate limiting using Redis or similar for multi-instance deployments
  • Caching to reduce backend calls and improve latency
  • Graceful degradation and fallback strategies when limits are reached
  • Monitoring and alerting on rate limit violations and backend performance

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

What caching strategy would you use for this service, including TTL design, cache key structure, and how you'd handle cache stampedes?

System DesignAPI & Integrations
Author's notes

This was the part I felt best about.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the service's read/write patterns, consistency requirements, and scale to tailor your caching strategy. Then propose a layered caching approach (e.g., local + distributed) with TTLs based on data volatility and access frequency, and explain how you'd prevent stampedes using techniques like locking or probabilistic early expiration.

Pro tip: Quantify the impact: estimate cache hit ratio and latency reduction, and discuss trade-offs like memory cost vs. freshness. Also, mention monitoring cache metrics (hit rate, eviction rate) to iterate on TTLs.

1. Clarify requirements and constraints

Ask about read/write ratio, data size, consistency needs, and latency SLAs to determine if caching is appropriate and what strategy fits.

2. Choose caching layers and technology

Decide between local (in-process) and distributed caches (e.g., Redis, Memcached) based on scale, consistency, and fault tolerance.

3. Design cache keys and TTLs

Define a key structure that includes relevant dimensions (e.g., user ID, resource ID) and set TTLs based on data volatility and access patterns, possibly using different TTLs for different data types.

4. Handle cache stampedes and consistency

Implement stampede protection (e.g., mutex locks, probabilistic early expiration) and choose a cache invalidation strategy (write-through, write-behind, TTL-based) that meets consistency requirements.

5. Monitor and iterate

Set up metrics (hit ratio, latency, eviction rate) and adjust TTLs and cache size based on observed performance and changing access patterns.

Key Points to Mention

  • Cache key design: include all relevant parameters (e.g., user ID, query params) and use consistent hashing for distribution.
  • TTL strategies: static TTLs for stable data, sliding expiration for session data, and adaptive TTLs based on access frequency.
  • Cache stampede mitigation: mutex locks, probabilistic early expiration (e.g., XFetch), or background refresh.
  • Cache invalidation: write-through vs. write-behind, and handling stale data with versioning or time-based invalidation.
  • Layered caching: local cache (e.g., Caffeine) for hot data and distributed cache (e.g., Redis) for shared data.
  • Monitoring and metrics: track hit ratio, latency, and eviction rates to optimize TTLs and cache size.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

How would you use ConcurrentHashMap for shared state in this service, and what are the trade-offs between optimistic and pessimistic locking here?

System DesignTechnical Trade-offs
Author's notes

Blanked for a second on the pessimistic vs optimistic framing in this specific context.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the service's shared state and access patterns, then explain how ConcurrentHashMap provides thread-safe operations without global locking. Compare optimistic and pessimistic locking in terms of contention, consistency, and performance, and justify your choice based on the specific use case.

Pro tip: Mention that ConcurrentHashMap's computeIfAbsent is atomic and can be used for lazy initialization, but be aware that the mapping function should not attempt to update the map to avoid deadlocks. Also, highlight that optimistic locking often pairs well with retry loops, while pessimistic locking can lead to thread starvation under high contention.

1. Clarify the shared state and access patterns

Identify what data is shared, how frequently it's read/written, and the required consistency guarantees. This determines whether ConcurrentHashMap is suitable and which locking strategy fits.

2. Explain ConcurrentHashMap's concurrency model

Describe how it achieves thread-safety via segment locking (Java 7) or CAS and synchronized nodes (Java 8+), and its atomic compound operations like compute, merge, and putIfAbsent.

3. Define optimistic and pessimistic locking

Optimistic locking assumes low contention and uses versioning or CAS with retries; pessimistic locking assumes high contention and acquires locks before access to prevent conflicts.

4. Compare trade-offs in the context of the service

Discuss performance (throughput vs. latency), scalability, complexity, and failure modes. For example, optimistic locking may cause retries under contention, while pessimistic locking can cause blocking and deadlocks.

5. Recommend a strategy with justification

Choose based on expected contention, consistency needs, and performance goals. For low contention, optimistic locking with ConcurrentHashMap's atomic operations is often sufficient; for high contention, consider pessimistic locking or alternative data structures.

Key Points to Mention

  • ConcurrentHashMap's atomic operations (e.g., computeIfAbsent, merge) and their internal implementation (CAS, synchronized blocks).
  • Optimistic locking: versioning, CAS, retry loops, and suitability for low-contention scenarios.
  • Pessimistic locking: explicit locks (e.g., ReentrantLock), blocking, potential for deadlocks, and suitability for high-contention scenarios.
  • Trade-offs: throughput vs. latency, scalability, complexity, and risk of contention-related issues.
  • Alternatives: using ConcurrentHashMap with external synchronization, or other concurrent data structures like ConcurrentSkipListMap.
  • Real-world examples: caching, counters, session management, and how Adobe might apply these in a service.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

How would your service gracefully degrade if one of the backend dependencies becomes saturated or unavailable?

System DesignAdaptability & Ambiguity
Author's notes

Talked about circuit breakers and fallback responses.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the scenario and the criticality of the dependency, then outline a layered defense strategy that includes detection, isolation, fallbacks, and communication. Emphasize that graceful degradation is about maintaining core functionality and user experience, not just avoiding crashes.

Pro tip: Mention specific patterns like circuit breakers, bulkheads, and fallbacks, and tie them to real-world examples (e.g., Netflix Hystrix). Also, discuss how you'd measure and alert on degradation to enable rapid response.

1. Clarify the scenario

Ask questions to understand which dependency is affected, its criticality, and the expected impact on the service. This shows you don't jump to solutions without context.

2. Detect and isolate

Explain how you would detect saturation or failure (e.g., timeouts, error rates, latency) and isolate the failing dependency using patterns like circuit breakers and bulkheads to prevent cascading failures.

3. Implement fallbacks

Describe fallback strategies such as serving cached or stale data, using default values, or degrading non-critical features while keeping core functionality available.

4. Communicate and monitor

Outline how you would inform users (e.g., UI messages) and internal teams (e.g., alerts, dashboards) about the degradation, and how you would monitor recovery.

5. Test and iterate

Mention the importance of chaos engineering and load testing to validate degradation behavior, and continuously refine based on incidents.

Key Points to Mention

  • Circuit breaker pattern to fail fast and prevent resource exhaustion
  • Bulkhead pattern to isolate failures and limit blast radius
  • Fallback mechanisms: cached responses, default values, degraded features
  • Timeouts and retries with exponential backoff and jitter
  • Monitoring and alerting for early detection of saturation
  • User experience considerations: clear communication and graceful UI degradation

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.