Developing Reviews and Ratings System in Mobile Apps
Without a reviews system, the app loses one of the main tools of social proof — users don't see others' experience and don't leave their own. But implementing it correctly is harder than it seems: skewed aggregated rating due to a few early reviews, photos that take 4 seconds to load, or moderation that bots bypass — these are typical problems encountered on rework.
What usually breaks in homemade implementations
Rating aggregation and update
Most common mistake — calculate average rating on the fly SELECT AVG(rating) on entire reviews table per each product page request. At 50,000 reviews this starts to slow down. Correct approach: denormalized average_rating and reviews_count fields on the server, updated via trigger or queue (Celery/Sidekiq/BullMQ) on adding/changing/deleting review. Client gets already ready value.
On mobile, show rating as star indicator — iOS and Android implement it differently. In UIKit build custom UIView with CALayer masks or assemble from five UIImageView with states .full, .half, .empty. In Jetpack Compose — Row with Icon and calculation via floor/ceil fractional value. Animate fill on first load via withAnimation (Compose) or UIView.animate with clip-mask width change.
Pagination and infinite scroll in reviews list
Classic OFFSET/LIMIT works poorly with large review count — at page 10,000 the database still scans the entire index to the needed offset. Use cursor-based pagination: sort by created_at DESC, id DESC, return next_cursor in response (base64 from last id + timestamp), next request passes it as parameter.
On iOS build list on UICollectionView with UICollectionViewDiffableDataSource — add new page via applySnapshot without flicker. prefetchDataSource requests next page when 3-4 cells remain. On Android — LazyColumn with LazyPagingItems from Paging 3.
Photos with review
Direct photo upload via main API is an anti-pattern. Correct scheme: client requests presigned URL from S3-compatible storage (AWS S3, Cloudflare R2, MinIO), uploads file directly there, then sends only object key to API. Compress before upload — on client: iOS via UIImage.jpegData(compressionQuality: 0.75), Android via Bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outputStream). Limit — 2-3 photos, max 5 MB per file after compression.
Display — via Kingfisher (iOS) or Coil (Android) with placeholder and crossfade 200ms. For gallery on tap — modal UIPageViewController or HorizontalPager in Compose with pinch-zoom.
How full implementation works
Data structure. Review contains: user_id, entity_id (product, service), entity_type, rating (1-5), body (text, optional), photos[], status (pending/approved/rejected), helpful_count, created_at. Indexes: (entity_id, entity_type, status, created_at DESC) for selecting approved reviews by object.
Moderation. Automatic pre-filter via profanity filter (library bad-words or custom list on backend) + manual review flag for reviews with keywords. Photos pass through AWS Rekognition Moderation Labels or Google Cloud Vision SafeSearch before publication. In moderator panel — queue with approve/reject and ability to reply to review.
Review reply. Business replies to review — this is separate entity review_reply (one-to-one with review). On publishing reply — push notification to author via FCM/APNs with deeplink to review.
Helpful voting. helpful_votes — separate table (user_id, review_id, UNIQUE). Limit: one vote per account. On client — optimistic counter update with rollback on error.
Purchase verification. If platform allows — mark reviews from real buyers with "Verified Purchase" badge, checking for closed order with user_id and entity_id.
Workflow stages
Audit current implementation (if exists) → design data schema and API → develop backend → mobile UI (both platforms or one) → integrate moderation → load testing (Artillery/k6 on "500 concurrent reviews" scenario) → publish.
For Flutter projects, all UI done once, logic extracted to ReviewBloc (BLoC) or ReviewNotifier (Riverpod).
Timeline
Basic system (star rating, text review, paginated list, status moderation) — 3-5 business days. With photos, business replies, helpful voting, and purchase verification — 8-12 days. Price calculated individually after requirements analysis.







