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

designengineeringstrategy
ShouldICallDonald: Satirical Political Engagement Website - Personal Project / Social Commentary

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.

ReactTypeScriptNext.jsNode.jsMongoDBPWAWebSocketsVercel

Interested in similar results?

Let's discuss how I can help bring your project to life with the same attention to detail.