A design question is deliberately vague. "Design a photo-sharing app" could mean a weekend project or a global service. The worst move is to start drawing servers. The best move is a simple, repeatable plan.
- Requirements. What must it do, and how well?
- Estimates. How many users, requests and bytes? Rough numbers are fine.
- API. What calls do clients make?
uploadPhoto,getFeed,follow. - Data model. What do we store, and how is it related?
- High-level design. The boxes and arrows: clients, servers, storage, caches, queues.
- Deep dive. Pick the hardest parts and go deeper: the feed, the bottleneck, what fails.
Requirements come in two kinds
- Functional: what the system does. "Users can upload photos. Users see a feed of people they follow."
- Non-functional: how well it does it. "The feed loads in under 200 ms." "Uploads are never lost." "It stays up during a server failure."
Non-functional requirements drive most design choices. "Never lose an upload" means storing copies in more than one place. "Feed in 200 ms" probably means a cache.
Why this order?
Each step feeds the next. You can't size a database before you know how much data there is, and you can't know that before you know what users do. Skipping straight to boxes produces a design for a problem nobody stated, and it's hard to defend any choice in it.
The order also keeps you honest about time. If you spend 30 minutes on requirements, there's no time for the deep dive, which is usually where the interesting trade-offs live. A rough split for 45 minutes: 5 on requirements, 5 on estimates, 5 on API and data, 15 on the high level, 15 on the deep dive.
Key idea: requirements, estimates, API, data model, high level, deep dive. Ask before you draw, and let the numbers and the non-functional requirements decide the design.