Skip to content
EL
Contents

Design reviews and trade-off analysis

Review the notifications design

DecisionExpert5 min read25 min hands-on#data-modeling#operations#indexing

The idea in 30 seconds

A full design review: a short design document for a notifications feature, with numbers you measured in the practice page. The skill being practised is not finding every flaw but sorting them: block on the one-way doors that will be expensive to undo, comment on the two-way doors, and ask for the decision and its triggers to be written down.

Helps to know first: Database reliability engineering, Secure database engineering, Database migrations at scale, Database testing and correctness, Database bottlenecks and capacity planning (coming soon), Partitioning (coming soon)

On this page

The situation

You are asked to review this design before the team starts building. It is short, as most real ones are.

Review taskDesign: in-app notifications

Goal. Show users notifications (order shipped, refund issued, price drop) with an unread badge on every page.

Expected load. 40 million notifications a month. 2,000 page views a second at peak, each showing the badge. Most users get a few a week; marketplace sellers can get thousands a day.

Schema.

CREATE TABLE notifications (
  id         uuid PRIMARY KEY DEFAULT uuidv4(),
  user_id    bigint NOT NULL,
  kind       text NOT NULL,
  payload    jsonb NOT NULL,
  read_at    timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz                  -- soft delete
);
CREATE INDEX ON notifications (user_id);

Queries.

-- badge, every page view
SELECT count(*) FROM notifications WHERE user_id = $1 AND read_at IS NULL AND deleted_at IS NULL;
-- list, newest first
SELECT * FROM notifications WHERE user_id = $1 AND deleted_at IS NULL ORDER BY created_at DESC LIMIT 20;

Retention. Keep everything; storage is cheap. If the table gets too big we will partition it later.

Would you approve this design? Write the review you would leave.

Your call

Sorting what you find

A design review produces a list. Its value depends on how you sort it.

Blocking: one-way doors that are wrong or undecided. Here, retention and partitioning, and the primary key. They cost little to fix now and a project to fix later.

Non-blocking but important: two-way doors with a clear better option. Indexes, timeouts and query shapes can all change in a normal deploy. Say what you would do, and let the author decide when.

Questions: things you could not judge from the document. “What is the largest seller’s notification count?” is often more useful than any comment.

Marking every comment as blocking is the mistake Amazon’s letter warns about in decision-making generally: it slows everything down, and makes the comments that really matter harder to see.

Check yourself

  1. Why is “we will partition later” a blocking comment, while the missing partial index is not?

    Show answer

    An existing table cannot be converted into a partitioned one in place, so partitioning later means copying hundreds of millions of rows into a new table while the feature runs. An index can be added at any time with CREATE INDEX CONCURRENTLY.

  2. What does choosing uuidv7() instead of uuidv4() cost now, and what would the change cost in two years?

    Show answer

    Now, nothing: same type, same size, a different default. In two years, rewriting the table’s primary key and every reference to it, or living with a permanently larger, randomly written index.

  3. Turn “the badge query might be slow for big sellers” into a comment a team can act on.

    Show answer

    For example: “With only an index on user_id, the badge read 1,160 pages for a user with 50,000 notifications against 101 for a typical user; at 2,000 page views a second, please add a partial index on unread, undeleted rows, which read 10 pages in our test.”

Finished reading?

Saved in this browser only.

Sources

Where each claim on this page comes from.

  1. 1.
    2015 Letter to Shareholders
    SecondaryJeff Bezos (Amazon.com, Inc.)· Invention Machine

    Irreversible (one-way door) decisions deserve careful deliberation; reversible (two-way door) decisions should be made quickly.

  2. 2.
    PostgreSQL documentation: 5.12 Table Partitioning
    Primary· §5.12.1 Overview; §5.12.2 Declarative Partitioning, including Limitations

    Removing a partition is far faster than a bulk DELETE and avoids its vacuum overhead; a regular table cannot be turned into a partitioned table; a primary key on a partitioned table must include all partition key columns.

  3. 3.
    PostgreSQL documentation: 11.8 Partial Indexes
    Primary· §11.8 Partial Indexes

    A partial index holds only rows matching its predicate.

  4. 4.
    PostgreSQL documentation: 9.14 UUID Functions
    Primary· §9.14 UUID Functions

    uuidv4() is random; uuidv7() is time-ordered.

  5. 5.
    PostgreSQL documentation: E.4 Release 18
    Primary· E.4.1 Overview

    uuidv7() is new in PostgreSQL 18.

  6. 6.
    Documenting Architecture Decisions
    SecondaryMichael Nygard· whole article

    Recording decisions with context and all of their consequences.

Go further

What to read next, and why it is worth the time.

  1. 1.
    2015 Letter to Shareholders
    SecondaryJeff Bezos (Amazon.com, Inc.)· Invention Machine

    Two paragraphs on one-way and two-way doors that change how you spend review time.

Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.