PROJECT 002 · CASE STUDY
Politicist
A social network for US politics. Users take stances on issues, follow their real elected representatives, and get a feed that blends people with automated news ingestion.
Problem
Political discussion online happens in places built for outrage. Politicist’s founder wanted a social network built around structure instead: users publish stances on specific issues, back them with evidence, and see what their actual elected representatives are saying and doing.
That turns into three products wearing one interface. A social network (posts, friends, messaging, notifications), a civic data layer (who represents this address?), and a content pipeline that keeps the feed alive with news and representatives’ posts even before the user’s friends show up.
What I built
I led the build at our agency over roughly two and a half years: a FastAPI backend, a Next.js frontend, and a Forest Admin back office, all on GCP.
- The issues system: users create or adopt issues, take a positive, neutral, or negative stance, attach evidence (articles, books, videos), and hand issues over to community moderation, with merge and share workflows for duplicates.
- Representative lookup through the Google Civic Information API: a street address resolves to actual officials from president down to house member, reconciled against our own representative records with fuzzy name matching.
- The ingestion pipeline: Cloud Tasks fans out one job per source, pulling RSS and YouTube feeds from curated news outlets and think tanks, plus each representative’s tweets, and turning them into feed posts with scraped OpenGraph imagery.
- Real-time chat and notifications on Firestore, sitting beside Postgres, which keeps all the relational and political data.
Decisions worth explaining
A trending feed that paginates honestly. “Trending” ranks issues by stance activity in the last 24 hours, which means the ordering shifts between requests. Offset pagination breaks under that, so the cursor packs the ranking metric and the row timestamp into one composite key. Users can page through a moving leaderboard without duplicates or gaps.
Split storage by access pattern, not by fashion. Chat and notifications want low-latency fan-out, so they live in Firestore. Issues, stances, representatives, and the social graph are join-heavy, so they live in Postgres. Two databases is a cost, and it was cheaper than making either one do the other’s job.
A circuit breaker around the government API. The Civic Information API backs the core representative flow and it goes down. A hand-rolled breaker (three failures, one-hour cooldown) with an offline zip-code fallback keeps signup working through outages instead of hard-failing on a third party.
Deduplication tuned to how news actually syndicates. The same article arrives from multiple source URLs hours apart. Matching on title within a 24-hour publish window, with sources cross-linked in a join table, keeps the feed clean without a heavyweight similarity system.
Outcome
Politicist is live at politicist.com. The build ran about 5,400 commits across the backend and frontend under a 95% test coverage gate, and the ingestion pipeline keeps the product feeling alive independent of user volume, which is exactly what a young social network needs.
Stack
FastAPI, SQLAlchemy, and Postgres on App Engine, Next.js and TypeScript on the frontend, Firestore for real-time features, Cloud Tasks for ingestion, Firebase auth, and SendGrid.