System design sample solution

System design: tag content across products

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

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

The system design question

Design a system that lets people add tags to content across Atlassian products, like Jira issues and Confluence pages. Users should see the tags on any item, and find everything with a given tag.

Agree on the scope (5 minutes)

Say up front what you're covering.

We'll designWe'll skip
Adding and removing tagsFull-text search
Showing an item's tagsTag suggestions
Finding everything with a tagThe products' own pages
Popular tagsBilling

Then agree on what "good" means:

  • Fast both ways: showing an item's tags and listing a tag's items should both be quick.
  • Safe: people should never see content they don't have access to.
  • Quick to update: a new tag should show up within a second or two.
  • Separate: each customer's tags stay in their own space.

Rough numbers (5 minutes)

Made up, but reasonable: 1 billion taggable items across all customers, about 3 tags each, and far more people reading tags than adding them.

3B
item-to-tag links
~300 GB
to store them, at about 100 bytes each
~2K
tag changes per second
~50K
tag look-ups per second
QuestionRough mathWhat it means
How big is one huge tag?A tag like 'bug' on a big siteCould be millions of items, so we'll need pages
Does it fit on one machine?300 GB, stored twicePossible at first, but we'll want to split it up
Reads or writes?50K reads vs 2K writesRead-heavy, so caching helps

The overall design (10 minutes)

Jira, Confluencetag somethingTag servicechecks and savesTag databaselinks, both waysChange queuetag eventsCounts and cachespopular tags
Products call one tag service. Changes are saved, then sent out so counts and caches can update.
  1. A product, like Jira, asks the tag service to add or remove a tag on one of its items.
  2. The tag service saves the link and records the change.
  3. Showing an item's tags or a tag's items reads straight from the tag database.
  4. Each change goes on a queue, and a background job keeps tag counts and popular tags up to date.
  5. Before showing results, we check the person can actually see each item.

What the apps need to do

ActionWho does itWhat's sent
Add or remove a tagProductCustomer site, item ID, item type and tag
Get an item's tagsProductCustomer site and item ID
Get a tag's itemsProduct or userCustomer site, tag, and a cursor for the next page
Popular tagsProductCustomer site, and optionally a product

What we store and where

DataWhere it goesWhy
TagsRelational databaseA small list of names per customer site
Item to tagsDatabase, keyed by itemFast 'what tags does this have?'
Tag to itemsDatabase, keyed by tagFast 'what has this tag?'
Tag countsCache, rebuilt from changesRead constantly, fine if slightly behind

Step 1: The data model (10 minutes)

Store every link twice: once organized by item, once organized by tag. That way both look-ups are a quick read. It costs some extra space and two writes per change, but tags change far less often than they're read.

Put the customer site in every key. Every query starts with the site, so one customer's data never mixes with another's, and later it gives us an easy way to split the data across machines.

A relational database is a fine start. If it gets too big, a wide-column store suits these 'give me everything under this key' look-ups well.

Step 2: Very popular tags (10 minutes)

A tag like 'bug' can be on millions of items, so never return them all at once. Return a page at a time, and use a cursor that says where the next page starts.

If one tag gets too big for one place, split its items into a few buckets and read from each in turn.

Counting a tag's items on every request is too slow. A background job reads the change queue and keeps running counts in a cache, like stream processing. Popular tags are read a lot, so caching them helps too.

Step 3: Permissions (10 minutes)

Each product owns its own permissions, and they change all the time. Copying them into the tag service is a good way to end up showing someone a page they shouldn't see.

So fetch a page of results, ask the product which ones the person can see, and drop the rest. If too many get dropped, fetch a bit more.

Choices you should be able to defend

We choseInstead ofBecauseWe'd reconsider if
Storing links twiceOne table with an indexBoth look-ups stay fast as things growWrites became the bottleneck
Checking permissions when readingCopying permissions into the tag serviceProducts stay the source of truthFiltering threw away most results
Counts kept by a background jobCounting on every requestCounting millions of items is slowExact counts became a requirement
Splitting by customer siteSplitting by tagEvery query starts with the siteOne customer got too big for its share

What could go wrong (5 minutes)

What happensWhat we do about it
The change queue falls behindTags still work. Counts and popular tags are just a bit out of date
One of the two writes failsRetry it, and a repair job compares the two copies and fixes any differences
Content gets deleted in a productThe product sends an event, and we remove its tags
A product's permission check is slowTime out and show fewer results, never unchecked ones
Someone retags 100,000 items at onceRun it as a background job, in batches

Follow-up questions (5 minutes)

How would you rename or merge tags?

Give tags IDs and store links by ID, so renaming is one change. Merging is a background job that moves links over in batches.

How would you suggest tags?

Look at tags on similar items in the same space, and popular tags in that project.

Could users follow a tag?

Read the change queue and notify followers, batching notifications so nobody gets spammed.

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–30The data model
30–40Very popular tags
40–50Permissions
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 Atlassian.