Implementing Comments and Likes in database
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Comments and likes look simple in the interface, but the database design underneath matters a lot. A good schema has to support fast reads, prevent duplicate likes, and handle deletion rules cleanly as posts and comments change over time.
Start with a Clear Comment Model
A comments table usually belongs to a parent entity such as a post, article, or photo. If you want threaded replies, add a nullable parent comment reference.
This schema supports both top-level comments and replies. A top-level comment has parent_comment_id set to NULL. A reply points to another row in the same table.
Model Likes as a Relationship, Not a Counter Alone
A like is usually a many-to-many relationship between users and target records. The most important rule is that one user should only be able to like the same target once.
Separate tables keep referential integrity simple:
The composite primary key prevents duplicate likes automatically. If a second insert tries to like the same post again, the database rejects it.
Separate Tables vs One Polymorphic Likes Table
Some systems use one table with columns such as target_type and target_id. That can work, but it weakens referential integrity because one foreign key cannot point to both posts and comments cleanly in a standard relational design.
Separate tables are often easier to reason about:
- foreign keys stay real
- cascade delete behavior is straightforward
- indexes are simpler and smaller
A polymorphic table can make sense if you support many likeable entities, but it usually needs extra application logic or triggers to maintain integrity.
Querying Counts Efficiently
The interface often needs comment counts and like counts. You can compute them on demand:
That works, but high-traffic applications often denormalize counts into columns such as posts.comment_count or posts.like_count. If you do that, keep the source-of-truth relationship tables anyway and update counters transactionally or through background jobs.
Think About Delete Behavior Early
Deletion rules affect user experience and data consistency. ON DELETE CASCADE is convenient when removing a post should automatically remove its comments and likes. However, some products prefer soft deletes so moderation history can be preserved.
That choice changes query logic, because soft-deleted comments should usually disappear from the interface while still existing in the database.
A Typical Insert Flow
Comment creation is a simple insert:
A like can use an upsert-style pattern to avoid duplicate errors surfacing to the user:
That gives you an idempotent "like" operation, which is helpful when clients retry requests.
Common Pitfalls
The most common design mistake is storing only a like counter and not the individual relationships. Without the user-to-target rows, you cannot prevent duplicate likes or answer who liked what.
Another issue is missing composite uniqueness. If the database does not enforce one-like-per-user-per-target, duplicate likes eventually appear.
Deeply nested comments can also become expensive to query. If you need unlimited threading, plan the retrieval strategy early instead of assuming a single self-reference solves the whole problem.
Summary
- Use a comments table with a self-reference when you need replies.
- Store likes as relationship rows, not only as counters.
- Enforce uniqueness with composite primary keys or unique constraints.
- Prefer separate like tables when you want strong foreign-key integrity.
- Add the right indexes and decide early between hard deletes, soft deletes, and denormalized counters.

