A follow system looks deceptively simple.
At first glance, it’s just:
- Follow a user
- Unfollow a user
- Show follower and following counts
But once real users enter the system, a follow feature quickly evolves from basic CRUD into a distributed systems problem.
Designing this correctly requires thinking about scale, consistency, and user experience - not just database operations.
Why Follow Systems Are Not Just CRUD
CRUD assumes:
- Independent operations
- Simple data flows
- Minimal side effects
A follow action violates all three.
When one user follows another:
- Two users’ states change
- Feeds may need to update
- Notifications may be triggered
- Counts must stay accurate
This single action can ripple through multiple parts of the system.
Core Data Model
A typical follow system uses a join table:
follows
-------
follower_id
following_id
created_at
With constraints:
(follower_id, following_id)must be unique- A user cannot follow themselves
This table becomes hot very quickly in large systems. Indexes and constraints are not optional - they are fundamental.
Handling Idempotency
One of the most common issues in follow systems is duplicate requests.
Users may:
- Double-click the follow button
- Retry requests due to network issues
Without idempotency:
- Follower counts inflate
- Data becomes inconsistent
The solution is simple but essential:
- Enforce a unique constraint at the database level
- Treat duplicate follow requests as no-ops
Idempotency turns repeated actions into safe operations.
Consistency vs Performance
A key design decision is how follower counts are handled.
Option 1: Calculate Counts Dynamically
- Always accurate
- Expensive at scale
Option 2: Store Counts Separately
- Fast reads
- Requires careful updates
Most production systems choose a hybrid approach:
- Store counts for fast reads
- Periodically reconcile for correctness
This trade-off is unavoidable in large-scale systems.
Feeds and Fan-Out Strategy
Follow systems directly impact feed generation. Two common approaches exist:
Fan-Out on Write
- Push new posts to followers’ feeds
- Fast reads
- Expensive writes
Fan-Out on Read
- Generate feed at request time
- Cheaper writes
- Slower reads
The right choice depends on user behavior, read/write ratios, and infrastructure constraints.
Edge Cases That Matter
Some edge cases are easy to overlook:
- Blocking a user
- Private accounts (request vs. follow)
- Deactivated users
- Race conditions during follow/unfollow
Ignoring these early leads to painful refactors later. Good backend design anticipates these scenarios upfront.
Designing for Scale From Day One
A follow system will almost always grow. Designing with scale in mind means:
- Proper indexing
- Clear data ownership
- Idempotent APIs
- Thoughtful feed architecture
Even small applications benefit from these decisions early.
Final Thoughts
Follow systems are a perfect example of how backend engineering goes beyond CRUD. They force you to think in terms of:
- System behavior
- Failure scenarios
- Trade-offs at scale
Good backend systems aren’t defined by how they work on day one, but by how well they hold up as usage grows.