Representative interview topic

System Design Interview: Design an Image Transformation and Delivery Pipeline

System designHard
Offer.cc Editorial TeamPublished Updated

Question

Design a service that accepts originals and generates resized, cropped, compressed, and converted derivatives without duplicate work or resource exhaustion from hostile parameters.

Problem and scope

The platform receives 10 million originals per day, while clients request many dimensions, crops, and output formats. Design original storage, transformation jobs, derived-image caching, and CDN delivery. Originals must be reprocessable; collaborative editing layers, content-moderation models, and professional color calibration are out of scope. The numbers are interview assumptions, not industry benchmarks.

What the interviewer is evaluating

Separate the identity of an original, a transform request, and a derived asset. Explain how asynchronous processing coexists with low-latency reads. Strong answers cover deterministic cache keys, duplicate-job coalescing, queue backpressure, pixel and decompression bombs, object authorization, content negotiation, and cleanup after deletion.

Clarifying questions

  • Are arbitrary dimensions, crops, and filters allowed? What are pixel, file-size, and time limits?
  • Which formats, quality levels, color spaces, and animation behaviors are required?
  • Must the first request be synchronous, or may it return 202 with job status?
  • What are retention, tenant-isolation, and cross-region requirements?
  • Are results public, or must every URL carry authorization?

A 30-second answer framework

“I would store originals as content-addressed immutable assets and encode normalized transform parameters into a derived key. Reads check the CDN and derived object first; a miss submits an idempotent job partitioned by tenant and original. Workers run in a sandbox with pixel, memory, CPU, decompression, and output limits, then atomically publish a validated temporary object. Retries carry a processor version and budget, while identical requests share one job. The access layer applies signed URLs, negotiation, and cache controls; deletion events clean derived indexes and CDN entries.”

Step-by-step deep design

The upload API authenticates, verifies a digest, and writes the original object. Metadata records asset_id, tenant, content hash, media type, dimensions, frame count, color information, scan state, and retention policy. Content deduplication never replaces a tenant authorization check. Validate magic bytes and decodeability instead of trusting a filename or Content-Type.

Normalize transform parameters such as dimension limits, crop coordinates, resampling algorithm, quality, rotation, background, and output format into a stable sequence. A derived key can be hash(original_bytes, normalized_transform, processor_version). Processor upgrades change the version, so old and new algorithms do not silently overwrite each other. Reject negative values, NaN, extreme ratios, and recursive filters.

The read path checks the CDN, the derived-object index, and the job deduplication table. A usable derivative returns with Cache-Control, ETag, and the correct content type. On a miss, create a PENDING job and return a status URL when latency is acceptable; a small common image may use a tightly bounded synchronous path. A unique constraint on (tenant_id, derived_key) lets identical requests share work.

Partition queues by tenant and original hash, and schedule by pixel cost rather than request count. Tenant quotas, global concurrency, and priority queues provide backpressure; one huge image cannot occupy every worker. Jobs carry leases and processor versions, so a killed worker can be retried. Exponential backoff is for recoverable failures; invalid parameters and unsupported formats fail explicitly.

The processing sandbox limits CPU, memory, temporary disk, decompression ratio, frame count, and output dimensions, and cannot reach internal networks. Recompute actual pixels and frames after decoding to stop decompression bombs. Write a temporary object, validate digest, dimensions, and format, then publish atomically through conditional or versioned writes. Define explicit policies for SVG, ICC profiles, and metadata to prevent scripts, path traversal, and privacy leaks.

Cache derivatives by their key. Original deletion or permission changes emit an asset event that invalidates the derived index and CDN. Private assets use short-lived signed URLs whose signatures cover tenant, derived key, expiry, and allowed response headers. Keep Vary limited to negotiation dimensions that truly change output; arbitrary query parameters must not create unbounded cache variants.

Monitor original-write success, queue age, pixel-weighted throughput, cache hit rate, duplicate-job coalescing, p95 transform latency, failure classes, sandbox peaks, derivative growth, and CDN 5xx. Reconciliation compares original metadata, job terminal states, derived indexes, and object listings to remove orphans. Inject worker kills, object-store timeouts, duplicate messages, processor upgrades, CDN invalidation failures, and tenant quota exhaustion.

High-quality sample answer

“I would treat the original as an immutable addressable asset and combine normalized parameters with a processor version into a derived key. A request checks the CDN and derived object; a miss creates a shared job under a unique constraint and returns the existing job or a status URL. Queues schedule by tenant and pixel cost. Workers run in a sandbox that limits decompression ratio, memory, CPU, frames, and output size, then validate and atomically publish a temporary result.

Private access uses a signed URL covering tenant, derived key, and expiry; public responses set correct ETag, Cache-Control, and content type. Deletion events clean indexes and CDN entries. Metrics cover hit rate, queue age, p95 latency, resource peaks, and failure classes; reconciliation finds orphaned objects. Identical requests do not recompute, hostile images cannot exhaust capacity, and processor upgrades cannot contaminate old derivatives.”

Common mistakes

  • Use the filename as identity → renames and collisions overwrite assets → use content hashes and asset IDs.
  • Concatenate raw parameters → equivalent requests fragment the cache → normalize before keying.
  • Create a job for every miss → hot images trigger a compute storm → coalesce by tenant and derived key.
  • Limit bytes only → a decompression bomb expands into huge pixels → limit decoded pixels, frames, and ratio.
  • Process every request synchronously → slow work blocks reads → combine an async queue with a bounded fast path.
  • Write directly to the final object → clients can read partial output → validate a temporary object and publish atomically.
  • Sign only the URL → parameters or response policy can change → sign tenant, key, expiry, and policy.
  • Delete only the original → private derivatives remain accessible → drive cleanup from asset events.

Follow-up questions and answers

Follow-up 1: Why include the processor version in the cache key?

Different algorithms or libraries can produce different bytes. Versioning allows new results to coexist and old results to be retired deliberately.

Follow-up 2: How do you handle animated images?

Include frame count, duration, and output policy in the cost model, cap total frames and pixels, and use a separate key for a first-frame thumbnail.

Follow-up 3: When do you return 202?

Return 202, a job ID, and retry guidance when cost is unpredictable, output is large, or the queue is backed up. Small common formats may use a strict synchronous budget.

Follow-up 4: How do you prevent cache poisoning?

Only the server creates keys from normalized parameters. Validate type, length, and digest before publication; bind private caches to tenant authorization.

Follow-up 5: What happens to old URLs after replacing an original?

Use immutable version IDs. Replacement creates a new asset or version event; old derivatives remain or expire according to retention rather than silently changing bytes.

Follow-up 6: How do you keep costs fair?

Charge by decoded pixels, output pixels, and filter complexity. Tenant token buckets, concurrency caps, and daily budgets control expensive work and produce auditable quota responses.

Public sources

Related questions

Related interview tool

Use Solve for a system design answer

Clarify the requirements first, then move through scale, architecture, component choices, and trade-offs.

View the tool