Personal Project / Social Commentary
ShouldICallDonald: Satirical Political Engagement Website
A satirical web app that turned the day's political news into a comedic 'should you call?' verdict — built solo, launched publicly, and since retired
Year
2024
Role
Full-Stack Developer & Creative Director
Duration
4 weeks
Read Time
7 min read
ShouldICallDonald: When Satire Meets Civic Engagement
ShouldICallDonald was a satirical web app I built and launched in 2024. The premise was simple and deliberately absurd: the app watched the political news cycle and answered one question — should you pick up the phone and call Donald about it, or should you go touch grass instead?
The app is no longer live, but it remains one of my favourite builds. It was equal parts comedy writing project and engineering exercise, and it taught me more about shipping something public than any tutorial ever could.
The Idea
Political news is relentless. Most of us oscillate between doomscrolling and tuning out entirely, and neither feels great. I wanted to build something that poked fun at that anxiety loop — a machine that takes the day's political chaos seriously enough to analyse it, then delivers its verdict with a completely straight face and a joke.
So the app did real work under the hood — pulling headlines, scoring sentiment, categorising events — and then wrapped the output in satire. The contrast between the earnest data pipeline and the ridiculous verdict was the whole joke.
How It Worked
The Event Pipeline
The backend polled news APIs, normalised the results into a common event shape, and scored each one:
interface PoliticalEvent {
id: string
title: string
severity: 'low' | 'medium' | 'high' | 'critical'
category: 'policy' | 'scandal' | 'election' | 'international' | 'economic'
timestamp: Date
sentiment: number // -1 to 1
keywords: string[]
}
Severity was a weighted blend of keyword matching, sentiment score, source, and recency. None of it was rigorous political science — it was a heuristic tuned until the outputs felt right, which is its own kind of engineering problem. Getting a satire engine to be consistently funny rather than randomly weird took far more iteration than the data plumbing did.
The Verdict Engine
The core of the app was the decision function. It took the current event scores plus a few tongue-in-cheek inputs (time of day, day of the week, how the user said they were feeling) and produced a verdict with reasoning:
interface DecisionResult {
shouldCall: boolean
confidence: number
reasoning: string[]
}
The reasoning lines were where the comedy lived. A critical news day at 7am on a Friday might get you:
"Critical political events detected. Democracy might need a check-in call!" "It's early. Even politicians need their coffee before taking calls." "TGIF! End the week with some civic engagement!"
I wrote a bank of these responses keyed to different event categories, severities, and contexts, so the same news day could produce different verdicts depending on when and how you asked. The generator picked lines by matching the current context against the bank:
private generateReasoning(
events: PoliticalEvent[],
factors: DecisionFactors,
shouldCall: boolean
): string[] {
const reasoning: string[] = []
if (events.some(e => e.severity === 'critical')) {
reasoning.push(shouldCall
? "Critical political events detected. Democracy might need a check-in call!"
: "Even with critical events, maybe give democracy a moment to sort itself out first."
)
}
if (factors.timeOfDay < 9) {
reasoning.push(shouldCall
? "Early bird gets the political worm! Perfect time for a morning democracy call."
: "It's early. Even politicians need their coffee before taking calls."
)
}
if (factors.dayOfWeek === 'Friday') {
reasoning.push(shouldCall
? "TGIF! End the week with some civic engagement!"
: "It's Friday. Save democracy for Monday, enjoy the weekend!"
)
}
return reasoning
}
Whatever the verdict, the app followed up with genuine suggestions — call your actual representative, read a second source, or log off and watch some political comedy instead. The satire was the hook, but I wanted the exits from the app to point somewhere constructive.
Wrangling the News APIs
The least glamorous and most instructive part of the build was the news integration. Different providers return different shapes, disagree on timestamps, and rate-limit at inconvenient moments. I ended up with an aggregator that treated every provider as unreliable by default:
async aggregateNews(): Promise<PoliticalEvent[]> {
const results = await Promise.allSettled(
Array.from(this.apiClients.entries()).map(async ([provider, client]) => {
const limiter = this.rateLimiters.get(provider)!
return limiter.execute(async () => {
const cached = await this.dataCache.get(provider)
if (cached) return cached
const data = await client.fetchLatestNews()
await this.dataCache.set(provider, data, 300) // 5 min cache
return this.normalizeData(provider, data)
})
})
)
return results
.filter(r => r.status === 'fulfilled')
.flatMap(r => (r as PromiseFulfilledResult<PoliticalEvent[]>).value)
}
Promise.allSettled plus per-provider rate limiting and a short cache meant one flaky API never took the verdict down with it — the app just leaned on whichever sources were behaving that hour.
Delivery
The whole thing shipped as a mobile-first progressive web app on Next.js and Vercel:
- PWA with service worker — installable on a phone, with cached verdicts available offline
- WebSockets for pushing fresh events to open sessions as the news cycle moved
- MongoDB for the event cache and response bank
- Native share API with graceful fallbacks, so verdicts could be shared straight from the result card
Because it was satire about a divisive topic, I also kept the tone guardrails deliberate: the humour targeted the news-anxiety loop rather than any one audience, and the "actions" the app suggested were the same regardless of which way you leaned — call your representative, read widely, log off sometimes.
What I Learned
Shipping in public is a different discipline. A side project on localhost can be half-finished forever. The moment this had a domain and strangers could load it, everything mattered: error states, load times on bad connections, what happens when a news API rate-limits you at the worst moment. I built more resilience into this four-week project than into things I'd tinkered with for months.
Comedy is a spec like any other. The hardest part wasn't the pipeline — it was making the output land. Too much absurdity and the app felt like a random joke generator; too little and it read as genuinely preachy, which was worse. I ended up treating tone as a tunable parameter and iterating on the response bank the way you'd iterate on any other feature.
Heuristics need constant care. News APIs disagree with each other, sentiment scoring gets sarcasm hilariously wrong (a real problem when your source material is political news), and a severity formula that felt right one week felt off the next. Anything that scores messy real-world data is never "done."
Satire has a shelf life. The app was tied to a specific political moment, and part of the reason it's offline is that the moment moved on. That's fine — not everything needs to run forever. Some projects are worth building precisely because they're of their time.
Why It's Gone
I took the site down when it stopped making sense to keep it running. The joke had run its course, the news APIs cost money to keep polling, and I'd learned what I set out to learn. I'd rather retire a project cleanly than let it rot online.
It still holds a special place for me: it was the first thing I built where the design, the writing, and the code all had to work together, and where real strangers used something I made.
Interested in similar results?
Let's discuss how I can help bring your project to life with the same attention to detail.