The Planning Session

The last Groupr post was all premise — the problem, the vague feature list, the optimistic milestone roadmap. Today was the session where those handwaves turned into actual decisions. Stack, data model, project scaffolding. The kind of work that’s unsexy but determines whether you’re building on solid ground or sand.

Picking the Stack

I spent a good chunk of time thinking through the tech choices before landing on anything. Here’s where I ended up and why:

Next.js (App Router) — A single repo handles both frontend and backend without the overhead of maintaining two separate projects. App Router gives me server components, API routes, and a clean deployment workflow all in one. The alternative was a separate Express or Hono API, but that introduces unnecessary friction in keeping the backend and frontend code in-sync.

tRPC v11 — This ensures end-to-end type safety between server and client. The API contract lives in the TypeScript types themselves. No OpenAPI spec to maintain, no manual syncing of request/response shapes. If I change a procedure signature on the server, the client immediately breaks at compile time. That’s the right kind of feedback.

Tailwind + shadcn/ui — Mobile-first from the start, since Groupr will primarily be used on phones. shadcn gives me polished, accessible components that I own outright — not a dependency to upgrade around, just files in the project. Tailwind handles everything else.

Better Auth v1 — I looked at Auth.js and Clerk, but kept coming back to Better Auth. It’s open source, the TypeScript API is clean, and it ships with MFA and rate limiting built in. For an app where community trust matters, I didn’t want to paper over auth with the minimum viable solution.

Prisma 7 + PostgreSQL — Groupr’s data is fundamentally relational. Groups, members, proposals, votes — these all have clear relationships and constraints that a relational database handles well. Prisma’s TypeScript support is strong, the migration story is straightforward, and I already know it. No reason to reach for something else.

EKS on AWS — I know the AWS ecosystem. The overhead of containerizing from day one is worth it to avoid a painful migration later when (if) this actually needs to scale. I’ve done that migration before. Never again.

Designing the Data Model

The data model took a few passes, but it’s well worth the extra time to get it right.

The core entities:

One thing I deferred deliberately: Events. It was tempting to design the event system now while the model is fresh, but events touch a lot of surfaces — RSVPs, recurring schedules, notifications. Designing that half-baked creates constraints I’ll regret. Events are scoped to v0.1.

Games are implemented as string tags on the Group model rather than a normalized table. A proper game library with a Game entity and a many-to-many relationship is the right long-term answer, but for MVP it’s overhead that doesn’t buy anything. A list of strings is queryable enough to find groups by game type and trivially easy to edit.

Key Design Decisions

Two things I’m most deliberate about, because they shape the whole experience:

No group owner. Groups in Groupr are democratically owned by all members equally. There’s no “founder” role, no single account that can unilaterally edit the group or remove people. Every consequential action — changing the group description, removing a member, disbanding — goes through a Proposal that all members vote on. Proposals have a 24-hour timeout; if a majority votes yes before then, the action executes. This is intentional friction. It’s what makes Groupr feel like a community rather than someone’s fiefdom.

Group creation via GroupCharter. You can’t spin up a group alone. Group creation requires at least two people to go through a back-and-forth negotiation — the GroupCharter — where they jointly agree on the name, description, and initial rules. It’s a small thing, but it means every group starts with at least two humans who both wanted it to exist. No ghost groups, no accidental duplicates, no one-person “groups” that are really just profiles.

The Conversation model unifies DMs and group channels under the same schema. A DM is just a Conversation with two ConversationMembers. A group channel is a Conversation associated with a Group. Same queries, same message rendering, same notification logic. I like designs that collapse surface area.

First Commit

With the model in place, I scaffolded the project:

Nothing works yet in the sense that a user can’t do anything. But the plumbing is in and the types are talking to each other. That’s the foundation.

What’s Next

  1. Auth flows — signup, login, and session-aware layout
  2. User profiles — basic profile page, editable fields
  3. GroupCharter flow — the two-user group creation negotiation
  4. Group discovery page — browsable list with game tag filtering

The fun part starts now.

— Shai Guy