Build a following feed with current access checksLESSON 9.08 · 8 OF 22 IN CHAPTER
PART C / System design under constraints
Step 138 of 252
LESSON 9.08 · 8 OF 22 IN CHAPTERTry it, then open the solution

Build a following feed with current access checks

Application background

A reader opens a feed of posts from people they follow. The application gathers candidate post IDs and displays the posts the reader may currently see. Preparing those candidates ahead of time makes the feed faster.

That preparation is not enough on its own. A creator can delete a post or make it private after its ID has been placed in followers' feeds. A celebrity with millions of followers also makes copying every new post ID expensive.

Example walkthrough

01 · Try this input

Input / starting state
An ordinary creator publishes a post
Expected result
Make its ID available to followers.

02 · Try this input

Input / starting state
A celebrity publishes to ten million followers
Expected result
Use a strategy that does not require the same immediate work as a small account.

03 · Try this input

Input / starting state
A candidate post becomes private
Expected result
Check current access before displaying its content.

A feed entry is a pointer to a possible result. It is not a permanent permission to read the post.

Sizing that affects this decision

At 120,000 feed requests/s with 30 items per page, the service may need to consider 3.6 million candidate items/s. Separately, one celebrity post can target ten million follower feeds. Read-side checks and publication fan-out are different amounts of work, so size them separately.

These are exercise assumptions. The estimation reference explains the units and approximations. They do not establish the local demo's measured capacity.

Your assignment

Deliver: Build a following feed, including a strategy for authors with very large audiences. Check current deletion and privacy rules before displaying candidate posts.

Required behavior: GET /feed returns up to 30 currently authorized posts using a stable cursor. New eligible posts should appear within ten seconds under normal load. The precomputed feed stores candidate post IDs, rather than granting access to their content. Post visibility is checked before returning content.

The required first milestone is a working local implementation of the behavior above. The numbered implementation steps define the scope. The cloud architecture is a later extension, not something the starter has already provisioned.

Get the code and run the supplied example

The code is in the public junior-to-staff repository. Install Git and Python 3.12+. No AWS account or Python packages are required for this first run. If you already have a checkout, use it and skip cloning.

git clone https://github.com/Soulful-Iris/junior-to-staff.git
cd junior-to-staff
python3 examples/architecture-starts/social_feed.py

Supplied file: examples/architecture-starts/social_feed.py. You can also read or download the source here (download file, source below).

Read the supplied code · social_feed.py
read or download the source here · social_feed.py
"""Local mechanism demonstration for social-feed. No AWS resources are created."""
posts={'p1':{'text':'Team update','public':True},'p2':{'text':'Old private draft','public':False}}
feed=['p2','p1']; deleted={'p3'}
def visible(ids):
    return [posts[i]['text'] for i in ids if i not in deleted and i in posts and posts[i]['public']]
print('Warm feed candidates:',feed); print('Returned content:',visible(feed))
posts['p1']['public']=False
print('After revocation:',visible(feed))

This program is a mechanism demonstration: it runs the small scenario in one process and prints the result. It is not an HTTP service, a complete application, or an AWS deployment. A successful run demonstrates this mechanism only. It does not establish the workload or failure guarantees of the application you will build.

Example output from the supplied run:

Generated IDs and timestamps may differ. Compare the state transitions and outcomes.

Warm feed candidates: ['p2', 'p1']
Returned content: ['Team update']
After revocation: []

Set up your implementation workspace

Create work/social-feed/ in your checkout (or use a separate repository). Copy the supplied mechanism into that directory as mechanism.py, then extract its state transitions into functions you can call from your implementation. The record and module names below describe what you must implement. They are not a promise that files with those names already exist. Keep a README.md beside your implementation with its exact run commands and observed results.

Local components and state to implement

This table names the records, interfaces or decision inputs for your deliverable. Unless a name is explicitly linked to supplied source above, it is something you create. Implement the local state transitions first, then connect the HTTP, storage or worker boundaries required by the steps.

Record / module Key or interface Responsibility
posts author,post_id,version,visibility,deleted Current content and authorization authority.
feed_entries reader,sort_key,post_id Candidate references, never independent permission grants.
follow_edges follower,author,revision Relationship source for fan-out and read-time eligibility.

Implement the assignment

1. Build a read-time feed first

Query recent posts by followed authors and merge by a deterministic order. Use opaque cursors that preserve ordering across pages. Measure the cost before introducing fan-out. This baseline is also a recovery path when materialization lags.

2. Materialize ordinary-author candidates

Emit post events from an outbox and append idempotent references to follower inboxes. Record fan-out progress and age. Treat deletion as a source version change so late create events cannot resurrect a removed post.

3. Handle celebrities on read

Keep very large-author streams separate and merge them with the reader’s materialized candidates. Choose a threshold from measured fan-out cost and read frequency, not follower count alone. Bound candidate fetches so one empty/private stream cannot cause unbounded refill work.

4. Authorize before returning content

Batch-fetch current post metadata and apply membership, blocks, privacy and deletion rules. Cached candidates may survive. Restricted content must not. Define whether a cursor remains usable when eligibility changes, and tolerate shorter pages rather than leaking an item.

Demonstrate the completed local result

01 · Try this input

Input / starting state
Run the starting program
Expected result
A cached private candidate is filtered. Revoking the remaining post produces an empty result.

02 · Try this input

Input / starting state
Pause fan-out
Expected result
Freshness age rises and the bounded read-time fallback remains available.

03 · Try this input

Input / starting state
Deliver an old create after deletion
Expected result
Its version cannot make the post visible again.

Handoff: In your implementation README, include the start command, one successful operation, the failure case above and the resulting stored state or decision. State which dependencies are simulated. Someone with a fresh checkout should be able to reproduce this without your chat history.

Workload assumptions and capacity decisions

These are constructed exercise assumptions. The stated workload is a design target. The local demonstration does not establish that throughput. Use the estimation constants to check units before choosing capacity.

Input or objective Calculation / consequence
20 million daily users. 15,000 writes/s and 120,000 reads/s peak Read amplification, fan-out and content storage need separate capacity models.
30 items/page 120,000 reads/s can require 3.6 million candidate checks/s before batching and caching.
Celebrity with ten million followers One post can create ten million inbox writes. Use a different delivery strategy for such authors.

Map the local implementation to AWS

Deployment status: local only. Running the supplied command creates no AWS resources and configures no cloud connections. The diagram is a proposed deployment of the completed application. Each box needs either a deployed runtime, a provisioned service or an explicitly external dependency.

Read the diagram by following the arrows from the entry point: application code accepts the request or event, the state owner commits it, and any worker produces the later result. The table ties those roles to code and adapter work. Multiple boxes do not imply multiple Python files already exist.

Build a following feed with current access checks: AWS services, their general roles, and the primary data flow

The hybrid design spends writes for ordinary authors and reads for celebrities. Neither path owns privacy. Current source state decides which content can leave the service.

Local responsibility Cloud destination and role Implementation still required
Local HTTP boundary or the endpoint you will add Amazon API Gateway: feed HTTP entry Create routes and an integration. Translate requests and responses and configure identity validation.
Application or worker process Amazon ECS: feed assembly service Build a container and task definition. Supply configuration, task roles and graceful shutdown behavior.
Local dictionary, SQLite records or state model Amazon DynamoDB: post and relationship authority Design partition/sort keys and write a storage adapter with conditional updates or transactions. Python state and SQL are not uploaded as a database.
Local event sequence or input stream Amazon Kinesis: post-change stream Implement producer/consumer adapters, partition keys, durable acceptance and checkpoint/replay behavior.
Application or worker process Amazon ECS: fan-out workers Build a container and task definition. Supply configuration, task roles and graceful shutdown behavior.
Local cache, counter or coordination state Amazon ElastiCache: feed candidate cache Implement a Redis/Valkey adapter and atomic operations, expiry and unavailable-cache behavior. Keep the durable authority separate.

Provision resources, then connect the application

Resource or boundary Initial configuration and reason
Feed storage Partition inboxes by reader and time range. Bound retained candidates and large-author fan-out.
Caching Keep content authorization outside cached candidate membership. Never cache a shared private response under an unscoped key.
Operations Track ten-second freshness attainment, fan-out lag, candidate rejection rate and read amplification.

Use one disposable AWS environment for the cloud exercise. Put the named resources in infra/template.yaml or your existing IaC tool, pass resource IDs through configuration, and scope each runtime role to its own tables, buckets and queues. The diagram is a design to implement. It is not a claim that these resources have been deployed. Record the commands you used to deploy and remove the exercise resources.

For concrete provisioning commands, configuration wiring and cleanup, use the AWS foundation guide. It includes a deployable table/queue/object-storage foundation and explains which application and service adapters you still implement.

A provisioned queue or table does not make the local program use it. Configure resource IDs in the deployed runtime, replace the local adapter, and replay the same successful and failing operation against that runtime. Record the deployed commit and observable result, then remove the disposable resources using your infrastructure tool.

Extend the design after the baseline works

Worked follow-up: Revoke a creator without waiting for every feed copy to disappear

A popular creator has millions of copied feed entries. Deleting every projection first can take minutes or hours. Ranking snapshots solve pagination, but do not authorize serving revoked content.

Starting design Changed requirement
Feed projections contain copied post IDs and ranking data. A removed creator must stop appearing even while projection cleanup is behind.

Revised architecture. Follow the changed responsibility and failure path below. This is a design to implement. The supplied local example does not provision these components.

Diagram: Worked follow-up: Revoke a creator without waiting for every feed copy to disappear

What to implement. Keep source visibility and creator status authoritative. Before returning feed candidates, filter them using a revocation decision with a stated freshness bound and fail-closed behavior. Repair projections asynchronously. Use a snapshot or ranking version for pagination, but let revocation override that snapshot. Separate fanout workers from cleanup workers and budget both so a removal does not freeze new posts. The cache stores candidates, not an irrevocable permission grant.

Walk through the result. Fetch page one under rank version 12, remove creator C, then fetch page two while cleanup is paused. C must be filtered and pagination must not repeat prior visible posts. Explain that the page may contain fewer items. Hand over the cursor fields, revocation bound and repair-progress metric.

Introduce ranked ordering. Freeze or version enough ranking context to make pagination understandable, then explain how you avoid duplicate posts when new scores arrive between pages.

Additional design cases, alternatives and original source notes

Assume 20 million daily readers, 15,000 ordinary writes/s, 120,000 peak read requests/s, and a home page of 30 items. These are capacity assumptions, not observed company numbers. First clarify follow privacy, delete behavior, ranking versus recency, and the ten-second measurement point.

01 · Try this input

Input / starting state
Author publishes and refreshes immediately
Expected result
Own post visible from authoritative write path

02 · Try this input

Input / starting state
Eight-million-follower author posts
Expected result
Posting acknowledgement does not wait for eight million feed writes

03 · Try this input

Input / starting state
Follower is removed before feed read
Expected result
Former follower cannot read a now-private post from stale fanout

04 · Try this input

Input / starting state
One fanout partition lags
Expected result
Feed can show an explicit partial/freshness state. Catch up with cursor

Show the multiplication

Fanout on write copies or indexes each ordinary post into follower feed candidates. For a 500-follower account one post entails roughly 500 feed insertions. For eight million followers, four posts would imply 32 million candidate insertions in a minute. A hybrid design writes the post once, fanouts ordinary accounts, and merges popular-account posts at read time. Mark thresholds as measured operational choices. The 500/8m distinction is illustrative.

The fork shows why average follower count is a bad capacity input. Work for the viral branch moves to reads, so also estimate what happens to read amplification when many followed popular authors post at once.

What each store owns

Posts live in the source-of-truth store. Follower edges and privacy are authoritative elsewhere. Feed rows are a repairable projection. On pagination, pin a ranking/version watermark or specify how inserts move the page boundary. Stable IDs prevent duplicates when fanout and read-time merge both produce the same post. Cache an eligible candidate list, but recheck authorization on read for private content and revoke or filter cached rows when relationships change.

Put the AWS names on the boxes

Why these boxes, and what changes the choice: DynamoDB owns post IDs and versioned views. Aurora is a reasonable alternative for follower joins under smaller load. SQS decouples ordinary fanout but has duplicate delivery. Cache readers merge high-fanout authors. Neither queue nor cache replaces read-time privacy checks.

Senior follow-up: Queue backlog exceeds the ten-second freshness target. Derive backlog duration from ingestion and drain rates, choose whether to shed expensive ranking or delay social content, and define a user-facing freshness metric. A DLQ alone does not catch the main queue up.

Staff follow-up: A creator with eight million followers is removed for abuse while their post remains in millions of projections. Specify source-of-truth revocation, projection repair, regional invalidation, and audit evidence. A complete purge of every copy may take time. Access control cannot rely on that purge finishing.

Practice artifact: Baseline and hybrid box diagrams, fanout estimate, freshness calculation, one privacy revocation test, and a failure-injection recovery plan.

AWS translation: S3 for large media. DynamoDB/RDS for posts and follower relationships subject to access patterns. SQS/Kinesis for asynchronous fanout as appropriate. ElastiCache for hot candidates. Queue delivery can duplicate, so projection writes must be idempotent. Spotify's 2026 engineering discussion separates serving and evaluation responsibilities. This exercise makes the simpler feed/data ownership distinction without claiming to reproduce Spotify's design.

Sources and further reading · 1