ZL STUDIOStart a project

Case study

GoWeya

Get out. Go together.

Find people who are up for it. Activity-based social discovery, in beta on iOS and Android.

Platforms
iOS · Android
Role
Product · UX/UI · Engineering · Infrastructure · Release
Stack
React Native · Expo · Supabase
GoWeya's map view, showing activity pins across Metro Manila.
A GoWeya activity, showing an approximate location circle rather than an address.

The problem

You are at the mall this afternoon and you want to watch a film, but only with two or three other people. There is no app for that. Group chats are full of people who are busy, event apps are full of things happening next month, and every social app that puts people first eventually turns into a dating app whether or not that was the plan.

GoWeya's Discover tab, listing fifteen activities happening nearby.

The product decision

Activities first. People second.

You browse things to do, not people to meet. There is no user directory, no way to browse profiles, and no fields for romantic intent. That is not a content policy we bolted on, it is the data model. A product where you cannot search for people is a product that cannot quietly become something else.

It shows up in the create flow too. The first question is not who you are, it is whether this is happening in person, online, or is just a conversation.

GoWeya's create flow, asking whether an activity is in person, online or just talking before anything else.

Privacy by architecture

Location is a type problem, not a rule.

Meeting strangers in physical space is the whole product, so location is the whole risk. Most apps handle it with a rule: only send the exact address to people who have been accepted. Rules get forgotten.

  1. 01

    Approximate location

    A guest sees a fuzzed point and a bucketed distance. In the app: somewhere within about 150 m of here.

  2. 02

    Request to join

    Still approximate. Asking does not reveal anything, so nobody can farm addresses by requesting.

  3. 03

    Host accepts

    The decision sits with the person who owns the activity, not with a setting nobody reads.

  4. 04

    Exact meeting point

    Only now do the real coordinates, the address and the meeting point exist for that viewer.

The reason that holds is that a public location and an entitled location are two unrelated types. Not one type with optional fields, not a base class with a subclass. Neither can be assigned to the other, and a single function decides which one a given viewer receives.

  • Distances are computed from the fuzzed point, never the real one, because a precise distance from an imprecise point can be trilaterated back.
  • Display geometry is generated once at write time and stored, never recomputed per request.
  • Precision can loosen over time. It can never tighten in a way that takes information back from someone who already had it.
  • A development-time tripwire throws if an exact coordinate ever reaches a public projection.

Designed for real devices

Constraints we chose on purpose.

360px

Minimum supported layout width

The type scale and every layout assume a 5.5 inch, 720p phone. Nothing is designed on a laptop and then squeezed.

Low data

Budgeted payloads, compressed on device

Data frugality is a design constraint, not an optimisation pass. The launch market is prepaid.

iOS + Android

One shared codebase

React Native and Expo, with native modules where the platform demands them.

What shipped

In people's hands, not on a roadmap.

  • Activity discovery

    Browse by what is happening near you, filtered by interest, time and distance.

  • Map view

    Every activity as a pin, placed on its fuzzed point rather than its real one.

  • Activity creation

    In person, online or just talking. Free to host, a minute to post.

  • Join requests

    Hosts accept or decline. Acceptance is what releases the exact location.

  • Invite-only activities

    Post something visible to nobody but the people you send it to.

  • Activity chat

    A thread per activity, readable by moderation because reporting depends on it.

  • Direct messages

    End-to-end encrypted, and only after proven co-attendance.

  • Circles

    The people you actually do things with, without a follower graph.

  • Recurring series

    The weekly badminton game posts itself.

  • Reliability

    Event-sourced from whether people turned up. A tier and counts, never a score.

  • Identity checks

    Optional verification, surfaced as a badge rather than as a document.

  • Reporting and blocking

    Report, block, and a moderation path that can actually read the evidence.

Engineering

For whoever wants the detail.

Where messages are encrypted, and where they are not

Two surfaces are end-to-end encrypted: a direct thread, and a one-seat activity, which the server seals by construction because two people in a room with no third seat is a direct message. Both are sealed on the device. The server holds ciphertext and never a key.

Group activity chats are deliberately not encrypted, and we would rather say so than round the claim up. Moderation, reporting and the contact gate all depend on being able to read them. An app that tells you everything is encrypted, when most of its traffic is not, has told you something false about the part that matters.

Reputation without star ratings

Nobody is rated out of five. Five-star systems compress a person into a number, punish newcomers, and turn every interaction into a performance. Reputation here is event-sourced from things that actually happened, such as turning up to something you said you would. What other people see is a tier and a few concrete counts. The underlying score is never sent to any client, including your own.

The stack, and why

React Native and Expo for one codebase across both stores, with Apple Maps on iOS and Google Maps on Android. Supabase and PostgreSQL underneath, with row-level security doing the work that application code would otherwise be trusted to remember. Capacity is enforced atomically in a transaction with a row lock, so two people cannot take the last seat. Every state change publishes a domain event through a transactional outbox.

The release plumbing is part of the product: signing, provisioning, store review, staged rollouts and over-the-air updates, so a fix does not have to wait on review.

What it took

The hard parts were not the screens. They were deciding what the product must refuse to do, and then making those refusals structural instead of remembered. That is the work we would want to do on yours.

goweya.com


Want this kind of thinking on your product?

Tell us what you are building and where it is up to. We will tell you what it would take.