Web app New build

Yinz Do: an events app that works without harvesting anyone

A social-style feed showing Pittsburgh what is happening tonight, built in six sprints with no GPS, no social graph, and no ad tech, because the data it never collects is data it can never lose.

ClientChristine McGee
IndustryLocal Events & Consumer Apps
Timeline12 weeks to MVP

The problem

Finding something to do in Pittsburgh is a discovery problem, not a supply problem. The events exist in abundance. They are just scattered in places nobody can search at once.

  • The listings are fragmented. Venue sites that have not been updated in years, three separate ticketing platforms, a Facebook event somebody's cousin made, and a flyer taped to a door on Carson Street.
  • So people give up. Check four sites, find nothing coherent, conclude there is nothing to do. There is plenty to do.
  • The apps that could fix it want too much in return. Contacts, location history, an ad ID, and a permanent seat inside your phone, in exchange for a list of trivia nights.
  • And the obvious way to get the data was a trap. The original prototype pulled listings by scraping, which is the fast way to a demo and a slow-motion failure in production.

The brief was to solve discovery without building the surveillance apparatus that usually comes with it.

What I built

Four things, kept deliberately small.

  • A feed you can toggle. General Feed is straight chronological, so the next thing happening shows up first. Custom Feed filters to chosen interests and followed venues. One tap between them, and that is the entire navigation concept.
  • A profile that barely knows you. Display name, zip code, a handful of interest tags, and the venues you follow. That is the whole user record.
  • Event data from sources that hold up. Official API pulls from Ticketmaster and Eventbrite, direct submissions from venue partners, and an admin approval queue for anything from a source not yet trusted. A dedup engine catches the same show listed three different ways.
  • A partner dashboard. Venues post their own events, see what is live, and fix a typo without emailing anyone.

The privacy position is enforced by the data model rather than asserted on a landing page. Distance comes from the zip code you type, so the app never requests location permission and there is no location history to leak or subpoena. There is no friends list, no follower count, and no contact upload, because there is no table for relationship data. No profile photos, which skips image hosting and biometric-adjacent storage entirely. No ad tech, no third-party pixels, nothing on the page phoning a stranger.

The single most consequential decision was made in the first working session: replacing the prototype's scraping layer with official APIs before any production code shipped. It cost early velocity and it is the reason the feed cannot go dark the week a platform decides to block scrapers.

The usual events appYinz Do
LocationLive GPS permission and location historyA zip code you type in
Social graphFriends, followers, contact uploadNo relationship data, because there is no table for it
Profile photosUploaded and storedNone, so no image hosting and no biometric-adjacent data
Check-inGeofencing and auto check-inCut before a line of it was written
TrackingAd tech, third-party pixelsNone on the page
Sign-inOAuth handoff to an identity brokerEmail and password, hashed, with refresh tokens
Event dataScraped and fragileOfficial APIs, venue partners, moderated queue
Business modelAds and data, which need scale to workVenues pay for placement and analytics, which works at 500 users

The outcome

Yinz Do is live at yinzdo.co, shipped in twelve weeks across six sprints against a scope written as two lists: what ships, and what explicitly does not. The second list was longer, and it is why the date held. Push notifications, AI recommendations, third-party sign-in, native store builds, price filtering, and Elasticsearch were all cut on purpose, each for a stated reason rather than a shrug.

The result installs to a home screen, opens full screen, and runs from one React codebase instead of three. The data model is city-agnostic, so a second city is a data exercise rather than a rewrite. And because the privacy decisions live in the schema, they cannot quietly erode the first time somebody wants a growth metric.

12 weeks
MVP delivered
Six two-week sprints against a scope fixed before sprint one
Zip code
Personal data collected
No GPS, no contacts, no social graph, no profile photos
Zero
Third-party trackers
No ad tech and no pixels, which is also why pages load fast
3
Event sources
Official APIs, venue partners, and a moderated submission queue, so no single failure empties the feed

Built with

ReactTypeScriptTailwindNode.jsPostgreSQLPostGISRedisProgressive Web App

Killing the scraper before it killed the app

The prototype pulled event data by scraping. That is the fastest route to a working demo and a slow-motion disaster in production: the major platforms are actively blocking scrapers, and the ones that are not will be. A business built on that foundation works fine right up until it does not, with no recourse and no warning.

I flagged it in the first working session and re-architected the ingestion layer around official Ticketmaster and Eventbrite APIs before any production code shipped.

It cost some early velocity. It also means the app does not wake up one Tuesday with an empty feed and nothing to do about it. Catching that in week one instead of month nine is most of what a developer is actually for.

Privacy as a schema decision, not a policy page

Anyone can write “we respect your privacy” on a landing page. The only proof that means anything is what the system is structurally incapable of doing.

Distance to an event is calculated from a zip code the user typed, so the app never requests location permission and no location history exists to leak, subpoena, or sell. There is no friends list and no contact upload, so there is no relationship data in the database, because there is no table for it. Skipping profile photos skips image hosting and biometric-adjacent storage for people who only wanted to find a comedy show. Geofencing and automatic check-in were cut before a line of either was written, on the grounds that they are battery-hostile, unsettling, and unnecessary.

The convenient thing about not collecting data is that you cannot lose what you never had. A good deal of what gets called a security posture is really just cleaning up after an appetite nobody needed to have.

Twelve weeks meant saying no, repeatedly

The MVP scope was written as two lists, and the list of things that were not being built was the longer one: push notifications, email digests, AI recommendations, Google and Apple sign-in, native store builds, price filtering, friend visibility, promoted listings, artist profiles, Elasticsearch, and an itinerary planner.

Each cut had a reason rather than a shrug. Elasticsearch went because PostgreSQL full-text search is genuinely sufficient at launch scale, and adding a second search system would have spent a week of infrastructure on a problem that did not exist yet. Price filtering went because ticket price data is inconsistent across sources, and a filter that lies is worse than no filter at all. Push notifications went because they need a preferences interface, a content strategy, and a reason, and the project had none of the three.

Sprints ran in pairs: auth and schema, then ingestion and deduplication, then the general feed, then custom feed matching, then the admin queue and partner dashboard, then QA, performance, and deployment.

Scope discipline is the whole trick. Timelines rarely slip because the work is slow. They slip because the work quietly grows and nobody says no out loud.

Questions this raises

If the app does not sell ads or data, how does it make money?

Venues are the ones who benefit most, so venues are the customer. Promoted placement in the feed, partner tiers, and analytics on their own listings, sold as a straightforward business product and labeled honestly. The version of this that requires surveillance is also the version that requires scale, and scale is exactly what a local app does not have on day one. Charging venues works at 500 users; ad revenue does not work until 50,000.

Why a progressive web app instead of native iOS and Android builds?

One codebase instead of three, and no app store review sitting between a bug and its fix. It installs to the home screen and opens full screen with native-style bottom navigation. If native distribution matters later, the React codebase wraps into React Native without starting over. This was the fastest honest path to a shipped product, not a shortcut.

Can it handle a real user base?

PostgreSQL with PostGIS handles the geospatial queries, Redis caches feed reads and handles rate limiting, and event ingestion runs as a separate scheduled service that scales independently of the app. The data model is city-agnostic, so expanding to another city is a data problem rather than a rewrite.

What happens if the event data is wrong or thin?

Three sources feed the pipeline, so no single failure empties the feed. Trusted API sources publish automatically; everything else goes through an approval queue before a user sees it. Deduplication runs on title, date, venue, and performer so the same show does not appear three times.

How much of this can be changed later without the original developer?

It is a standard, boring, well-documented stack: React, Node, PostgreSQL. No proprietary framework and no exotic dependency that requires one specific person. You own the code and the infrastructure accounts.

How do you make a 12-week timeline actually mean 12 weeks?

By writing the scope as a list of things you are not building and agreeing to it before sprint one. Timelines rarely slip because developers are slow. They slip because scope grows quietly and nobody says no out loud.

Have something like this to build?

Tell me what you're working with and where it's stuck. If it's a fit, we'll book a scoping call - no obligation, and I respond personally.

Let's Talk →