Case study · Fintech · 2024

Fintech Reporting Dashboard200M rows · 60% faster · sub-second queries

Parquet/Arrow-backed reporting layer with Plaid and Stripe ingestion and reconciliation pipelines, replacing an overloaded Postgres that was spending more time locking than serving.

200M
Rows indexed
across 3 years
60%
Query speed
faster median
420ms
Dashboard p95
down from 7.2s
$0
Reconciliation gap
post-cutover
Fintech reporting dashboard architecture — OLTP rail with Postgres system of record, OLAP rail with Parquet lake and DuckDB, reconciliation bridge every fifteen minutes, late-arriving Stripe dispute pipeline.
Two-rail OLTP + OLAP with a 15-minute reconciliation bridge
How it works · step by step

The diagram, walked through in plain language

  1. 1
    Two databases, two jobs

    The original Postgres database keeps doing what it's good at: handling live transactions (a customer adds an invoice, payments come in via Plaid and Stripe, etc.). It is the legal source of truth and stays that way.

  2. 2
    A copy goes to a 'data lake'

    Every change in Postgres is mirrored to a Parquet file lake on Amazon S3 — a format that compresses well and is built for analytics rather than transactions.

  3. 3
    Reports query the lake, not the live database

    The dashboard talks to DuckDB (a small, fast analytics engine) reading from those Parquet files, with the most recent month held in memory for instant answers.

  4. 4
    Late events get handled gracefully

    Stripe disputes can arrive 90 days after a charge. A separate pipeline rewrites only the affected days in the lake without breaking older reports or invalidating cached numbers.

  5. 5
    The two sides reconcile every 15 minutes

    A check runs the same totals on both Postgres (truth) and the Parquet lake (reports). If they ever disagree by more than a rounding cent, the team is alerted before the customer notices.

  6. 6
    Customers see the numbers, not the plumbing

    The React + Grafana dashboard hits the fast path; nothing about the new architecture changes the bookkeeping the customer sees.

The brief

An SMB-focused fintech had a dashboard that was becoming an embarrassment. Queries for a 90-day P&L took 7 seconds on a good day, 40 seconds on a bad one, and the root cause was structural: they were running analytical aggregations against their OLTP Postgres that was also handling every ingestion write.

They didn't need magic. They needed to stop punishing a transactional database for doing analytics work, without rebuilding the data model from scratch.

The constraints

  • System of record stayed on Postgres. Regulatory constraints ruled out moving it.
  • Every reported number had to reconcile to the cent against the Postgres ground truth, continuously and automatically.
  • Ingestion had to handle Plaid's inconsistent webhook ordering and Stripe's delayed dispute events gracefully.
  • Dashboard latency budget: < 1 second for 95% of queries.
  • Infra cost had to be roughly flat — this was a cost problem, not a spend-more problem.

The shape we built

Postgres kept its job as the system of record. Alongside it, a Parquet lake on S3, partitioned by tenant and month, maintained by a thin Python pipeline that reads from Postgres's logical replication slot and writes columnar files.

Queries stopped hitting Postgres. The dashboard hits a DuckDB process running against Parquet files, with the hot month held in memory via Arrow. Reconciliation runs every fifteen minutes: the same aggregate computed both places, diffed, alerted if non-zero.

Ingestion got its own reliability layer. Plaid webhooks land in an idempotent inbox; out-of-order events are reordered by timestamp before commit. Stripe disputes, which can arrive 90 days after the original charge, flow through a separate late-arriving events pipeline that rewrites only the affected daily partitions.

What was hard

  • Timezone semantics. “Monthly revenue” meant four different things across four different reports. We standardized on account-local midnight-to-midnight, which sounds obvious and took two weeks to sell.
  • Reconciliation tolerance. Float arithmetic on a long series of fees produced 0.01-cent differences that were technically real and technically unactionable. We round to the cent at the boundary and log the pre-rounding delta separately.
  • Stripe dispute backfill. The late-arriving events pipeline rewrites history, which means caching has to be smarter than “invalidate after 24 hours.” We tag every cached aggregate with the latest source event ID.

What it does today

The dashboard that used to take 7 seconds now answers in 420ms at p95. Median queries are 60% faster than the Postgres baseline across the full reporting surface. The Parquet lake indexes 200M rows across three years of history. Reconciliation runs every 15 minutes against Postgres and has not found a material discrepancy since cutover. Infra spend is within 5% of the pre-migration number.

What I'd do differently

I'd have shipped reconciliation before the new dashboard, not alongside it. Turning on a new reporting path when you can't yet prove its numbers match the old one is a conversation you do not want to have with finance. Once reconciliation was green, everything else felt like paperwork.

Stack
  • Python · PyArrow · DuckDB
  • Plaid · Stripe ingestion
  • Postgres (system of record)
  • AWS Lambda + S3 (Parquet lake)
  • dbt (transformation layer)
  • Grafana + custom React dashboard
More work

Continue the tour

Algo Trading · 2025

Order Router & Execution Engine

$80M routed · 38ms p99 · zero downtime

A trading desk's chart fires a buy or sell signal; this system safely turns each signal into a real order at the right brokerage in milliseconds — while quietly making sure they never trade more than they meant to or place an order they can't afford.

Read case study
AI / LLM · 2024

AI Content Platform

10K daily users · 12 models · 35% lower cost

A SaaS that generates marketing-style writing (articles, ads, product copy) for thousands of paying users — intelligently picking the cheapest AI model that can do each job well, and switching providers in seconds when one of them goes down.

Read case study
SaaS · 2024

JobbyAI

resume scoring · job match · interview prep

A free web app that helps job seekers in three ways: it scores their resume, ranks how well they match a job posting, and prepares them for the interview — all using a single AI model behind the scenes, with no signup required to try it.

Read case study
Algo Trading · 2023

Quant Backtest Harness

50K parameter combos · 3 engines · one CLI

A single command-line tool that lets a quant team test trading strategies on three different simulation engines without rewriting any strategy code — and then compares the results in one shared format, so 'which strategy is actually better' becomes a question with a real answer.

Read case study
Fintech · 2023

Accounting API Sync

4 providers · one trait · zero drift

A behind-the-scenes service that keeps an accounting SaaS in sync with QuickBooks, Xero, Wave, and AccountEdge — when a customer edits an invoice in either place, the change shows up on the other side within 30 seconds, without ever silently overwriting work.

Read case study
AI / LLM · 2025

Multi-LLM Agent Runtime

OpenAI · Claude · Gemini · Grok

A small, stateless service that lets non-engineers wire up AI 'agents' (which can call tools, look things up, and reply) — running across four AI providers so a single outage never takes a customer offline, and replay-able to the byte for debugging.

Read case study
Algo Trading · 2024

TradingView ↔ Plaid Bridge

webhook in · broker-native out · 4 signal types

A bridge that takes 'buy' or 'sell' alerts from TradingView charts, checks the user actually has the cash via their bank link (Plaid), then sends the order to their brokerage — all in under a fifth of a second, so the price they wanted is still the price they get.

Read case study
DevTools · 2023

Figma + Chrome Plugin Suite

design · engineering · less friction

Three small browser plugins that quietly fix the slow, fiddly hand-off between designers (working in Figma) and engineers (writing code) — saving each engineer about four hours a week of busywork that nobody was tracking, but everyone resented.

Read case study

Have a similar problem?

If this shape of engagement fits what you're working on, I'd be happy to scope it.