Three Phase Boundaries, Three Different Bottlenecks
A feed that serves a thousand users and a feed that serves a million share a product surface and almost nothing underneath. The growth does not stress the system evenly, either: it stresses different components in sequence, and each phase boundary produces its own bottleneck. The first boundary, somewhere in the tens of thousands of users, is the database, where every feed request translates into expensive queries against growing tables. The second, deeper into six figures, is fan-out, where the act of posting becomes a write-amplification event. The third is money, where storage and delivery costs start shaping product decisions.
Knowing the boundaries in advance is what makes 'no rewrite' possible. Each phase has a known fix: read replicas and a cache tier for the first boundary, a fan-out strategy that separates cheap writes from expensive ones for the second, and lifecycle storage management for the third. None of the fixes require abandoning the architecture; they require that the architecture left room for them. Feeds fail to scale when the feed query, the timeline assembly and the storage layout are welded into one code path nobody can change without touching everything.
That is the actual insurance policy against a rewrite: draw the boundaries early. Keep feed generation behind an interface, publish content events rather than writing timelines directly, and store media in an abstraction rather than a hardcoded bucket path. A team that does this can change its fan-out strategy, its cache topology and its storage tiering independently as each boundary arrives. A team that skipped it discovers the coupling at the worst possible moment, which is when the product is growing fastest and downtime is most expensive.
Fan-Out: Push, Pull, and the Hybrid
The central design decision in any feed is when the timeline gets built. Push, or write-time fan-out, builds feeds when someone posts: the post is written into every follower's precomputed timeline, reads become cheap lookups, and writes become expensive, proportionally to follower count. Pull, or read-time fan-out, assembles the feed at request time from the posts of everyone a user follows: writes stay cheap and reads become expensive, growing with follow count and request rate. Each strategy is optimal for a workload, and every real social product contains both workloads.
The failure mode of pure push is the account with a large follower base: one post from a popular creator triggers thousands of timeline writes, and a burst of such posts backs the queue up until ordinary users' timelines go stale. The failure mode of pure pull is the popular feed itself: assembly cost grows with follow count, and the read path becomes the database's full-time job. The standard resolution is a hybrid, fan out at write time for ordinary accounts, fetch from high-follower accounts at read time, and merge both sources when the feed renders. Where the threshold sits is tunable and should move with your traffic.
Two mechanical details keep the hybrid honest. Fan-out jobs must be idempotent and replayable, because a crashed worker mid-delivery cannot be allowed to duplicate or lose a post for part of the audience. And a newly followed account must backfill recent content into the follower's timeline, or the follow feels broken for hours. Feeds are also honestly eventually consistent, a post appearing a few seconds late is invisible to users, but a missing post from a close connection is not, so consistency effort should follow relationship strength.
Cache Layers That Hold at the Boundary
The cache architecture that carries a feed to a million users has three layers, each answering a different question. A CDN answers 'is this media bytes anywhere nearby,' handling the overwhelming majority of traffic because feeds are mostly images and video. An object cache answers 'what are the IDs in this feed,' holding precomputed timelines as lists of post identifiers. The hydration layer answers 'what are these posts,' fetching post objects, author profiles and engagement counts in batch. Feeds built this way read like database queries and behave like cache hits.
The discipline that keeps caches correct is staleness policy, decided per surface rather than inherited globally. A feed that is thirty seconds old is fine; a notification count that is thirty seconds stale feels broken; a deleted post still visible in a cached feed is a moderation incident. Version the cache keys so takedowns propagate, set TTLs per surface, and write invalidation explicitly for anything user-visible about removal. The budget conversation matters too: decide what share of reads may reach the backing store at peak, and size the cache tier so the answer is 'almost none.'
Precomputation is the final layer, and the one teams forget until p99 latency ruins an app release. The most active users, the ones with the largest follow graphs and the highest session frequency, are exactly the users whose feeds are most expensive to assemble cold. Keeping their timelines warm on a schedule converts the worst latencies into the best, at a cost you can measure. Cache hit rates per tier should be a first-class dashboard metric from the start, because cache efficiency degrades quietly as the follow graph densifies, and the degradation shows up as user-facing latency before it shows up anywhere else.
The Moderation Pipeline Is Part of the Architecture
Moderation load scales with content volume, which means at a million users it is a subsystem with its own engineering, not a queue an admin checks between other tasks. The pipeline that survives growth starts at upload: automated triage of media and text before or immediately after publication, classifiers scoring for policy categories, and reports from users flowing into the same funnel. Everything routes by risk, obvious violations handled automatically, ambiguous cases queued for human review, and clean content never touching a human at all.
The human layer needs tooling as a design requirement, not a courtesy. Reviewers need the content in context, the reporting history of the account, the relevant policy, and a one-action decision interface; review throughput per moderator is an engineering metric like any other. Escalation paths and appeals need owners and response-time expectations, because appeals volume is a leading indicator of false positives in the automated layer, and false positives corrode community trust faster than the violations they prevent.
Architecturally, moderation must be a producer and consumer in the same event pipeline as the feed itself. A takedown is an event, and it propagates everywhere the content exists: timelines, caches, CDN objects, notification history. Systems where moderation updates the database but not the cache layer ship the category of bug where removed content lives on in feeds for hours, which is the specific failure that turns a moderation incident into a press incident. We build moderation hooks into the social platform feature set from the first sprint for exactly this reason.
Storage Economics
Media dominates storage economics so thoroughly that posts, indexes and metadata are a rounding error. Every upload carries a train of derivatives, thumbnails, transcoded renditions at multiple resolutions, and the storage bill is the sum of keeping all of it, forever, in at least two geographic locations. The decisions that control the bill are made early and casually: whether originals are kept, how many renditions are generated, and what happens to media attached to deleted accounts. Change any of them after a million users and the migration is measured in petabytes.
Lifecycle policy is the main lever. Hot storage for recent media, cooler tiers for content that has not been requested in months, and deletion or archival for accounts that left long ago, tiering is boring until the invoice arrives. Soft delete, where content disappears from the product immediately but remains recoverable for a retention window, then purges permanently, needs to be an explicit pipeline rather than a good intention. Cost per active user is the metric to track monthly; it should be stable or falling as optimizations land, and any rise is a product decision being made by accident.
Egress deserves its own attention, because delivery frequently exceeds storage cost. Per-demand transcoding instead of generating every rendition upfront, resolution caps that match real device needs, and edge caching tuned by access pattern routinely reshape the economics more than any storage-tier negotiation. The habits built at ten thousand users, what gets stored, what gets derived, what gets deleted, are the ones that determine whether a million-user storage bill is a line item or a crisis. Design them deliberately, and the growth that breaks competitors becomes your growth curve.