Rewiring the foundations (May, in progress) ๐๏ธ
Published about 2 months ago ยท 4 min read
May was the month we pulled up the floorboards., you may have noticed a few things wobble. Let me explain what we did and why.
Here's the 60 second recap:
The Big One: Trading Lifecycle v2 ๐๏ธ
For years, "what does this user own and what did they pay for it" was answered by replaying every closed order from the broker on every page load. it worked, but it was fragile, a single missed fill, a retry, a worker failure, and the numbers could drift.
We've split that into four cleanly separated records:
- OrderIntent: What you asked for
- BrokerOrder: What we sent to the broker
- BrokerFill: What actually filled
- PortfolioPosition: What you hold right now
Your dashboard now reads PortfolioPosition directly instead of replaying history every time.
Faster, simpler, and every dollar is auditable end to end. Portfolio-changing operations are also locked per portfolio now, so two background jobs can't fight over the same fund.
But: the Post-Deploy Bumps ๐๐ซ
We shipped v2 backend in May 9th, between then and May 11th, a real bug surfaced: portfolio order fills weren't being written into BrokerFill, so a chunk of users saw $0 on their dashboards even though Alpaca had actually filled the orders and debited cash. Artaza emailed us. then Gohar and Hamza noticed when they checked Alpaca directly.
~65 users, ~820 orders. we paused the queue, root-caused it (Alpaca's /v2/orders response doesn't carry the fill-id field we were depending on), and shipped a fallback plus a cohort-wide backfill. everyone affected has been reconciled. if your dashboard ever showed an unexplained $0 in early May and then quietly fixed itself, this is what happened.
a similar bug hit individual stock positions (PR #425) โ pre-v2 stock buys never got migrated forward, so they disappeared from the Stocks list. backfilled, fixed.
we also caught and fixed:
- a stale
ScheduledOrderrow burst during the same deploy window - a
reconcileBrokerOrdersworker hanging for 24h+ on a single Alpaca call (now bounded with a 60s timeout) - automatic liquidations getting re-burst from stale queue jobs (now require a DB-backed authorization record before any sell goes out)
honest version: a foundation rewrite of this size doesn't ship cleanly. we caught the issues fast, fixed them with proper backfills, and the new architecture is genuinely better for the next few years of building.
chart timeframes you can actually pick ๐
your portfolio history and per-fund charts now have 3M / 6M / 1Y / 2Y selectors. previously we were locked to 3 months. cached in your session, so flipping between ranges is instant. as a bonus, we batched the underlying queries โ even the default 3M view is now slightly faster than before.
export fund history as CSV ๐
per Atif's request: from any fund detail dropdown, you can now Export History to CSV. invested amount vs portfolio value, sampled twice a month from inception, with gain/loss per row. uses the same playback engine that handles splits, mergers, and dividends โ so the numbers match what's in the app.
dashboard banner when your broker disconnects ๐ก๏ธ
if you disconnect your broker, you can now still see your historical positions and realized P&L (great for Zakat) instead of getting bounced to onboarding. we added a clear banner at the top of the dashboard so you know what you're looking at and how to reconnect. one user used this exact path to calculate his purification โ exactly the use case we wanted to support.
mobile Safari + accessibility fixes ๐ฑ
three quiet wins on the fund modal:
- the withdraw keyboard now actually shows up on Mobile Safari (it was getting stuck in a focused-without-keyboard state)
- the amount step no longer steals focus from the dialog title (a11y)
- a decimal input hint plus a guard against the market-value shortcut accidentally submitting the form
filtered holdings: cleaner, more honest ๐งน
- "dust" rows (positions with effectively zero quantity) no longer pollute your filtered holdings list
- warrants and other corporate-action splits no longer create duplicate preview orders
- deleted slices are hidden from the filtered holdings UI
under the hood (you shouldn't notice but should know) ๐ง
- migrated the entire dashboard shell to a fresh shadcn / Tailwind v4 / React 19 stack
- bumped Node engine to 24 (LTS now)
- production builds switched to Turbopack
- dropped Vercel OpenTelemetry, simplified the observability stack
a lot of this month went under the surface. the new trading architecture is the foundation for things we couldn't build cleanly on the old one. thanks for your patience while we ship the messy middle โ the rest of the year gets to ride on top of it.
more soon โ this isn't the last update for May.
Best, Yazin