diff --git a/.planning/research/ARCHITECTURE.md b/.planning/research/ARCHITECTURE.md new file mode 100644 index 0000000..53e3d70 --- /dev/null +++ b/.planning/research/ARCHITECTURE.md @@ -0,0 +1,315 @@ +# Architecture Patterns + +**Domain:** Pet-first social media platform +**Researched:** 2026-06-05 +**Confidence:** HIGH (well-established social platform patterns; training knowledge cross-referenced against known Instagram/Twitter/TikTok architectural public disclosures) + +--- + +## Recommended Architecture + +OnlyPets follows a **layered monolith → service-extract path**, starting as a well-structured monolith with clear internal boundaries that can be split later if scale demands it. For v1, a single deployable backend (Next.js API routes or a dedicated Node/Express API) with explicit domain modules is the right call — microservices from day one are premature and costly. + +``` +Browser (React / Next.js) + │ + ▼ + API Layer (REST or GraphQL) + ├── Auth Service (owner accounts, sessions) + ├── Pet Service (profiles, follows, breed/species) + ├── Content Service (posts, stories, milestones, media) + ├── Feed Service (home feed assembly, explore) + ├── Engagement Service (likes, reactions, comments, paw-backs) + ├── Messaging Service (DMs between pet profiles) + ├── Health Service (weight logs, vet visits, vaccines) + └── Notification Service (push/in-app) + │ + ┌────┴────────────────────────┐ + │ │ +Primary DB Media Storage +(PostgreSQL) (S3-compatible) + │ + CDN (CloudFront + / Cloudflare) + │ + ─────┴────── + Cache Layer + (Redis) +``` + +--- + +## Component Boundaries + +### Owner / Auth Component +**Responsibility:** Sign-up, login, sessions, JWT/cookie management, owner profile. +**Owns:** `users` table, `sessions`, auth middleware. +**Communicates with:** Pet Service (to fetch owned pet profiles after login), all other services (supplies authenticated `owner_id` on every request). +**Does NOT own:** Pet identity — the pet profile is a separate entity that the owner merely manages. + +### Pet Profile Component +**Responsibility:** Pet identity — name, species, breed, bio, avatar, creation, management. +**Owns:** `pets` table, `follows` (pet-to-pet follow graph), `species`, `breeds` tables. +**Communicates with:** Content Service (which `pet_id` authored a post), Feed Service (follow graph is the input to feed assembly), Engagement Service (reacting as a pet identity), Health Service (pet is the subject of health records), Auth Component (authorization — only owner of pet can mutate). +**Key design rule:** Every action in the system is performed by a `pet_id`, not an `owner_id`. The API receives `owner_id` from auth but resolves it to the active `pet_id` the owner is acting on behalf of. + +### Content Component +**Responsibility:** Creating, storing, retrieving posts (photo, video, milestone, story), caption, media reference, tags. +**Owns:** `posts`, `stories`, `milestones`, `media_attachments`, `hashtags`, `post_tags` tables. +**Communicates with:** Media Storage (upload presigned URLs, store media keys), Pet Service (author identity), Feed Service (post events fan out to followers), Engagement Service (posts receive reactions). +**Stories rule:** Stories have a 24-hour TTL. Implement as a `expires_at` column with a background job purging expired rows; do not hard-delete immediately so grace period / moderation is possible. + +### Media Storage Component +**Responsibility:** Upload orchestration, CDN delivery, video transcoding. +**Owns:** No database tables — coordinates with S3-compatible storage and a CDN. +**Pattern:** Client requests a presigned URL from the API; client uploads directly to S3 (never through the API server). After upload, client notifies API of completion; API stores the S3 key in `media_attachments`. Video transcoding (thumbnail extraction, resolution variants) runs asynchronously via a queue. +**Communicates with:** Content Component (stores keys), CDN (serves media). + +### Feed Component +**Responsibility:** Assembling the home feed (posts from followed pets, ordered by recency/relevance) and the Explore page (breed/species discovery, trending hashtags). +**Owns:** `feed_items` (fan-out cache, Redis sorted sets by timestamp), Explore index. +**Two patterns — choose one:** +- **Fan-out on write (push model):** When a pet posts, write the post ID to every follower's feed sorted set in Redis. Fast reads, expensive writes for accounts with many followers. Correct for v1 (pets won't have millions of followers at launch). +- **Fan-out on read (pull model):** Merge followed-pet timelines at read time. Cheaper writes, slower reads. Better at scale with celebrities. +**Recommendation:** Fan-out on write for v1. If a pet accumulates >10K followers, switch that pet to pull-on-read hybrid (Twitter's celebrity hybrid approach). +**Communicates with:** Pet Service (follow graph), Content Service (post data), Redis (feed cache). + +### Engagement Component +**Responsibility:** Likes, paw-print reactions, comments, paw-backs (reposts). +**Owns:** `reactions`, `comments`, `pawbacks` tables; reaction counters (denormalized on `posts` table or Redis counters). +**Communicates with:** Content Component (post must exist), Notification Service (trigger notification to post author), Feed Component (paw-backs fan-out as new post events). +**Counter strategy:** Use Redis atomic increments for hot counters; periodically flush to PostgreSQL. Avoids lock contention on high-engagement posts. + +### Messaging Component +**Responsibility:** Direct messages between pet profiles, thread management, read receipts. +**Owns:** `dm_threads`, `dm_messages`, `dm_participants` tables. +**Communicates with:** Pet Service (resolve participants), Notification Service (new message notification). +**Real-time:** WebSocket (Socket.io or native WS) for delivery; fall back to polling for MVP. Store messages in PostgreSQL; cache recent thread list in Redis. + +### Health Tracking Component +**Responsibility:** Weight logs, vet visits, vaccine records, shareable health card. +**Owns:** `health_events`, `weight_logs`, `vet_visits`, `vaccines` tables. +**Communicates with:** Pet Service (which pet), Content Component (milestone posts can reference health events, e.g. "got vaccinated today"). +**Shareable card:** A public `/pets/:id/health-card` route renders a read-only summary — no separate service needed. Token-based sharing (signed URL with expiry) for private pets. + +### Notification Component +**Responsibility:** In-app notifications (likes, comments, new followers, DMs), future push notifications. +**Owns:** `notifications` table, notification preferences. +**Communicates with:** All components (receives events), client (WebSocket or SSE for real-time delivery). +**Pattern:** Event-driven — components emit notification events to a queue (Redis pub/sub or a simple in-process event emitter for v1); Notification Service consumes and fans out. + +--- + +## Data Flow + +### Post Creation Flow +``` +Owner (browser) + → POST /api/posts (with caption, tags, pet_id) + → API validates owner owns pet + → API issues S3 presigned upload URL + ← Returns presigned URL to client + +Owner uploads media directly to S3 + → POST /api/posts/:id/confirm (upload complete) + → Content Service stores media_attachment key + → Queues async job: transcode video / generate thumbnail + → Feed Service fans post out to followers' Redis sorted sets + → Notification Service notified (no-op for own post) + +Followers open app + → GET /api/feed + → Feed Service reads Redis sorted set for pet's feed + → Hydrates post data from Content Service + ← Returns paginated feed +``` + +### Follow Flow +``` +Pet A follows Pet B + → POST /api/pets/:b_id/follow (actor = Pet A) + → Pet Service writes follow edge to `follows` table + → Feed Service backfills recent posts from Pet B into Pet A's feed cache + → Notification Service notifies Pet B's owner +``` + +### Story Expiry Flow +``` +Background job (cron, every hour) + → SELECT * FROM stories WHERE expires_at < NOW() + → Mark as expired (soft delete) + → Schedule S3 media deletion (async, after CDN TTL) +``` + +### DM Flow +``` +Owner sends DM as Pet A to Pet B + → POST /api/dm/threads/:thread_id/messages + → Messaging Service stores message + → Emits event to Notification Service + → WebSocket pushes to Pet B's owner if online + → Otherwise: in-app notification on next open +``` + +--- + +## Patterns to Follow + +### Pattern 1: Pet-as-Actor (not Owner-as-Actor) +**What:** Every mutation in the API is performed by a `pet_id`. The auth token identifies the `owner_id`, but the request always includes an `active_pet_id`. Middleware validates the owner owns that pet before proceeding. +**When:** Every single API call that mutates state. +**Why:** This is the architectural expression of "pets are first-class identities." Without it, the owner identity bleeds through at the data layer and the pet-first UX becomes a facade. + +```typescript +// Middleware pattern +async function requirePetOwnership(req, res, next) { + const { pet_id } = req.body ?? req.params; + const owner_id = req.auth.owner_id; + const pet = await db.pets.findOne({ id: pet_id, owner_id }); + if (!pet) return res.status(403).json({ error: 'Forbidden' }); + req.pet = pet; + next(); +} +``` + +### Pattern 2: Presigned Upload (Client-to-S3) +**What:** API issues presigned S3 URL; browser uploads directly; API confirms completion. +**When:** All media uploads (photos, videos, story media). +**Why:** API server is never a media pipe. Avoids memory pressure, timeouts, and bandwidth costs on the API tier. + +### Pattern 3: Denormalized Counters +**What:** Store like/comment/follower counts as columns on the parent row (or Redis counters), not as SELECT COUNT(*) at read time. +**When:** Any count displayed in the UI (likes on a post, follower count on a profile). +**Why:** COUNT(*) queries on engagement tables do not scale. Increment/decrement atomically on write; resync periodically as a safeguard. + +### Pattern 4: Cursor-Based Pagination +**What:** Use `cursor` (last seen ID or timestamp) not `OFFSET` for all feed and list endpoints. +**When:** Feed, explore, comments, follower lists. +**Why:** OFFSET pagination degrades as the dataset grows (full table scan to find page N). Cursor pagination is O(log n) with a proper index. + +### Pattern 5: Soft Deletes for Content +**What:** Posts, stories, comments are never hard-deleted immediately. Set `deleted_at` timestamp; purge asynchronously. +**When:** All content deletion. +**Why:** Enables moderation windows, prevents orphaned engagement records, simplifies CDN cache invalidation scheduling. + +--- + +## Anti-Patterns to Avoid + +### Anti-Pattern 1: Owner as the Social Identity +**What:** Building follows, posts, and feeds around `owner_id` and "mapping" pets on top. +**Why bad:** Requires a translation layer everywhere; makes pet-to-pet follows awkward; forces the "pet is a profile" to be a UI trick rather than a data truth. +**Instead:** `follows` table is `(follower_pet_id, followee_pet_id)`. Posts have `author_pet_id`. The owner is an administrative actor, not a social one. + +### Anti-Pattern 2: Microservices from Day One +**What:** Splitting Auth, Feed, Content, Messaging into separate deployable services immediately. +**Why bad:** Network latency between services, complex distributed tracing, operational overhead, harder local development — all for a v1 with unknown load. +**Instead:** Well-named modules within a monolith. Extract to separate services at the boundary that actually becomes a bottleneck (likely Feed or Media at scale). + +### Anti-Pattern 3: Storing Media on the App Server +**What:** Receiving uploads in the API process, writing to disk or forwarding to S3 server-side. +**Why bad:** Memory spikes, request timeouts, horizontal scaling becomes stateful, no CDN path. +**Instead:** Presigned S3 upload from the browser. API only handles metadata. + +### Anti-Pattern 4: OFFSET Pagination on Feeds +**What:** `SELECT * FROM posts LIMIT 20 OFFSET 200`. +**Why bad:** PostgreSQL must scan and discard the first 200 rows on every paginated request. Gets progressively slower as users scroll. +**Instead:** Cursor pagination: `SELECT * FROM posts WHERE id < :cursor LIMIT 20`. + +### Anti-Pattern 5: Synchronous Feed Assembly on Every Request +**What:** On every `GET /feed`, querying all followed pets' posts and merging in real-time. +**Why bad:** Feed query cost grows linearly with follow count. At 200 follows, the query touches 200 × N rows per request. +**Instead:** Pre-computed fan-out feed in Redis (push model). Feed reads are O(1) range queries on the sorted set. + +### Anti-Pattern 6: Real-Time for Everything from Day One +**What:** Building WebSocket connections for notifications, DMs, story views, likes — all at launch. +**Why bad:** WebSocket infrastructure (connection management, reconnection, load balancer sticky sessions) is non-trivial. Diverts build time from product features. +**Instead:** Polling for notifications and feed updates at launch. Add WebSocket for DMs first (highest user expectation), then notifications. Stories are pull-based anyway. + +--- + +## Scalability Considerations + +| Concern | At 1K users | At 100K users | At 10M users | +|---------|-------------|---------------|--------------| +| Feed assembly | In-memory merge, short query | Redis fan-out, keep it | Celebrity hybrid (push+pull) | +| Media serving | S3 direct + basic CDN | CDN required, cache headers | Multi-region CDN, image optimization service | +| Database | Single PostgreSQL | Read replicas for feed/explore | Shard by pet_id, separate OLAP | +| Search / Explore | PostgreSQL full-text (tsvector) | Elasticsearch or Typesense | Dedicated search cluster | +| DMs | PostgreSQL + polling | WebSocket + Redis pub/sub | Kafka message bus | +| Story expiry | Cron job | Cron job | Queue-based worker fleet | + +--- + +## Suggested Build Order + +Build order is driven by data dependencies — a component cannot be tested end-to-end without its dependencies. + +``` +1. Auth Component + └── All other components depend on authenticated owner_id + +2. Pet Profile Component + └── All social actions require pet_id as the actor + +3. Media Storage Component (presigned upload pipeline) + └── Content depends on media being uploadable + +4. Content Component (photo + milestone posts first) + └── Feed depends on content existing + +5. Feed Component (home feed, fan-out) + └── Core engagement loop — must exist before Explore + +6. Engagement Component (likes, comments, paw-backs) + └── Depends on content; increases feed value + +7. Explore / Discovery (breed/species browse, hashtags, search) + └── Depends on content volume to be meaningful + +8. Stories Component + └── Same content pipeline as posts; add TTL and expiry job + +9. Health Tracking Component + └── Independent feature; can build any time after Pet Profile + +10. Messaging Component (DMs) + └── Independent of feed; requires Pet Profile and Auth + +11. Notification Component + └── Ties all components together; implement last for clean event contracts +``` + +**Phase groupings implied by build order:** + +- **Phase 1 (Foundation):** Auth + Pet Profile + basic Media pipeline +- **Phase 2 (Content Core):** Photo posts + Milestone posts + Home Feed +- **Phase 3 (Engagement):** Likes, Comments, Paw-backs + Explore/Discovery +- **Phase 4 (Content Depth):** Stories (with expiry) + Video posts + Hashtag search +- **Phase 5 (Pet Health):** Health tracking + Shareable health card + Milestone integration +- **Phase 6 (Social Depth):** Direct Messaging + Real-time notifications (WebSocket for DMs) +- **Phase 7 (Polish):** Notification centre, Explore tuning, Breed/species browse improvements + +--- + +## Key Architectural Decisions + +| Decision | Recommendation | Rationale | +|----------|---------------|-----------| +| API style | REST with resource-per-domain | GraphQL adds complexity; REST is sufficient for defined feature set | +| Database | PostgreSQL | Relational model fits follow graph, structured health records, content metadata | +| Cache | Redis | Sorted sets for feeds; pub/sub for events; session store | +| Media | S3 presigned upload + CDN | Never route media through API tier | +| Feed model | Fan-out on write (push) | Simple, fast reads; acceptable write cost at v1 scale | +| Auth | JWT (short-lived) + refresh tokens | Stateless, works for both web and future mobile | +| Real-time | Polling → WebSocket for DMs | Ship polling first; WebSocket is a surgical upgrade | +| Search | PostgreSQL full-text (tsvector) for v1 | Good enough for launch; swap to Typesense at scale | +| Monolith vs microservices | Modular monolith for v1 | Extract at proven bottleneck, not upfront | + +--- + +## Sources + +- Knowledge derived from public architectural disclosures: Instagram Engineering Blog (fan-out feed design), Twitter/X architecture papers (push/pull hybrid for celebrities), Uber Engineering blog (modular monolith patterns) +- Standard system design references: "Designing Data-Intensive Applications" (Kleppmann), "System Design Interview" (Alex Xu) — feed design, cursor pagination, presigned upload patterns +- Confidence: HIGH for core patterns (fan-out feeds, presigned upload, pet-as-actor model, build order); MEDIUM for specific tooling choices (PostgreSQL full-text vs. Typesense threshold, Redis sorted sets for feeds) — these are well-established but exact thresholds are project-specific diff --git a/.planning/research/FEATURES.md b/.planning/research/FEATURES.md new file mode 100644 index 0000000..2a270d3 --- /dev/null +++ b/.planning/research/FEATURES.md @@ -0,0 +1,166 @@ +# Feature Landscape + +**Domain:** Pet social media platform (pets as primary identities) +**Researched:** 2026-06-05 +**Confidence note:** Web search and fetch tools were unavailable in this session. All findings draw from training-data knowledge of platforms including Instagram pet accounts, TikTok pet creator accounts, BarkPost, Dogster, Petfinder community features, and Wag/Rover social layers. Confidence is MEDIUM overall; live competitor verification is recommended before locking scope. + +--- + +## Table Stakes + +Features users expect. Missing = product feels incomplete or untrustworthy. + +| Feature | Why Expected | Complexity | Notes | +|---------|--------------|------------|-------| +| Pet profile with photo, bio, breed, species | Core identity — every comparable platform has this. Without it a user has nothing to show. | Low | Must include avatar/cover photo upload. Breed is a primary discovery axis. | +| Photo posts with captions | Absolute baseline for any social platform. Pet owners already do this on Instagram. | Low | Single image is MVP; multi-image carousel is table stakes too (pets look better in bursts). | +| Short video posts | TikTok/Reels has set the expectation. Short-form video is now table stakes, not a differentiator. | Medium | 15–60 s limit keeps storage manageable. Requires transcoding pipeline. | +| Stories (24 h disappearing) | Instagram established this norm in 2016; users expect it on any social platform. | Medium | Story rings on profile, viewer list, auto-expiry. | +| Home feed from followed pets | Without a personalized feed there is no reason to return daily. | Medium | Chronological is simpler than algorithmic for v1 but feels less curated. | +| Likes / reactions on posts | The most basic engagement loop. A post with no feedback mechanism feels dead. | Low | "Paw print" reaction fits the pet theme without breaking user expectation. | +| Comments on posts | Users want to compliment pets, ask about breeds, share empathy on health posts. | Low | Requires moderation strategy (see pitfalls). | +| Follow / unfollow other pets | Core social graph primitive. Without it you have a gallery, not a social platform. | Low | Pet-to-pet follow (not owner-to-owner) is the OnlyPets differentiator but the mechanic is table stakes. | +| Search / hashtag discovery | Users must be able to find their breed community. Without search the platform feels closed. | Medium | Hashtags AND breed/species filter search is needed. Full-text is optional. | +| Explore / discover page | Organic growth requires non-follower users to stumble on content. | Medium | Breed-filtered explore is uniquely useful here vs generic social. | +| Notifications (likes, comments, follows, mentions) | Without notifications users don't know engagement happened and stop returning. | Medium | Push via browser (web push) for web-first v1. | +| Account settings, profile editing, password reset | Authentication hygiene. Users distrust platforms that can't handle password recovery. | Low | Standard auth flows. | +| Photo upload with basic crop/resize | Raw photos from phones are huge. Basic crop for profile photo is expected. | Low | Client-side resize before upload saves bandwidth. | +| Pet account switching | Most owners have multiple pets. Switching must be frictionless. | Low | Session-level context switch; common in multi-profile apps (Instagram does this). | +| Blocking / reporting | Safety baseline. Without it the platform is unsafe for vulnerable users (kids with pets). | Medium | Block hides content, report queues for manual review. | + +--- + +## Differentiators + +Features that set OnlyPets apart. Not universally expected, but valued — and some are core to the product thesis. + +| Feature | Value Proposition | Complexity | Notes | +|---------|-------------------|------------|-------| +| Pets as first-class identities (not a hashtag) | The core differentiator. On Instagram the human is the identity; here the pet is. Profile URL is the pet's name, not the owner's. | Medium | Requires dual-entity model: owner account + pet profiles, each with separate identity. | +| Milestone posts (special format) | Pet owners love marking firsts — first walk, adoption anniversary, birthday, first bath. A structured milestone post type (with date, milestone category, photo) creates shareable moments and long-term profile value. | Medium | Milestone timeline becomes a life record. Drives anniversary re-engagement (push: "Fluffy's adoption anniversary is tomorrow!"). | +| Pet health card / shareable vet record | Weight logs, vaccine dates, vet visit history, flea treatment. A shareable digital health card that owners can print or send to new vets. No mainstream social platform has this. | High | Privacy complexity (health data). Shareable URL with token. Differentiates from Instagram completely. | +| Breed / species browse (filtered explore) | On Instagram you search #labrador and get 40M posts. OnlyPets browse by breed returns only profiles. Useful for prospective owners researching breeds. | Medium | Breeds as structured taxonomy (not free-text). Standard lists per species (AKC dogs, CFA cats, common bird species). | +| Paw-backs (pet-native repost) | Sharing another pet's post to your profile maintains the pet-to-pet social graph. Twitter/X-style repost but framed as "Fluffy pawed this back." | Low | Simple repost with attribution. Optional quote-paw (with added caption). | +| Milestone-triggered anniversary notifications | "Today is Biscuit's 2nd adoption anniversary — share a memory!" Drives re-engagement on meaningful dates without feeling spammy. | Low (after milestones exist) | Requires milestone date storage and scheduled notification job. | +| Pet-to-pet direct messages | DMs between pet profiles, managed by owners. Allows breed communities and playdate coordination without surfacing owner real names prominently. | Medium | Requires message storage, read receipts, conversation list. | +| Multi-pet household management | One owner, many pets — switching and managing all from a single owner account is a real-world need. | Low | Account switcher with pet list; separate feeds per pet. | +| Species-specific content formatting | Bird owners want to log vocalizations; dog owners track walk distances; cat owners track indoor-only status. Species-aware profile fields signal that OnlyPets actually understands pets, not just social media. | Medium | Optional extended profile fields per species. Do not over-engineer v1. | +| Adoption origin story field | "Rescue from Austin shelter, age 2 when adopted" — a structured field for this creates emotional narrative and differentiates pet profiles from human profiles. | Low | Simple structured field on profile setup. | + +--- + +## Anti-Features + +Features to explicitly NOT build — they add complexity, harm the core value proposition, or are known ecosystem pitfalls. + +| Anti-Feature | Why Avoid | What to Do Instead | +|--------------|-----------|-------------------| +| Algorithmic feed (v1) | An algorithm requires substantial training data, engineering time, and creates engagement-optimization pressure that can degrade content quality. Instagram's algorithm is also widely blamed for making discovery worse. | Ship chronological feed. Add "Popular in your breeds" section as a simple query, not ML. Revisit algorithmic ranking only after 10K+ active pets. | +| Human lifestyle content bleeding in | The brand promise is pet-exclusive. If owners post selfies, travel photos, food — the platform becomes Instagram Lite. | Enforce at onboarding: posts require at least one pet profile tagged. Do not provide a generic "post without a pet" flow. | +| Long-form video (>60 s) | Storage, transcoding, CDN costs scale non-linearly. Long-form video also attracts different content (vlogs, tutorials) that dilutes the pet focus. | Cap at 60 s hard. Direct users to YouTube/TikTok for longer content with a note. | +| Marketplace / pet sales listings | Selling pets online is legally complex (state regulations on pet sales, USDA licensing), ethically fraught (backyard breeders, puppy mills), and a massive moderation burden. | If commerce is needed later, integrate with reputable adoption APIs (Petfinder, Adopt-a-Pet). Never build a raw marketplace. | +| Pet dating / breeding matchmaking (v1) | While there is a real niche (find a stud dog), it is legally and ethically complex, and a distraction from the social platform core. | Defer entirely. If demand is proven, build as a separate product or partner. | +| Follower count as primary status signal | Leaderboards, "top pets," follower count prominently displayed — this drives inauthentic behavior (follow/unfollow games) and makes new pets feel unwelcome. | Show follower counts on profile but do not surface leaderboards or "popular pets" rankings prominently in v1. | +| Real-time features (live video, live chat) | WebSockets + streaming infrastructure is a significant engineering investment. Pet owners are unlikely to go live — this is a human-identity feature. | Standard async posts and DMs are sufficient. Revisit only with strong demand signal. | +| Owner personal profiles (public-facing) | If owners get their own public profiles, the platform identity shifts from pet-centric to human-centric. | Owner accounts are backend management only. No public owner profile page. Owner identity surfaces only as "managed by @danielb" on the pet profile — minimal. | +| Overloaded notification types | Platforms that notify for every micro-interaction (someone viewed your story, your post is trending, new suggested pet) train users to ignore all notifications, then disable them. | Send notifications only for: new follower, new comment, new like (batched), new DM, milestone anniversary. | +| Vet / health data monetization | Selling aggregated health data or showing ads targeted by vet visit history would destroy trust immediately if discovered. | Never aggregate health data for advertising. Health card is a user-benefit feature only. Make this a public privacy commitment. | +| Pet "verification" badges (v1) | Celebrity pet badges create a two-tier system that discourages small accounts. Also hard to enforce fairly. | No verification badges in v1. All pets are equal. | + +--- + +## Feature Dependencies + +``` +Owner account (auth) → Pet profile creation +Pet profile → All content types (photo, video, story, milestone) +Pet profile → Follow graph +Follow graph → Home feed +Home feed → Feed notifications (likes, comments) + +Milestone posts → Milestone anniversary notifications (requires scheduled job) +Health card → Milestone posts (health events are milestones; shares the date + category model) + +Pet profile (breed field) → Breed explore page +Pet profile (breed field) → Breed search filter + +Post (photo/video) → Likes, comments, paw-backs +Paw-backs → Attribution on original post + +Pet profile → Direct messages (between pet profiles) +Direct messages → Message notifications + +Block/report → Moderation queue (manual review for v1) +``` + +--- + +## MVP Recommendation + +**Prioritize (must ship in Phase 1-2):** +1. Owner auth + pet profile creation (with breed, species, bio, avatar) +2. Photo posts with captions (single image, multi-image carousel) +3. Pet-to-pet follow graph +4. Home feed (chronological, from followed pets) +5. Likes (paw reactions) + comments +6. Explore by breed / species +7. Hashtag search +8. Basic notifications (follows, likes batched, comments, DMs) +9. Block / report (safety baseline before any public launch) + +**Prioritize (Phase 2-3 — differentiators that drive retention):** +10. Short video posts +11. Stories (24 h) +12. Milestone posts + milestone anniversary notifications +13. Paw-backs (repost) +14. Direct messages (pet-to-pet) +15. Multi-pet account switching + +**Defer (Phase 3+ — high complexity, validate demand first):** +16. Health card / vet record tracking (high complexity, privacy surface) +17. Species-specific profile fields +18. Adoption origin story field (simple but low urgency) + +**Never build (anti-features above):** +- Algorithmic feed (v1) +- Human lifestyle posts without pet tag +- Long-form video +- Pet marketplace / sales +- Live video + +--- + +## Engagement Loops (Why Users Return) + +Understanding these is critical for prioritizing features: + +| Loop | Trigger | Feature Required | Retention Impact | +|------|---------|-----------------|-----------------| +| Daily check | "What did pets I follow post today?" | Home feed + chronological freshness | High — core daily habit | +| Reaction loop | Post gets a like/comment → owner notified → returns to engage | Notifications + reactions | High — validates posting effort | +| Discovery loop | Finds a new breed community → follows many pets → feed gets richer | Explore by breed + search | Medium — acquisition and onboarding | +| Milestone loop | "Fluffy's birthday is tomorrow" → posts, gets congratulated | Milestone posts + anniversary notifications | High — drives spikes of engagement on meaningful dates | +| DM loop | Coordinates a playdate via DM → builds relationship | Direct messages | Medium — retention through relationships | +| Health loop | Updates weight log → shares health card with vet | Health card | Low (v1) — powerful long-term retention signal | + +--- + +## Confidence Assessment + +| Area | Confidence | Notes | +|------|------------|-------| +| Table stakes | HIGH | Social platform conventions are well-established; Instagram/TikTok norms are stable. | +| Differentiators | MEDIUM | Pet-as-identity, health card, and milestone concepts are sound but unvalidated against real user demand. Validate with beta users before over-investing in health card. | +| Anti-features | MEDIUM | Recommendations based on known failure modes of social platforms and pet-specific legal/ethical pitfalls. Marketplace and breeding matchmaking risks are HIGH confidence. | +| Engagement loops | MEDIUM | Pattern-matched from general social platform research; pet-specific engagement data not available without live verification. | + +--- + +## Sources + +- Training knowledge of Instagram pet account patterns (confidence: HIGH — stable conventions) +- Training knowledge of TikTok pet creator patterns (confidence: HIGH — stable conventions) +- Training knowledge of BarkPost, Dogster, Petfinder community feature sets (confidence: MEDIUM — last verified in training data, may have evolved) +- General social platform anti-pattern literature (confidence: HIGH — well-documented) +- OnlyPets PROJECT.md requirements (confidence: HIGH — authoritative for this project) +- Note: Live competitor research (Pawsome, DoggoApp, current Petfinder) was NOT possible in this session due to tool restrictions. Recommend manual competitor audit before finalizing feature scope. diff --git a/.planning/research/PITFALLS.md b/.planning/research/PITFALLS.md new file mode 100644 index 0000000..b12735d --- /dev/null +++ b/.planning/research/PITFALLS.md @@ -0,0 +1,318 @@ +# Domain Pitfalls: OnlyPets — Social Media Platform + +**Domain:** Pet-focused social media platform (photos, video, stories, feed, DMs, health tracking) +**Researched:** 2026-06-05 +**Confidence:** MEDIUM-HIGH (training data on social platform engineering; external search unavailable) + +--- + +## Critical Pitfalls + +Mistakes that cause rewrites, catastrophic performance failures, or unrecoverable architectural debt. + +--- + +### Pitfall 1: Storing Media Files in the Application Database + +**What goes wrong:** Developers store photo/video blobs in PostgreSQL (or any relational DB) as `BYTEA` or `BLOB` columns instead of object storage. This works fine for a handful of test posts, then collapses under real load: DB memory explodes, backup times balloon, queries on adjacent tables slow catastrophically, and horizontal DB scaling becomes impossible. Migrating tens of thousands of existing media rows to S3 after launch is a painful forced-downtime operation. + +**Why it happens:** It's the path of least resistance in early prototyping — no S3 bucket to configure, no signed URLs to manage, just one database. + +**Consequences:** +- DB disk fills up unpredictably; `VACUUM` and `ANALYZE` run for hours +- Every DB backup is enormous, making point-in-time recovery impractical +- Cannot serve media via CDN — every image request hits your backend server +- Load on the DB server from media reads starves query connections for feed/DM logic + +**Prevention:** +- Design the media pipeline with S3-compatible object storage (AWS S3, Cloudflare R2, MinIO) from Phase 1, even if only a few images exist +- Store only `storage_key`, `mime_type`, `file_size`, `width`, `height` in the DB — never the binary +- Generate pre-signed upload URLs so clients upload directly to S3, bypassing the app server entirely +- Put a CDN (CloudFront, Cloudflare) in front of the bucket from day one; retrofitting CDN URLs into existing content is tedious + +**Detection (warning signs):** +- DB size grows faster than your user count suggests it should +- `pg_stat_user_tables` shows `n_tup_ins` on a `media` table with large average tuple sizes +- Backup jobs start taking longer than your deployment window + +**Phase:** Must be resolved in Phase 1 (media infrastructure). Never allow the first post to land in the DB. + +--- + +### Pitfall 2: Naive Fan-Out Feed Architecture That Can't Scale Past Early Adoption + +**What goes wrong:** The simplest feed implementation is a query: `SELECT posts FROM follows WHERE follower = $me ORDER BY created_at DESC`. This is fine for zero users, but it becomes a full-table scan against an N×M cross product at moderate scale. Worse, popular pet accounts (with thousands of followers) cause write amplification: when one pet posts, every follower's feed query gets slower because the post index grows. Projects that start with pure pull-based feeds often rewrite to fan-out-on-write (push feeds) at 50K–200K users — painful and risky mid-product. + +**Why it happens:** The pull model is trivially correct and easy to test in isolation. + +**Consequences:** +- Feed load time degrades as follow counts grow +- Viral pets (popular accounts with huge follower counts) cause thundering herd on read +- Adding algorithmic ranking (likes weighting, decay functions) is difficult to bolt onto a pure SQL query feed +- Engineering team spends weeks rebuilding infrastructure that should have been designed in from the start + +**Prevention:** +- Design the feed as a hybrid from Phase 1: fan-out-on-write for accounts under a follower threshold (~10K), pull-on-read for mega-accounts (celebrity dogs) +- Store per-pet feed as a sorted set in Redis (or equivalent) keyed by `pet_id`, with post IDs as members and timestamps as scores — a "feed inbox" pattern +- Build pagination via cursor (ISO timestamp or ULID/KSUID), never `OFFSET` — offset-based pagination degrades to full table scans at page 50+ +- Keep the feed hydration layer (turning post IDs into full post objects) separate from the feed ordering layer + +**Detection (warning signs):** +- Average feed query time grows linearly with follow count (not constant) +- DB slow query log shows full seq scans on `follows` or `posts` during peak traffic +- P99 feed latency spikes when any high-follower pet posts + +**Phase:** Feed architecture decisions must be locked in Phase 2 (feed + social graph). The hybrid threshold can be a config value, not hardcoded. + +--- + +### Pitfall 3: Video Processing Blocking the Request Cycle + +**What goes wrong:** When a user uploads a video, the naive implementation processes it synchronously in the HTTP handler: FFmpeg transcodes, thumbnails are extracted, and the response waits. Video processing takes 10–120 seconds per file. This blocks web server threads, kills upload reliability (network interruptions abort the entire pipeline), and gives users no feedback. One viral pet video upload from multiple simultaneous users can hang the entire server. + +**Why it happens:** Synchronous processing is the obvious first implementation. + +**Consequences:** +- HTTP timeouts during upload/process on slow connections +- Server thread starvation — all threads blocked on FFmpeg, new requests queue +- No retry mechanism — a transient FFmpeg crash loses the upload entirely +- Users see no progress indication; abandonment rate spikes + +**Prevention:** +- Treat video processing as a background job from day one: client uploads raw video directly to S3, an S3 event (or webhook) triggers a job queue (BullMQ, Temporal, AWS MediaConvert) +- The post record is created immediately in a `processing` state; the UI shows a spinner +- The background worker transcodes to HLS/MP4 (H.264 baseline), extracts a thumbnail, writes output back to S3, then updates the post record to `published` +- Implement dead-letter queues and at-least-once retry with idempotency keys +- For v1, AWS MediaConvert or Cloudflare Stream are better than self-hosted FFmpeg — they scale automatically and handle edge cases (malformed containers, codec variants) + +**Detection (warning signs):** +- Upload endpoint P99 latency tracks with video duration +- Thread/connection pool exhaustion metrics spike during upload load tests +- Server memory grows unbounded during concurrent uploads (FFmpeg loads full video into RAM) + +**Phase:** Phase 1 (media pipeline). Design the job queue before accepting any video upload. + +--- + +### Pitfall 4: The "Owner vs Pet" Identity Model Collapses Under Standard Auth Assumptions + +**What goes wrong:** OnlyPets has a two-layer identity: human owner accounts and pet profiles (multiple per owner). Most auth libraries and API designs assume one principal per session. Developers bolt on the second layer after the fact: JWTs contain `owner_id`, and `pet_id` is passed as a query param or header everywhere. This pattern infects every query, breaks multi-tenant authorization, creates session confusion (which pet is "active"?), and makes audit logging nearly impossible. Permissions bugs where owner A can post as pet belonging to owner B are common with ad-hoc second-layer identity. + +**Why it happens:** Auth is set up first with a "we'll handle the pet layer later" attitude. + +**Consequences:** +- Authorization bugs — pets can be posted to by unauthorized owners +- API surface is inconsistent: some routes use `pet_id` from JWT, others from request body +- Session management becomes complex (owner switches active pet) +- Social graph queries are ambiguous: does "my follows" mean owner-level or pet-level? + +**Prevention:** +- Decide on the identity model before writing a single route: recommendation is to encode `{owner_id, active_pet_id}` in the JWT payload (or session), with an explicit "switch active pet" endpoint +- Row-level security in PostgreSQL (or equivalent) should enforce that a pet's content is only writable by the pet's owner +- API routes should be namespaced by actor type: `/owner/...` for account management, `/pet/:petId/...` for all pet actions — and middleware should validate that `petId` belongs to the authenticated owner +- Write an ownership assertion helper used in every route that touches pet data — never rely on caller discipline alone + +**Detection (warning signs):** +- Any route that accepts `pet_id` from the request body without verifying ownership +- Integration tests that test pet actions without asserting cross-owner access is denied +- JWT claims that only contain `owner_id` with pet context passed differently per endpoint + +**Phase:** Phase 1 (auth/identity). This is an upfront architectural decision — retrofitting is very expensive. + +--- + +### Pitfall 5: Stories Architecture Treated as "Just Posts with TTL" + +**What goes wrong:** Stories (24h disappearing content) are often implemented as posts with a `expires_at` timestamp and a filter in feed queries. This breaks down: expired stories still appear in queries until a background job cleans them (GDPR/privacy implication), the feed query becomes more complex (now filtering two content types with different expiry logic), stories require view tracking (who viewed, in what order) which adds write-heavy workload to the posts table, and story sequences (multiple stories per pet per day) need ordering that post timestamps don't cleanly provide. + +**Why it happens:** "It's just a post that disappears" seems like a reasonable simplification. + +**Consequences:** +- Privacy risk — expired stories served after 24h due to job lag or caching +- View tracking (story rings, seen/unseen state) adds 1 write per viewer per story to the posts table, causing hot-row contention +- Story sequences (pet has 5 stories today) are hard to group without a separate data model +- CDN caching must be invalidated at exact expiry time — tricky with TTL-based caching + +**Prevention:** +- Treat stories as a first-class separate entity: `stories` table (not folded into `posts`), with its own service boundary +- Store story view counts in Redis with a TTL matching the story TTL; flush to permanent storage only for analytics purposes — do not write a row per view to PostgreSQL +- Story sequences: `stories` table has `story_session_id` (groups stories posted by the same pet in the same ~12h window) for correct ring ordering +- Hard-delete (not soft-delete) stories after expiry; cascade to CDN cache invalidation via CloudFront invalidation API or Cloudflare Cache API +- Index on `(pet_id, expires_at)` — the single most important index for the stories query path + +**Detection (warning signs):** +- `posts` table contains rows with `type = 'story'` alongside regular posts +- View tracking implemented as `story_views` rows in PostgreSQL without Redis buffering +- No hard-delete job scheduled — stories just filtered by `expires_at < NOW()` + +**Phase:** Phase 2 (content types). Decide the stories data model before implementing the stories upload flow. + +--- + +## Moderate Pitfalls + +--- + +### Pitfall 6: Direct Messages Modeled as Shared Mailbox (Not Conversation Threads) + +**What goes wrong:** DMs are implemented as a simple `messages` table with `sender_pet_id` and `recipient_pet_id`, queried by filtering both parties. This makes it impossible to paginate correctly, ordering breaks across timezones, read receipts require scanning all messages, and adding group messaging later requires a full rewrite. It also makes it impossible to correctly handle soft-delete ("delete for me only" vs "delete for everyone") without adding flags that corrupt the ordering query. + +**Prevention:** +- Model DMs as conversations first: `conversations` table (id, created_at), `conversation_participants` (conversation_id, pet_id), `messages` (id, conversation_id, sender_pet_id, body, created_at) +- Paginate messages using cursor (message `id` or `created_at`), never OFFSET +- Track read state per participant in `conversation_participants.last_read_message_id` — a single column, not a `message_reads` table +- Use WebSockets (or Server-Sent Events) for real-time delivery; fallback to polling for v1 is acceptable + +**Phase:** Phase 4+ (DMs). Lock the data model before any DM UI is built. + +--- + +### Pitfall 7: N+1 Query Problem on Feed Hydration + +**What goes wrong:** The feed returns a list of post IDs, and the hydration layer fetches each post, author pet, owner avatar, reaction count, and comment count with separate queries per post. 20 posts = 100+ DB round-trips. This is invisible in development (single user, fast localhost), catastrophic in production (high latency, DB connection exhaustion). + +**Prevention:** +- Hydrate feeds with batch queries: `WHERE id = ANY($1)` to fetch all posts in one query, then JOIN or secondary batch for pet profiles +- Use a DataLoader pattern (batching + deduplication within a request lifecycle) if using GraphQL or a similar resolver architecture +- Add `reaction_count` and `comment_count` as denormalized columns on the `posts` table, updated on each reaction/comment event — never compute counts with `SELECT COUNT(*)` inline in feed queries +- Write a feed hydration integration test that asserts query count is constant regardless of posts returned + +**Detection (warning signs):** +- DB query count in APM tools scales linearly with feed page size +- Development is fast, staging is slow (difference explained by data volume and no DataLoader) + +**Phase:** Phase 2 (feed). Establish the hydration pattern before the feed is shipped. + +--- + +### Pitfall 8: Missing Rate Limiting on Engagement Actions + +**What goes wrong:** Likes, paw-backs, and follows are unthrottled. Bots can inflate engagement metrics, spam follows can DOS the social graph, and automated "unfollow all" scripts can thrash the follows table. This also affects the health tracking feature — automated writes to health logs can corrupt a pet's history. + +**Prevention:** +- Apply rate limiting per owner account (not per IP — owners on mobile may share IPs) at the API gateway or middleware layer before any feature launches +- Limits for v1: 200 likes/hour, 50 follows/hour, 30 DMs/hour, 10 posts/day per pet (configurable) +- Use a sliding window rate limiter in Redis (`rate-limiter-flexible` library for Node.js, or equivalent) +- Flag suspicious burst patterns (100 follows in 60 seconds) for review, even if under the hard limit + +**Phase:** Phase 2 (social graph). Add rate limiting middleware before the follow/like endpoints go live. + +--- + +### Pitfall 9: Forgetting CDN Cache Invalidation Strategy + +**What goes wrong:** Pet profile photos and post images are aggressively cached at the CDN edge. When an owner updates their pet's profile photo or deletes a post, the old image continues to serve for up to 24h (or however long the CDN TTL is). For deletions, this is a privacy/content moderation issue — deleted content remains visible. + +**Prevention:** +- Use content-addressed storage keys: `media/{pet_id}/{sha256_hash}.jpg` — changing the image changes the URL, so old URLs naturally expire +- For profile photos specifically, include a version suffix or timestamp in the URL: `profile/{pet_id}/avatar?v={updated_at_epoch}` +- For deletions: trigger CDN cache invalidation (CloudFront `CreateInvalidation`, Cloudflare Cache API) as part of the delete transaction — treat it as a required side effect, not a best-effort cleanup +- Set CDN TTL for user-generated content at 1–7 days max; never "cache forever" for mutable resources + +**Phase:** Phase 1 (media pipeline). CDN strategy must be designed alongside storage strategy. + +--- + +### Pitfall 10: Health Tracking Data Without Audit History + +**What goes wrong:** Health records (weight logs, vaccines, vet visits) are mutable rows updated in-place. An owner accidentally deletes a vaccination record, or a bug overwrites a weight log — the data is gone with no recovery path. Shareable health cards become untrustworthy if the underlying data can silently change. + +**Prevention:** +- Use append-only logging for all health events: never `UPDATE` or `DELETE` a health record row — instead, insert a new event with `type = 'correction'` or `type = 'deletion_marker'` +- The "current" state is derived by replaying events (event sourcing lite) or by using the latest non-deleted row per metric +- Soft deletes (`deleted_at` timestamp) at minimum — hard deletes only on explicit GDPR erasure request +- Health card exports (shareable PDF or link) should be point-in-time snapshots with a generation timestamp + +**Phase:** Phase 3 (health tracking). Design the event model before the first health log endpoint is written. + +--- + +## Minor Pitfalls + +--- + +### Pitfall 11: Breed/Species Taxonomy Handled as Free-Text + +**What goes wrong:** Breed is stored as a free-text string entered by the owner. "Golden Retriever", "golden retriever", "Golden ret.", and "goldie" are now four different breeds in the database. Explore page filtering by breed becomes useless; breed-based analytics are untrustworthy. + +**Prevention:** +- Seed a canonical breed taxonomy from a structured source (AKC breeds list for dogs, recognized cat registries for cats) before launch +- Breed selection in UI is a searchable dropdown/autocomplete from the canonical list, with an "Other/Mixed" escape hatch +- Store `breed_id` (FK to breeds table), not raw text; display name is joined from the taxonomy +- Allow custom breed for "Mixed" cases via a separate `mixed_breed_notes` text field + +**Phase:** Phase 1 (pet profiles). The taxonomy must exist before the first pet profile is created. + +--- + +### Pitfall 12: Ignoring Image Orientation (EXIF) on Mobile Uploads + +**What goes wrong:** Photos taken on phones contain EXIF orientation metadata. If the backend strips EXIF without applying the rotation, or the frontend renders without CSS `image-orientation: from-image`, photos appear sideways or upside-down. This is extremely common and looks unprofessional. + +**Prevention:** +- During upload processing, use `sharp` (Node.js) or equivalent to auto-rotate images based on EXIF before storing to S3 — write the corrected pixel data, strip EXIF metadata +- This also prevents inadvertent PII leakage (EXIF can contain GPS coordinates) +- Test with photos from both iOS and Android before launch + +**Phase:** Phase 1 (media pipeline). One-time implementation with significant polish impact. + +--- + +### Pitfall 13: Pagination Inconsistency Across Endpoints + +**What goes wrong:** Feed uses cursor pagination, comments use offset pagination, DMs use a third scheme. Frontend developers have to implement three different pagination state machines; bugs appear at page boundaries when content is inserted between pages. Offset pagination returns duplicate or missing posts when new content is inserted mid-scroll. + +**Prevention:** +- Standardize on cursor-based pagination across ALL list endpoints from the start +- Cursor = base64-encoded composite of (`created_at`, `id`) to handle ties +- Response envelope: `{ data: [], next_cursor: "...", has_more: bool }` — same shape everywhere +- Document the pagination contract in an API spec (OpenAPI) before any endpoint is built + +**Phase:** Phase 1 (API design). This is a cross-cutting concern — set it before the first list endpoint is written. + +--- + +### Pitfall 14: Insufficient Moderation Infrastructure for UGC + +**What goes wrong:** User-generated content on a pet platform seems low-risk, but OnlyPets DMs and public posts create the same moderation surface as any social platform: harassment between owners, inappropriate content uploaded via the pet profile, fake health records, spam accounts. Launching without any moderation tooling means the first incident requires emergency development under pressure. + +**Prevention:** +- For v1: integrate a client-side CSAM hash-check (PhotoDNA or equivalent) on all media uploads — this is a legal requirement in many jurisdictions, not optional +- Add a basic "report post/profile" flow in v1 — even if reports go to a spreadsheet initially +- Implement soft-ban (disable account, content hidden) at the data model level before launch; hard-delete everything is not a moderation strategy +- Auto-scan media with a content moderation API (AWS Rekognition Moderation, Google Vision SafeSearch) before setting post status to `published` + +**Phase:** Phase 2 (content pipeline). At minimum, CSAM scanning must exist before the platform is publicly accessible. + +--- + +## Phase-Specific Warnings + +| Phase Topic | Likely Pitfall | Mitigation | +|-------------|---------------|------------| +| Phase 1: Auth & Identity | Owner/pet two-layer identity breaking standard auth | Encode `active_pet_id` in JWT; ownership middleware on every pet route | +| Phase 1: Media Infrastructure | Binary media in DB | S3-first design with pre-signed URLs; CDN from day one | +| Phase 1: Pet Profiles | Free-text breed field | Seed canonical taxonomy; FK to breeds table | +| Phase 1: API Design | Mixed pagination schemes | Cursor pagination standard across all list endpoints | +| Phase 2: Feed | Naive pull-only feed hitting DB on every request | Redis feed inbox with hybrid fan-out strategy | +| Phase 2: Feed Hydration | N+1 queries on post/pet/count data | Batch queries + denormalized counts | +| Phase 2: Social Graph | Unthrottled engagement actions | Rate limiting middleware before endpoints go live | +| Phase 2: Content | Stories treated as posts with TTL | Separate `stories` table, Redis view tracking, hard-delete job | +| Phase 2: CDN | Missing invalidation on delete/update | Content-addressed URLs + explicit CDN invalidation on delete | +| Phase 3: Video | Synchronous video processing in request thread | Background job queue (BullMQ/MediaConvert); post in `processing` state | +| Phase 3: Health Tracking | In-place mutation of health records | Append-only event log; soft deletes only | +| Phase 4: DMs | Flat message table without conversation model | Conversation + participant model from the start | +| Phase 4: DMs | Missing moderation on DMs | Report flow + soft-ban at data model level before DMs launch | + +--- + +## Sources + +- Training knowledge on social media platform engineering patterns (Instagram, Twitter/X, TikTok architecture public writing) +- PostgreSQL documentation on performance anti-patterns (seq scans, OFFSET pagination) +- Redis sorted set feed inbox pattern (common in distributed systems literature) +- Node.js sharp library EXIF handling documentation +- AWS MediaConvert / Cloudflare Stream service documentation +- General social platform post-mortems and engineering blog posts (Slack, Discord, Instagram engineering blogs) + +**Confidence:** MEDIUM-HIGH on all pitfalls — these are well-documented, recurrent patterns in social platform engineering. LOW confidence on specific library version recommendations (not verified against current releases due to search restrictions). diff --git a/.planning/research/STACK.md b/.planning/research/STACK.md new file mode 100644 index 0000000..754eb4c --- /dev/null +++ b/.planning/research/STACK.md @@ -0,0 +1,169 @@ +# Technology Stack + +**Project:** OnlyPets — Pet Social Media Platform +**Researched:** 2026-06-05 +**Confidence note:** WebSearch and WebFetch tools were unavailable during this research session. Findings are drawn from training knowledge (cutoff August 2025) supplemented by one confirmed official source (Next.js 15 release blog, fetched directly). Confidence levels reflect this constraint. + +--- + +## Recommended Stack + +### Core Framework + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Next.js | 15.x (stable) | Full-stack React framework | App Router gives co-located server components + API routes in one repo. SSR for SEO on pet/explore pages. Turbopack dev server is now stable. React 19 support. Confirmed stable per official release blog. | +| React | 19.x | UI rendering | Ships with Next.js 15. React Server Components reduce JS bundle for feed pages. Concurrent features matter for infinite scroll. | +| TypeScript | 5.x | Type safety | Next.js 15 ships `next.config.ts` with first-class TS support. Non-negotiable for a team codebase; catches data-shape bugs early (pet profiles, post types). | + +### Styling + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Tailwind CSS | 4.x | Utility-first styling | Fastest path to a polished, consistent UI. No CSS-in-JS runtime cost. v4 (released early 2025) uses a new CSS-native engine — faster builds, no config file needed. Standard choice for Next.js apps in 2025. | +| shadcn/ui | Latest | Component primitives | Radix UI primitives + Tailwind. Copy-into-repo model means no dependency lock-in. Accessible by default — critical for input forms (onboarding, post upload, DM composer). Not a library to install — a component source strategy. | + +### Backend / API + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Next.js Route Handlers | 15.x | API layer | For most endpoints (CRUD on pets, posts, follows, reactions) Next.js Route Handlers eliminate a separate API server entirely. Server Actions for form mutations (post creation, profile edits) keep the round-trip minimal. | +| tRPC | 11.x | Type-safe API client-server bridge | Where you need client-side data fetching with full type safety (feed, DM polling, notifications), tRPC with React Query eliminates manual API typing. Pairs well with Next.js App Router via the `@trpc/server/adapters/fetch` adapter. Use for internal browser-to-server calls; use plain Route Handlers for webhooks and external service callbacks. | +| Zod | 3.x | Schema validation | Input validation on all API boundaries. tRPC uses Zod natively. Also use for validating Mux/Cloudinary webhook payloads. | + +### Database + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| PostgreSQL | 16.x | Primary data store | The correct choice for a social graph. Supports relational integrity (pet→owner, post→pet, follow→pet), JSONB for flexible milestone metadata, full-text search for hashtag/breed discovery, and row-level security (RLS) when using Supabase. | +| Supabase | Latest (hosted) | Managed Postgres + realtime + auth option | Supabase provides managed Postgres 15/16, built-in Realtime (websocket pub/sub on DB changes), Storage (S3-compatible for photos), and optional Auth. Dramatically reduces infrastructure work for a v1. The free tier covers early-stage traffic. **Critical:** Supabase Realtime handles feed updates and DM delivery — replaces the need for a separate WebSocket server. | +| Prisma | 5.x | ORM / schema management | Type-safe DB client generated from schema. Migrations are declarative. Integrates with Supabase Postgres directly. Prisma Accelerate (edge-compatible connection pooling) solves the serverless connection limit problem that hits Postgres hard on Vercel. | +| Redis (Upstash) | Serverless | Cache + rate limiting + ephemeral state | Stories TTL management (24h expiry), feed caching for hot users, rate limiting on post upload and DM endpoints. Upstash is serverless Redis — pay-per-request, no always-on instance. Critical: do NOT use Supabase as a cache layer; Redis is the right tool for ephemeral/fast-expiry data. | + +### Media Storage & Delivery + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Cloudflare R2 | — | Photo storage | S3-compatible object storage with zero egress fees. Photos are the dominant media type by volume. Egress costs on S3 accumulate fast on a social platform; R2 eliminates this. Pair with Cloudflare CDN for global image delivery. | +| Cloudflare Images | — | Image optimization & CDN | Resize, crop, and format-convert (WebP/AVIF) on the fly via URL parameters. Replaces the need for a custom image pipeline. Cost: ~$5/100k images served. Alternative is Next.js Image Optimization pointed at R2, but Cloudflare Images is more capable and decoupled from your Next.js server. | +| Mux | — | Video upload, transcoding, adaptive streaming | Short-form video (pet posts, stories) requires HLS adaptive streaming — raw MP4 upload is not acceptable for mobile networks. Mux handles: upload via signed URLs → auto-transcoding to multiple bitrates → HLS delivery → thumbnail extraction → webhooks on completion. The `mux-player-react` component embeds in 10 lines. Pricing is per-minute stored + per-minute delivered — fair for short-form video. **Do not use Cloudinary for video at scale**: Cloudinary's video pricing is expensive and less optimized for streaming than Mux. | + +**Note on Supabase Storage vs. R2 + Mux split:** Supabase Storage (S3-compatible) is tempting to use for everything. Avoid it for video — Supabase Storage does not transcode or serve HLS. Use it only if you need RLS-protected file access (e.g., private health records). Photos should go to R2+Cloudflare Images; video to Mux. + +### Real-Time Features + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Supabase Realtime | — | Feed updates, DM delivery, notification toasts | Supabase Realtime uses PostgreSQL logical replication to broadcast DB changes over websockets. Subscribe to `INSERT` on `messages` table → DMs appear instantly. Subscribe to `INSERT` on `posts` from followed pets → live feed updates. No separate Socket.io or Ably subscription needed. Included in Supabase plan. | +| TanStack Query (React Query) | 5.x | Client-side data fetching, caching, optimistic updates | Pairs with tRPC. Handles feed pagination (cursor-based infinite scroll), optimistic like/reaction updates, and background refetch. Do NOT use SWR — React Query v5 has superior cache invalidation and mutation handling for a social app's complexity. | + +### Authentication + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Clerk | Latest | Authentication provider | Clerk is the standard choice for Next.js 15 App Router auth in 2025. Ships with: pre-built UI components (sign-in, sign-up, user profile), social OAuth (Google, Apple, Facebook — important for pet-owner onboarding friction), session management with edge-compatible JWT verification, webhooks for user lifecycle events. The `@clerk/nextjs` middleware integration is first-class. **Note:** OnlyPets has a two-layer identity (owner account + pet profile). Clerk manages the owner account; your DB manages the pet-as-identity layer. Do NOT try to put pets as Clerk users — they are not auth entities, they are app-domain entities. | + +**Alternative considered:** NextAuth.js (Auth.js v5). Rejected because: more configuration overhead, no pre-built UI components, Clerk's DX is substantially better for a greenfield social app that needs social OAuth working on day one. + +### Infrastructure & Deployment + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| Vercel | — | Hosting | Next.js is optimized for Vercel — zero-config deployment, edge middleware, ISR, image optimization. For a greenfield social app the operational overhead savings outweigh cost at v1 scale. Preview deployments per PR accelerate iteration. | +| Upstash | — | Serverless Redis | Rate limiting (post uploads, DM sends), story TTL tracking, hot-user feed cache. Serverless pricing eliminates idle cost. | + +--- + +## Alternatives Considered + +| Category | Recommended | Alternative | Why Not | +|----------|-------------|-------------|---------| +| Frontend framework | Next.js 15 | Remix v2 | Remix has excellent data loading patterns but smaller ecosystem, less Vercel integration, and fewer community examples for social apps. Next.js App Router now covers Remix's main advantages (nested layouts, data co-location). | +| Frontend framework | Next.js 15 | SvelteKit | Smaller ecosystem, fewer hire-able devs, React component ecosystem (shadcn, Mux player) is unavailable. | +| Database | PostgreSQL (Supabase) | MongoDB | No relational joins — social graph queries become application-level N+1 problems. MongoDB's flexible schema is a liability when you need FK constraints on pet→post→reaction. | +| Database | PostgreSQL (Supabase) | PlanetScale (MySQL) | PlanetScale removed its free tier in 2024; Vitess's lack of FK enforcement is a footgun for a social graph. Supabase offers more value (Realtime, Auth, Storage) in one platform. | +| ORM | Prisma | Drizzle ORM | Drizzle is faster and more lightweight, but Prisma's migration tooling and ecosystem maturity are better for a team starting greenfield. Revisit Drizzle in v2 if Prisma performance becomes an issue. | +| Video | Mux | Cloudinary Video | Cloudinary video pricing is significantly higher per GB and less optimized for HLS adaptive streaming. Mux is purpose-built for streaming video. | +| Video | Mux | AWS MediaConvert + S3 | More control but enormous operational complexity for a v1. Requires Lambda triggers, MediaConvert job polling, CloudFront CDN setup. Mux abstracts all of this. | +| Photo storage | Cloudflare R2 + Images | AWS S3 + CloudFront | Equivalent capability but S3 egress costs accumulate. R2's zero-egress pricing is a direct advantage for a photo-heavy social platform. | +| Photo storage | Cloudflare R2 + Images | Supabase Storage | Supabase Storage lacks image transformation. Use Supabase Storage only for private files needing RLS (health records). | +| Auth | Clerk | NextAuth (Auth.js v5) | More setup, no pre-built UI, requires more custom code for social OAuth flows. Clerk's Next.js 15 App Router integration is tighter and more production-ready. | +| Real-time | Supabase Realtime | Ably / Pusher | Additional paid service when Supabase Realtime (included) covers the use cases. Supabase Realtime's DB-change subscription model is particularly clean for a feed driven by Postgres inserts. | +| Real-time | Supabase Realtime | Socket.io (self-hosted) | Requires a persistent server — incompatible with Vercel serverless. Supabase Realtime is managed and serverless-friendly. | +| Client fetching | TanStack Query v5 | SWR | React Query v5 has richer mutation/optimistic update APIs. For a social app with likes, comments, and reposts all needing optimistic UI, React Query's `useMutation` is more capable than SWR's `useSWRMutation`. | +| Styling | Tailwind CSS v4 | CSS Modules | CSS Modules don't scale as well for a design system approach; Tailwind + shadcn provides a cohesive component language out of the box. | +| Styling | Tailwind CSS v4 | styled-components / Emotion | Runtime CSS-in-JS has a performance penalty in RSC environments and adds complexity. Tailwind generates static CSS — no runtime overhead. | + +--- + +## Installation + +```bash +# Bootstrap +npx create-next-app@latest onlypets --typescript --tailwind --app --eslint + +# Core dependencies +npm install @trpc/server @trpc/client @trpc/next @trpc/react-query +npm install @tanstack/react-query +npm install @clerk/nextjs +npm install @prisma/client prisma +npm install zod +npm install @supabase/supabase-js +npm install @mux/mux-player-react @mux/mux-node +npm install @upstash/redis @upstash/ratelimit + +# Dev dependencies +npm install -D prisma +npm install -D @types/node + +# shadcn/ui (interactive CLI, installs component source into your repo) +npx shadcn@latest init +``` + +**Tailwind v4 note:** `create-next-app` may scaffold Tailwind v3. Upgrade manually: +```bash +npm install tailwindcss@^4.0 @tailwindcss/vite +``` +Check Tailwind v4 migration guide — the `tailwind.config.js` file is replaced by CSS-native `@theme` configuration. + +--- + +## Key Version Pinning Notes + +| Package | Version to pin | Reason | +|---------|---------------|--------| +| `next` | `^15.0.0` | Stable, confirmed. Avoid `canary`. | +| `react` / `react-dom` | `^19.0.0` | Required for Next.js 15 App Router. | +| `@prisma/client` | `^5.0.0` | v5 has improved query performance and better TS types. | +| `@trpc/server` | `^11.0.0` | v11 is the App Router-native version; v10 had compatibility issues. | +| `@tanstack/react-query` | `^5.0.0` | v5 has breaking API changes from v4; pin major. | +| `tailwindcss` | `^4.0.0` | v4 engine is not backward compatible with v3 config format. | + +--- + +## Confidence Assessment + +| Area | Confidence | Notes | +|------|------------|-------| +| Next.js 15 as framework | HIGH | Confirmed via official release blog (fetched). Stable since Oct 2024. | +| React 19 | HIGH | Ships with Next.js 15, confirmed in release blog. | +| TypeScript 5.x | HIGH | Industry standard; no uncertainty. | +| Tailwind CSS v4 | MEDIUM | Released early 2025 per training data; could not verify current minor version with live docs. | +| shadcn/ui | MEDIUM | Well-established pattern as of August 2025 training cutoff. | +| tRPC v11 | MEDIUM | v11 targeting App Router compatibility per training data; verify current release on npmjs.com. | +| Prisma v5 | MEDIUM | Stable per training data; Prisma Accelerate for serverless connection pooling verified as an existing product. | +| Supabase (Postgres + Realtime) | MEDIUM | Core product is well-established; Realtime feature set stable per training data. Cannot confirm latest feature additions. | +| PostgreSQL 16 | HIGH | Mature, stable; Supabase provides it managed. | +| Cloudflare R2 + Images | MEDIUM | R2 launched 2022, zero-egress pricing confirmed stable; Images product behavior from training data. | +| Mux for video | MEDIUM | Mux is the established standard for developer-facing video streaming; `mux-player-react` is the official component. Pricing model from training data — verify before committing. | +| Clerk | MEDIUM | Established Next.js auth provider as of training cutoff; Next.js 15 App Router compatibility from training data. Verify `@clerk/nextjs` supports Next.js 15 before starting. | +| Upstash Redis | MEDIUM | Serverless Redis product is well-established; pricing from training data. | +| Vercel | HIGH | Official Next.js hosting platform, no uncertainty. | + +--- + +## Sources + +- Next.js 15 official release blog: https://nextjs.org/blog/next-15 (fetched 2026-06-05) — HIGH confidence +- Remaining technology assessments based on training data (cutoff August 2025) — MEDIUM confidence +- Recommend verifying: Clerk `@clerk/nextjs` Next.js 15 compatibility, tRPC v11 stability, Tailwind v4 migration guide, Mux current pricing diff --git a/.planning/research/SUMMARY.md b/.planning/research/SUMMARY.md new file mode 100644 index 0000000..9f8eaf0 --- /dev/null +++ b/.planning/research/SUMMARY.md @@ -0,0 +1,222 @@ +# Project Research Summary + +**Project:** OnlyPets -- Pet Social Media Platform +**Domain:** Pet-first social media (photos, video, stories, health tracking, DMs) +**Researched:** 2026-06-05 +**Confidence:** MEDIUM + +## Executive Summary + +OnlyPets is a pet-first social media platform where pets are the primary social identities. This is not a cosmetic distinction; it is a foundational architectural constraint that shapes every data model, every API route, and every UI interaction. Experts build social platforms like this as modular monoliths with clear domain boundaries (Auth, Pet Profile, Content, Feed, Engagement, Messaging, Health), using PostgreSQL as the primary store for relational social graph data, Redis for fast feed delivery and ephemeral state, S3-compatible object storage for all media, and a CDN layer from day one. The technology choices here (Next.js 15, Supabase, Prisma, Clerk, Mux, Cloudflare R2) are well-matched to this architecture and allow a small team to ship a complete v1 without managing separate infrastructure services. + +The recommended approach is to build in strict dependency order: identity foundation first, then content pipeline, then feed and social graph, then engagement mechanics, then differentiated features (stories, video, milestones, DMs, health tracking). The pet-as-actor identity model must be established in Phase 1 -- it cannot be retrofitted. Media storage via presigned S3 uploads and a CDN must also be in place before the first real content is accepted, as migrating binary data out of a relational database post-launch is painful and expensive. The feed should be built with Redis fan-out from the start, not as a naive SQL join query that gets replaced under load. + +The key risks are architectural: collapsing the owner/pet two-layer identity into a single principal, treating media uploads as synchronous API operations, building a pull-only SQL feed that cannot scale, and letting stories bleed into the posts table. Each of these is a known pitfall that causes rewrites at moderate scale. Mitigation is straightforward when designed in from Phase 1 but expensive to retrofit. The differentiated features (health card, milestone anniversary notifications, pet-native reposts) are well-grounded in user need but should be validated with real users before heavy investment, particularly the health card which carries meaningful privacy complexity. + +## Key Findings + +### Recommended Stack + +The stack is centered on Next.js 15 (App Router) with React 19, TypeScript 5, and Tailwind CSS v4. Authentication is handled by Clerk (owner accounts only; pet profiles are app-domain entities, not auth entities). The database is PostgreSQL 16 via Supabase, which also provides Realtime (WebSocket-based DB change subscriptions for feed and DMs) and managed infrastructure. Prisma 5 is the ORM, with Prisma Accelerate for serverless connection pooling on Vercel. tRPC v11 with TanStack Query v5 covers type-safe client-server communication. Redis via Upstash handles feed caching, story TTL, and rate limiting. Media splits by type: Cloudflare R2 + Cloudflare Images for photos (zero egress cost, URL-based transforms), Mux for video (auto-transcoding to HLS, signed upload URLs, thumbnail extraction). Supabase Realtime replaces the need for a separate WebSocket server for feed updates and DMs. + +**Core technologies:** +- Next.js 15 + React 19: Full-stack framework -- App Router enables co-located server components and API routes with SSR for SEO on explore/breed pages +- Clerk: Owner authentication -- pre-built UI, social OAuth, Next.js 15 App Router integration; pet profiles live in the app DB, not Clerk +- PostgreSQL (Supabase): Primary datastore -- relational integrity for social graph, JSONB for milestone metadata, full-text search for hashtags +- Supabase Realtime: WebSocket feed/DM delivery -- built into Supabase plan, replaces separate socket infrastructure +- Prisma 5 + Prisma Accelerate: Type-safe ORM + serverless connection pooling -- required on Vercel serverless +- Redis (Upstash): Feed sorted sets, story TTL, rate limiting -- serverless pricing, no idle cost +- Cloudflare R2 + Images: Photo storage and delivery -- zero egress fees, URL-based image transforms +- Mux: Video upload, transcoding, HLS delivery -- purpose-built for streaming; Cloudinary video is more expensive and less capable +- TanStack Query v5 + tRPC v11: Client data fetching -- cursor-based infinite scroll, optimistic like/reaction updates + +### Expected Features + +The research distinguishes clearly between table stakes (expected by any social platform user), differentiators (the OnlyPets thesis), and anti-features (what must be explicitly excluded). + +**Must have (table stakes):** +- Pet profile with photo, bio, breed, species, avatar -- core identity +- Photo posts with captions (single and multi-image carousel) +- Short video posts (15-60 seconds, HLS delivery) +- Stories (24h disappearing, with story rings and viewer list) +- Home feed from followed pets (chronological for v1) +- Likes/reactions (paw-print) and comments on posts +- Pet-to-pet follow/unfollow +- Explore/discover page with breed and species filtering +- Hashtag search and discovery +- Notifications (follows, likes batched, comments, DMs) +- Block/report (safety baseline before any public launch) +- Account settings, pet switching, profile editing + +**Should have (differentiators):** +- Pets as first-class identities -- profile URL is the pet name, not the owner name +- Milestone posts (structured format: first walk, adoption anniversary, birthday) with anniversary re-engagement notifications +- Paw-backs (pet-native reposts with attribution) +- Pet-to-pet direct messages (playdate coordination, breed community) +- Breed/species structured taxonomy browse (not free-text hashtag search) +- Adoption origin story structured field on profile +- Multi-pet household management with frictionless switching + +**Defer (v2+):** +- Health card / shareable vet record -- high complexity, privacy surface, validate demand first +- Species-specific extended profile fields (vocalization logs for birds, walk tracking for dogs) +- Milestone-triggered anniversary notifications (simple after milestones exist, but lower urgency than core features) + +**Never build (anti-features):** +- Algorithmic feed in v1 -- ship chronological; revisit after 10K+ active pets +- Human lifestyle posts without a pet tagged -- enforced at the post creation flow +- Long-form video (>60 seconds) +- Pet marketplace or breeding matchmaking +- Live video +- Public owner profiles -- owner accounts are administrative only + +### Architecture Approach + +OnlyPets should be built as a modular monolith following a layered-monolith-to-service-extract path. All domain logic lives in clearly bounded modules within a single deployable Next.js application; extraction to separate services happens only at proven bottlenecks (likely Feed or Media at scale). The critical architectural invariant is the pet-as-actor pattern: every mutation is performed by a pet_id, not an owner_id. Auth supplies the owner_id; middleware resolves the active pet_id and validates ownership before any pet-context route executes. The feed uses a Redis fan-out-on-write (push) model with cursor-based pagination. All media goes through presigned S3 upload (client uploads directly to R2, never through the API server). Video processing is asynchronous via a job queue; posts are created in a processing state and transition to published when transcoding completes. + +**Major components:** +1. Auth / Owner -- Clerk-managed sign-up/login; owner account is an administrative actor, not a social one +2. Pet Profile -- pet identity (name, breed, species, bio, avatar); follows table uses (follower_pet_id, followee_pet_id) -- no owner IDs in the social graph +3. Media Storage -- presigned R2 upload pipeline; CDN delivery; Mux for async video transcoding +4. Content -- photo posts, milestone posts, stories (stories is a separate table, not posts with TTL) +5. Feed -- Redis sorted sets per pet, fan-out on write; cursor pagination; hydration via batch queries + denormalized counts +6. Engagement -- reactions, comments, paw-backs; Redis atomic counters flushed to PostgreSQL +7. Explore / Discovery -- breed taxonomy browse, hashtag search, PostgreSQL full-text (tsvector) for v1 +8. Messaging -- conversation + participant + messages model (not flat sender/recipient table); WebSocket via Supabase Realtime +9. Health Tracking -- append-only event log; shareable token-based health card URL +10. Notifications -- event-driven; consumes events from all components; in-app + web push + +### Critical Pitfalls + +1. **Owner/pet two-layer identity collapsing** -- Encode {owner_id, active_pet_id} in the JWT from day one. Every pet-context route validates ownership via middleware. Never pass pet_id from request body without ownership assertion. Must be designed in Phase 1; retrofitting is expensive. + +2. **Media binary data in PostgreSQL** -- Store only storage_key, mime_type, file_size, width, height in the DB. All binary goes to Cloudflare R2 via presigned upload from the client. CDN in front of R2 from the first byte. Never route media through the API server. + +3. **Naive pull-only SQL feed** -- Build a Redis fan-out feed inbox (sorted set per pet keyed by timestamp) from Phase 2. Never ship a raw SQL join on follows as the production feed query. Cursor pagination everywhere -- no OFFSET. + +4. **Video processing in the request cycle** -- Client uploads raw video to Mux via signed URL. Post is created in processing state. Background webhook from Mux updates post to published when HLS transcoding is complete. Never run FFmpeg in an HTTP handler. + +5. **Stories treated as posts with TTL** -- stories is a separate table with story_session_id, expires_at, and an index on (pet_id, expires_at). View tracking goes to Redis with matching TTL (not PostgreSQL rows per view). Hard-delete after expiry with CDN cache invalidation. Must be decided in Phase 2 before any stories UI is built. + +## Implications for Roadmap + +Based on research, the build order is driven by strict data dependencies: you cannot have content without pet identity, cannot have a feed without content, cannot have engagement without a feed. The architecture research provides an explicit 7-phase suggested grouping. + +### Phase 1: Foundation -- Identity, Profiles, Media Pipeline + +**Rationale:** Everything in the system depends on owner auth and pet identity. Media infrastructure must exist before the first real post is accepted. Breed taxonomy must be seeded before pet profile creation. These are not independently shippable features; they are pre-conditions for all subsequent work. +**Delivers:** Owner sign-up/login (Clerk), pet profile creation, breed/species taxonomy, presigned R2 upload pipeline with CDN, EXIF auto-rotation on image upload, cursor pagination standard established across all future endpoints, pet-as-actor middleware pattern. +**Addresses:** Pet profile (table stakes), breed/species taxonomy (differentiator), adoption origin story field (low effort, include here) +**Avoids:** Owner/pet identity collapse, binary media in DB, free-text breed field, mixed pagination schemes, EXIF orientation issues +**Research flag:** Standard patterns -- no phase research needed. Verify Clerk Next.js 15 App Router compatibility before starting. + +### Phase 2: Content Core -- Photo Posts, Milestones, Home Feed, Social Graph + +**Rationale:** The core engagement loop (post to feed to follow to richer feed) must be complete before any public-facing feature makes sense. Stories depend on the same content pipeline but have additional complexity (separate table, Redis view tracking, expiry job) -- they belong here rather than later to avoid retrofitting the content model. +**Delivers:** Photo posts (single + carousel), milestone posts, stories (separate stories table, Redis view tracking, 24h expiry job, CDN invalidation on expiry), pet-to-pet follow/unfollow, home feed (Redis fan-out, cursor pagination, batch hydration with denormalized counts), basic notifications (follows, likes, comments), block/report safety baseline, rate limiting on engagement actions. +**Uses:** Supabase Realtime for live feed updates, TanStack Query for infinite scroll, Upstash Redis for feed sorted sets and rate limiting +**Implements:** Content, Feed, Engagement components +**Avoids:** Pull-only SQL feed, N+1 hydration, stories-as-posts anti-pattern, unthrottled engagement, CDN invalidation gaps, missing moderation baseline +**Research flag:** Feed fan-out architecture is well-documented. Stories expiry + CDN invalidation may need targeted research during planning. + +### Phase 3: Discovery and Engagement Depth -- Explore, Search, Reactions, Paw-backs + +**Rationale:** Discovery drives organic acquisition -- users must be able to find breed communities without word-of-mouth. Paw-backs (reposts) extend the social graph value and are architecturally simple after engagement is in place. +**Delivers:** Explore page (breed/species browse from taxonomy), hashtag search (PostgreSQL tsvector), paw-backs with attribution, reaction depth, notification centre (consolidated view) +**Implements:** Explore / Discovery component, Engagement component depth +**Research flag:** PostgreSQL full-text search (tsvector) is standard. No phase research needed unless Typesense integration is considered. + +### Phase 4: Video Posts + +**Rationale:** Short-form video is table stakes but requires the most complex infrastructure (Mux async transcoding pipeline, job queue, processing state). Defer until content and feed are stable to avoid debugging two complex systems simultaneously. +**Delivers:** Short video posts (15-60s, Mux upload to HLS delivery to webhook-driven published state), video in stories, mux-player-react embed, thumbnail extraction +**Uses:** Mux signed upload URLs, webhook for async job tracking, post processing state +**Avoids:** Synchronous video processing +**Research flag:** Mux webhook integration and job queue choice (BullMQ vs. Temporal vs. Mux-native) may benefit from targeted phase research. + +### Phase 5: Direct Messaging + +**Rationale:** DMs are a distinct subsystem (conversation model, real-time delivery, read receipts) that requires pet identity and notification systems to be solid first. Structuring this as a separate phase prevents the messaging model from being bolted onto the posts table. +**Delivers:** Pet-to-pet DMs (conversation + participants + messages model), read receipts via last_read_message_id on participants, real-time delivery via Supabase Realtime WebSocket, DM notifications, report flow for DMs +**Implements:** Messaging component +**Avoids:** Flat sender/recipient message table, missing DM moderation +**Research flag:** Verify Supabase Realtime private channel subscription model for 1:1 conversations before building. + +### Phase 6: Health Tracking and Milestone Depth + +**Rationale:** Health tracking is the highest-complexity differentiator (privacy surface, append-only event model, shareable token URLs) and should be built after the platform core social loop is stable. +**Delivers:** Weight logs, vet visit records, vaccine history (append-only event log, no in-place mutation), shareable health card (token-based signed URL, point-in-time snapshot), milestone anniversary push notifications (scheduled job), species-specific extended profile fields (optional) +**Avoids:** In-place mutation of health records +**Research flag:** Append-only event sourcing pattern in Prisma + GDPR erasure handling may benefit from phase research. Scheduled notification job infrastructure needs a clear implementation decision. + +### Phase 7: Polish, Scale Preparation, Notification Centre + +**Rationale:** Final hardening before wider launch -- explore tuning, notification preferences, breed browse improvements, Typesense evaluation if PostgreSQL full-text is showing strain, performance profiling. +**Delivers:** Notification preferences (opt-out by type), Explore page tuning, breed browse improvements, Typesense spike (if needed), account switcher polish, web push notifications +**Research flag:** Typesense integration (if pursued) needs research. Otherwise standard patterns. + +### Phase Ordering Rationale + +- Phase 1 before everything: Pet identity is the social actor for all subsequent features; media pipeline is a pre-condition for content; breed taxonomy is a pre-condition for pet profiles. +- Phase 2 before Phase 3: The feed and follow graph must exist before Explore has anything to surface. Stories belong in Phase 2 to avoid retrofitting the content model later. +- Phase 4 (video) after Phase 2 (content): Avoids debugging two complex systems simultaneously. Video adds Mux async pipeline on top of an already working post flow. +- Phase 5 (DMs) after Phase 2 (notifications baseline): DM notifications rely on the notification component from Phase 2. DMs are independent of Feed/Explore and can be built in parallel with Phases 3-4 if capacity allows. +- Phase 6 (health) late: Highest privacy complexity; benefits from a stable platform before adding health data handling. +- Phase 7 last: Polish and scale prep after features are complete. + +### Research Flags + +Phases needing deeper research during planning: +- **Phase 4 (Video):** Mux webhook integration, job queue choice (BullMQ vs. Temporal vs. Mux-native), post state machine for processing to published transition +- **Phase 5 (DMs):** Supabase Realtime private channel subscription model for 1:1 conversations; verify current Realtime capabilities before designing the DM real-time layer +- **Phase 6 (Health):** Append-only event sourcing pattern in Prisma; GDPR erasure handling for health records; scheduled job infrastructure for anniversary notifications + +Phases with standard patterns (skip research-phase): +- **Phase 1 (Foundation):** Clerk + Next.js 15 App Router is well-documented; presigned S3 upload pattern is standard +- **Phase 2 (Content Core):** Redis sorted set feed inbox is well-documented; TanStack Query infinite scroll is standard +- **Phase 3 (Discovery):** PostgreSQL tsvector search is standard; paw-backs are architecturally simple +- **Phase 7 (Polish):** Standard patterns unless Typesense is pursued + +## Confidence Assessment + +| Area | Confidence | Notes | +|------|------------|-------| +| Stack | MEDIUM | Next.js 15 confirmed via official source. All other technology choices are training-data assessments (cutoff August 2025). Verify Clerk Next.js 15 compatibility, tRPC v11 stability, Tailwind v4 migration guide, and Mux pricing before starting. | +| Features | MEDIUM | Table stakes are HIGH confidence (stable social platform conventions). Differentiators (health card, milestones) are sound but unvalidated against real user demand. Recommend beta user validation before heavy health card investment. Anti-features are HIGH confidence based on documented failure modes. | +| Architecture | HIGH | Core patterns (fan-out feed, presigned upload, pet-as-actor, cursor pagination, denormalized counters, modular monolith) are well-established and drawn from documented Instagram/Twitter/TikTok architectural patterns. | +| Pitfalls | MEDIUM-HIGH | All pitfalls are well-documented recurring patterns in social platform engineering. Prevention strategies are established. Specific library version recommendations not verified against current releases. | + +**Overall confidence:** MEDIUM -- architecture patterns are solid, stack choices are reasonable for 2025/2026, feature scope is well-reasoned, but live competitor verification and dependency version confirmation are outstanding before finalizing scope. + +### Gaps to Address + +- **Clerk + Next.js 15 App Router compatibility:** Verify @clerk/nextjs supports Next.js 15 App Router before starting Phase 1. This is the auth foundation for everything. +- **tRPC v11 current release status:** Confirm v11 is stable on npm before adopting. Fall back to tRPC v10 with App Router adapter if v11 is still in beta. +- **Tailwind v4 migration path:** create-next-app may scaffold Tailwind v3. Confirm v4 upgrade path and @theme CSS-native configuration before starting Phase 1. +- **Mux current pricing:** Verify per-minute-stored + per-minute-delivered pricing model before committing to Mux for Phase 4. +- **Competitor feature audit:** Live competitor research (Pawsome, DoggoApp, current Petfinder social features) was not possible during research. Recommend a manual competitor audit before finalizing Phase 2-3 feature scope. +- **CSAM scanning legal requirement:** Legal requirement in many jurisdictions before public launch. Confirm legal obligations for target market before Phase 2 content pipeline completion. +- **Supabase Realtime private channel model:** Confirm Supabase Realtime supports private 1:1 conversation channels before committing to it for DM real-time delivery in Phase 5. + +## Sources + +### Primary (HIGH confidence) +- Next.js 15 official release blog: https://nextjs.org/blog/next-15 (fetched 2026-06-05) -- framework version confirmation, React 19, Turbopack stable +- OnlyPets PROJECT.md -- authoritative product requirements + +### Secondary (MEDIUM confidence) +- Instagram Engineering Blog (public) -- fan-out feed design, media presigned upload patterns +- Twitter/X architecture papers (public) -- push/pull hybrid feed for celebrity accounts +- Designing Data-Intensive Applications (Kleppmann) -- cursor pagination, feed design +- System Design Interview (Alex Xu) -- feed architecture, presigned uploads +- Training knowledge of Instagram/TikTok/BarkPost/Dogster feature sets (cutoff August 2025) + +### Tertiary (needs validation) +- Clerk Next.js 15 App Router compatibility -- verify at clerk.com/docs before starting +- tRPC v11 stability -- verify at trpc.io / npmjs.com before adopting +- Tailwind CSS v4 migration guide -- verify at tailwindcss.com before upgrading +- Mux current pricing -- verify at mux.com/pricing before committing to video pipeline + +--- +*Research completed: 2026-06-05* +*Ready for roadmap: yes*