System design sample solution

System design: build a secure client portal

A step-by-step system design answer you could actually give in 60 minutes.

System design interview · Sample 60-minute answer · Not Bloomberg's actual design · Independent, not affiliated with Bloomberg

The system design question

Design a web application where users of a financial data company can sign up, reset their password, request information about companies, and raise IT help requests. Each feature should deploy independently, and the data is highly sensitive.

Agree on the scope (5 minutes)

Say up front what you're covering.

We'll designWe'll skip
Signing up and logging inWhere the company data comes from
Resetting a passwordBilling and contracts
Requesting company informationThe UI design
Raising and tracking IT ticketsThe IT team's internal tools
How each feature ships on its own

Then agree on what "good" means:

  • Secure: only the right person sees their own data, and a stolen database dump is useless to whoever took it.
  • Independent: the ticket team can ship on a Tuesday without touching sign-up or waiting on anyone.
  • Contained: if one feature breaks or gets attacked, the others keep working.
  • Auditable: we can say who looked at what, and when, months later.

Rough numbers (5 minutes)

Made up, but reasonable: about 300,000 client users, 50,000 of them active on a given day, around 100,000 company info requests a day and a couple of thousand IT tickets. Audit logs kept for 7 years.

~300K
client users
~50K
active each day
~200
requests per second at a peak
7 years
of audit logs kept
QuestionRough mathWhat it means
How busy is it?50K users × about 100 requests a day ÷ 86,400 secondsAbout 60 a second on average, maybe 200 at a peak. A few small servers per feature
How big is the user table?300K users × about 2 KBUnder 1 GB. It fits in one database easily
How big are audit logs?50K users × 100 events × 500 bytes × 365 days × 7 yearsAbout 6 TB, so store it somewhere cheap and append-only

The overall design (10 minutes)

Client browserHTTPS onlyAPI gatewaychecks the tokenFeature servicesauth, info, ticketsOwn databasesone per serviceAudit logwho did what
Every request goes through the gateway, then to the one service that owns that feature and its data.
  1. The browser talks only to an API gateway over HTTPS. The gateway checks the login token, applies rate limits, and routes the request.
  2. Behind it are separate services: an auth service for sign-up, login and password reset, a company info service, and a ticket service. Each has its own database and its own deploy pipeline.
  3. The auth service hands out short-lived tokens. The other services check the token themselves, so they don't call auth on every request.
  4. Slow work, like sending emails or pulling a big company report, goes onto a queue and gets picked up by workers.
  5. Every service writes who did what to a shared, append-only audit log.

What the apps need to do

ActionWho does itWhat's sent
Sign upNew userEmail, name, company, and an invite code from their account manager
Log inUserEmail, password, then a one-time code from their second factor
Request a password resetUserEmail. Always replies 'check your inbox', whether or not the account exists
Set a new passwordUserThe reset token from the email and the new password
Request company infoUserCompany ID and what they want, like filings or financials
Open or update a ticketUserSubject, description, attachments

What we store and where

DataWhere it goesWhy
Users and password hashesAuth service's relational databaseSmall, needs strict uniqueness on email, and nobody else can read it
Reset tokensAuth database, stored hashed with a 15 minute expirySo a leaked table can't be used to reset anyone's password
Company info requestsInfo service's relational databaseSimple rows with a status, tied to a user ID
Popular company dataA cache in front of the info serviceLots of people ask about the same big companies
Tickets and commentsTicket service's database, attachments in encrypted object storageFiles don't belong in the database
Audit eventsAppend-only log storage, kept 7 yearsNobody can edit or delete them, including us

Step 1: Sign-up, login and password reset (10 minutes)

Since this is a paid product for businesses, don't let just anyone sign up. The account manager creates an invite, and the user signs up with that invite code. That cuts out most fake accounts before they start.

Store passwords with a slow hashing algorithm made for passwords, like bcrypt or Argon2. Slow on purpose means someone who steals the table can only guess a handful of passwords a second. Require a second factor, like an authenticator app or a hardware key, for everyone. This is standard authentication practice, and with financial data it's not optional.

Login hands back a short-lived access token, around 15 minutes, plus a longer refresh token stored in a secure cookie the page's JavaScript can't read. Short tokens mean a stolen one isn't worth much for long.

Password reset

The user enters their email. We always say 'if that account exists, we sent a link', so attackers can't use the form to find out who's a customer. Then we email a random token that works once and expires in 15 minutes. We store only a hash of it, the same way we treat passwords.

When they set the new password, kill all their existing sessions and email them that it changed. Rate limit the reset form per email and per IP so nobody can spam it.

Step 2: Letting each feature deploy on its own (10 minutes)

Split by feature: auth, company info, and tickets are separate services, each owned by one team, with its own code, its own database and its own pipeline. The rule is that no service reads another service's database. If tickets needs a user's name, it asks auth through an API or keeps its own copy.

The gateway is the only shared piece, and it just routes by path, like everything under /tickets goes to the ticket service. Keep it boring so changing it is rare.

To ship safely, each service versions its API and only adds fields, never removes or renames them in place. New versions roll out to a small slice of traffic first, and roll back automatically if errors jump.

The frontend can be split the same way: one shell page that loads each feature's screens separately, so the ticket team can ship UI changes too. In an interview, mention it, but it's fine to say one shared frontend with feature flags is simpler and probably enough to start.

Step 3: Keeping sensitive data locked down (10 minutes)

Encrypt everything in transit, including between our own services, and encrypt every database and file store at rest with keys held in a key management service, not in config files.

Each service gets its own database credentials, and only it can read its data. Inside each service, check that the user is allowed to see this specific thing, like 'this ticket belongs to your company'. Checking only that someone is logged in is the most common way these apps leak data.

Ticket attachments are the riskiest input, since anyone can upload anything. Scan them for malware, store them outside the web servers, and serve them through short-lived signed links.

Every read and write of sensitive data goes to the audit log: who, what, when, from where. Write it to storage that can't be changed after the fact, so even an admin can't cover their tracks.

  • Don't log passwords, tokens or full documents in normal app logs.
  • Admin access for engineers goes through a separate login, needs approval, and is time-limited.
  • Cache only data that's the same for everyone, like public company facts, never one user's private data in a shared cache.

Choices you should be able to defend

We choseInstead ofBecauseWe'd reconsider if
A service per featureOne app with modulesTeams can deploy independently, which the question asks forWe had one small team, where a single app is easier to run
Invite-only sign-upOpen sign-up with email checksPaying clients are known ahead of time, so fakes are mostly goneWe added a free tier for the public
Short-lived tokens checked by each serviceCalling auth on every requestAuth isn't a bottleneck or a single point of failureWe needed to revoke access instantly, in which case keep a small revoked list
Relational databasesA document storeThe data's small and structured, and we want strict rules like unique emailsTickets got really free-form, which a document store handles fine too

What could go wrong (5 minutes)

What happensWhat we do about it
The auth service goes downPeople already logged in keep working until their token expires. New logins wait
The ticket service has a bad deployOnly tickets break. The rollout catches the error spike and rolls back
Someone tries thousands of passwordsRate limits per account and per IP, then lock the account and alert. The second factor stops them anyway
The email provider is downReset and verification emails wait on the queue and go out when it's back
A database backup leaksData is encrypted with keys that weren't in the backup, and passwords are slow hashes

Follow-up questions (5 minutes)

How would you add single sign-on for big clients?

Let a client company connect its own login system, so their users log in through their employer. The auth service trusts that and still hands out our own tokens.

How do you remove someone who leaves a client company?

The client admin disables them, auth kills their refresh tokens, and access ends when the current short token expires a few minutes later.

How would you handle a big jump in company info requests?

Cache popular, non-private company data and put slow report generation on the queue, so the web servers stay quick.

How the 60 minutes add up

MinutesWhat you're doing
0–5Agree on what you're building
5–10Rough numbers
10–20Draw the overall design
20–30Sign-up, login and password reset
30–40Letting each feature deploy on its own
40–50Keeping sensitive data locked down
50–55What could go wrong
55–60Follow-up questions

Key concepts used here

Further reading

We wrote this sample answer ourselves.

Practice Loop is independent and isn't affiliated with or endorsed by Bloomberg.