Design: Photo Backup
This is a real example of what the design-with-blocks Claude skill hands you at the end of a 10-minute conversation. The doc below — and the architecture diagram on the skill page — are both produced automatically from one paragraph describing the app.
What you are building
A private photo backup app. Users sign in, their phone auto-uploads new photos to a personal library, and a fast thumbnail grid lets them browse on any connection. No social features, no comments, no algorithm. Just the photos.
Users and external forces
- Users: humans, mobile-first (iOS and Android web). Designing for tens of thousands of users uploading hundreds of photos each per month.
- External Services: none for the MVP. A push-notification provider (Twilio Notify, OneSignal) could be added later for "your photos backed up" confirmations.
- Time: none for the MVP. A nightly cleanup job for orphan files is a Phase 2 addition.
Features, decomposed
Feature 1: Upload a photo
- Blocks: Photo Service, File Store, Relational Database, Queue, Thumbnail Worker
- Flow: User uploads. Photo Service writes the binary to File Store, writes metadata (user_id, filename, taken_at, size) to Relational Database, writes a job to the Thumbnail Queue, and returns "uploaded" within a second. Thumbnail Worker pulls from the Queue, generates 3 thumbnail sizes, writes them back to File Store, updates the Relational Database row with the thumbnail URLs.
Feature 2: Browse the photo grid
- Blocks: Photo Service, Key-Value Store, Relational Database
- Flow: User opens the grid. Photo Service checks the Key-Value Store for the recent-photos slice for this user. Cache hit returns immediately. Cache miss queries the Relational Database for the most recent N rows, writes the result to the Key-Value Store with a 5-minute TTL, returns.
Feature 3: View one photo full-size
- Blocks: Photo Service, File Store
- Flow: User taps a thumbnail. Photo Service issues a signed URL to the original in File Store and returns it. Browser fetches directly from File Store, no proxy through Photo Service.
Block inventory
| Instance | Role | Technology choice | Why |
|---|---|---|---|
| Photo Service | Sync upload, browse, signed URL issuing | Docker Container + FastAPI | Mobile clients hit one endpoint per action; FastAPI's async support keeps the connection pool tight under upload bursts. |
| Thumbnail Worker | Async thumbnail generation | Docker Container + Celery | Standard async-job pattern in Python; integrates cleanly with Pillow for image resizing. |
| Thumbnail Jobs (Queue) | Buffer between upload and worker | RabbitMQ | Durable, retry-friendly, decouples upload throughput from worker throughput. |
| Recent Photos Cache | Per-user grid view cache | Redis | Single-key gets, sub-millisecond reads, TTL handles staleness automatically. |
| Photo Library (File Store) | Originals plus generated thumbnails | S3 (or MinIO if self-hosted) | Designed for blob storage; clients fetch via signed URL without proxying through Photo Service. |
| App DB | Photo metadata, user accounts, thumbnail URLs | Postgres | Structured data with relations (user → photos), indexed time-range queries for the grid view. |
| User | Mobile-first humans | — | iOS and Android web; uploads happen in the background, browsing is the active session. |
To learn more about each building block — what it is, when to use it, common junior mistakes, and the patterns it shows up in — see systemthinkinglab.ai/learn.
Common mistakes to avoid
- Skipping the Queue. Do not make the Photo Service generate the thumbnail synchronously. The user is waiting for the upload to finish; a 5-second thumbnail generation makes the upload feel slow even though the binary is already saved.
- Storing the photo binary in Postgres. Postgres is a Relational Database, not a File Store. Postgres can hold a few KB blob, but the moment you have 10 GB of photos per user, that decision becomes the bottleneck you cannot move past without a migration. Use File Store for blobs from day one.
- Caching the entire grid forever. TTL it. 5 minutes is fine. Otherwise users upload a new photo and do not see it for hours.
What to build next
Paste this design into Claude Code or Cursor and have it implement each block. Start with Photo Service and Postgres; the upload flow and the grid query are both small enough to ship in a day. Add the Thumbnail Queue and Worker once the synchronous flow works end-to-end. Add the Cache last, only after you measure that the grid query is the bottleneck.
What this skill cannot do for you
This skill gave you one design, this one time, with a Claude session doing the thinking. It cannot:
- Teach you to design future systems without this tool.
- Give you the mental models (coat check, smart librarian, kitchen staff) that let you reason about systems you have never seen.
- Take you through 15 real companies (Instagram, Netflix, Uber, Stripe, Discord, Shopify, and more) so the pattern sticks.
- Prepare you for system design interviews.
- Help you debug production in a codebase you just inherited.
That is what Course I: Universal Building Blocks teaches.
Designed using the 7 Building Blocks framework by Kay Ashaolu. The original architecture diagram is on the skill page.