01 — Work

Systems, not screenshots

Most of my work lives behind a customer login, so each entry is the write-up instead: the constraint, the architecture, and what I'd change. Personal projects are marked as such.

ANY FEATUREMESSAGE ENGINECHANNELSLoad eventsDocumentsIntegrationsNotificationserviceROUTE BY TYPECHECK PREFERENCESRABBITMQPreferencesPER USER · PER TYPEIn-appSIGNALR · ON BY DEFAULTEmailPER PREFERENCENext channelNEEDS A NEW COLUMN TODAYPUBLISH ONCE · DELIVER PER PREFERENCEWork
01 · Messaging2026
Notification SystemA centralized notification system with in-app and email delivery, and per-user preferences so recipients control what they get and which channel it arrives on.
Zeronotification polling requests
C#RabbitMQSignalRSQL Server
BEHIND A LOAD BALANCERDispatcherDispatcherDispatcherWEBSOCKETAPI instance 1SIGNALR HUBAPI instance 2SIGNALR HUBAPI instance 3SIGNALR HUBPUBLISHDELIVERDELIVERRedis backplaneSHARED PUB/SUB CHANNELSANY INSTANCE PUBLISHES · EVERY INSTANCE DELIVERSWork
02 · Realtime2025
SignalR Backplane with RedisKeeping real-time updates in sync across a horizontally scaled application, so a message reaches the people it is meant for no matter which instance they landed on.
Fourreal-time feeds on one backplane
C#ASP.NET Core SignalRRedisAzure
BEFORE · SYNCHRONOUSCONTROLLERSERVICEDATABASETHREAD BLOCKED FOR THE WHOLE REQUESTAFTER · AWAIT END TO ENDCONTROLLERSERVICEAWAITING I/O · THREAD FREEANOTHER REQUEST USES THE THREADMIGRATED CONTROLLER BY CONTROLLER · NO REWRITEWork
03 · Performance2026
Sync to Async MigrationMigrating a live production backend from synchronous to asynchronous, one controller at a time, without a rewrite and without taking the product down.
100%of the API layer migrated
C#.NET
UPDATEDSignalR hubGROUPS BY ACCESSOther tenantYour tenantOther tenantUNDER 100 ROWS ON SCREEN · SCOPED PUSH, NOT REFRESHWork
04 · Realtime UI2022-now
Dispatch Live GridThe operations grid dispatchers work out of, streaming live status changes into a Kendo Grid over the SignalR backplane so they see a load move the moment it moves.
Livereplaces manual page refresh
AngularKendo GridSignalRTypeScript
ON SAVESaveAudit middlewareDIFFS CHANGED FIELDSOPT-IN PER ENTITYEntity tableAudit logIDS ONLYON READKEPT TWO QUARTERSAudit logResolve IDsLOOKUP AT READ TIMEChange historyFROM → TO · WHOCHEAP TO WRITE · RESOLVED ON READWork
05 · Auditability2025
Audit LogMiddleware sitting in front of every save that records which fields changed, what they changed from and to, and who changed them.
2 quartersof field-level change history
C#.NETAngular
Any queryNO WHERE CLAUSEBRANCH A ACCESS KEYTENANT FILTERBRANCH ABRANCH B×BRANCH C×BRANCH ABRANCH C×BRANCH B×BRANCH AISOLATION AT THE ORM · NOT IN EVERY QUERYWork
06 · Multi-tenancy2022-now
Multi-tenant Access ControlTenant isolation enforced by scoped access keys and an EF Core global query filter applied to protected entities, so isolation does not depend on every query remembering its WHERE clause.
One filterinstead of every WHERE clause
C#Entity Framework CoreSQL Server