All work

Fishbowl

Principal Engineer · Sep 2022 – Present

Full-time
8 customer-facing apps 430+ restaurant clients 2.4M+ API requests/month 2.2M+ events/month 84,000+ loyalty members 45% latency reduction
Vue 3 TypeScript Pinia Vite Turborepo React Native Symfony RabbitMQ PostgreSQL Redis

Context

Fishbowl is a restaurant engagement SaaS platform — loyalty programs, email marketing, guest data, and POS integrations for multi-unit operators. When I joined in September 2022, the company was losing money, the product was aging, and the engineering team was being rebuilt around a core group of engineers tasked with replacing the platform entirely.

The goal wasn’t incremental improvement. It was survival.

The Challenge

The existing platform was a C# ASP.NET monolith backed by SQL Server and hosted on Azure, with an older React frontend layered on top. It worked — but it was expensive to maintain, difficult to extend, and impossible to sell into the enterprise market we needed to compete in. Every new feature pulled against years of accumulated technical debt. Every outage was a referendum on whether Fishbowl would make it to the end of the quarter.

We had to rebuild the entire platform while the existing one kept paying customers live, and we had to do it with a lean team on a timeline that left no room for detours.

Approach & Architecture

We made a series of deliberate architectural bets and stuck with them.

Frontend: Vue 3, TypeScript, Turborepo monorepo. Eight customer-facing applications — the guest relationship management platform, the loyalty member portal, the feedback and reviews surface, the operator tooling for restaurants, the payments app, the signup and onboarding flow, the events surface, and the platform-wide authentication — all living in a single monorepo with a shared component library, shared Pinia state, shared domain types, and shared icon and style systems. One frontend architecture, one design language, one developer experience, eight apps.

Backend: Symfony + Doctrine. We chose Symfony over Node and Rails for the maturity of its ecosystem, the expressiveness of Doctrine ORM, and Symfony Messenger’s first-class support for the event-driven work we planned. PHP isn’t fashionable, but it’s battle-tested, and the team had deep experience with it.

RabbitMQ for messaging. We picked RabbitMQ over Kafka and managed cloud queues because our use case was work queues and inter-service communication, not high-throughput event streaming. RabbitMQ gave us dead-letter queues, retry mechanisms, and mature tooling without the operational overhead of Kafka or the vendor lock-in of a managed queue service.

Vitest and Playwright across the stack. Unit and component tests in Vitest, end-to-end tests in Playwright, running in CI on every PR. I established the testing standards and the CI infrastructure that support them.

The theme across every decision: pick mature, boring, well-understood technology. Save the novelty budget for the business logic.

Key Contributions

Primary author of the frontend platform. I wrote the overwhelming majority of the frontend across eight Vue 3 applications and am the top code contributor to the platform by a significant margin. That includes the customer-facing surfaces (GRM, loyalty, feedback, operator tooling, payments, onboarding, events) and the platform-wide OAuth authentication application they all share. Every app follows consistent patterns because the same engineer wrote them.

Shared component library and design system. Built and maintained the internal component library that serves every application — well over a hundred composable Vue 3 components spanning forms, data display, navigation, payments, dashboards, tables, modals, toast/notification systems, calendars, mapping, reporting, and more. Paired with shared Pinia state stores, shared domain types, and shared style tokens, the library is what makes a single engineer shipping across eight apps possible at all.

Native mobile application. Sole author of the Fishbowl payment mobile application — a React Native app built as a native-first reimagining of the web payments experience. Designed the architecture, built the shared package infrastructure, and shipped the entire frontend myself.

Platform-wide authentication and payments. Built OAuth SSO serving 5,000+ users across eight applications, with OTP-based loyalty verification serving 84,000+ active members at peak traffic. Designed multi-provider payment processing that fans requests out across Stripe, Square, and Checkout.com depending on merchant configuration, with unified error handling and reconciliation.

POS integrations. Designed and built real-time integrations with NCR Aloha, Zuppler, and OLO — each with its own authentication model, data shape, and set of undocumented behaviors. Integration failures cascade badly in a loyalty platform (wrong points, duplicate charges, angry operators), so everything runs through idempotent handlers with dead-letter queues for manual reconciliation.

SOC 2 compliance. Worked directly with the CTO throughout 2023 to achieve SOC 2 Type II certification. This meant implementing formal change management (enforced PR gates, deploy approvals, audit trails), role-based access control across every service, centralized secrets management, structured logging pipelines, and documented incident response processes. The certification closed in late 2023 and directly unlocked enterprise contracts with Landry’s, Lettuce Entertain You, and Raising Cane’s across 1,200+ brands.

Platform-wide performance optimization. Improved system performance by 45% through a combination of Doctrine query optimization, Redis caching at the service layer, eliminating N+1 patterns that had crept in during rapid development, and consolidating over-split microservices into a cleaner Symfony 7.3 API monorepo. Most of the gains came from two places: caching the hot read paths for loyalty lookups, and fixing an eager-loading pattern that was fanning out dozens of queries per guest profile request.

AI integration. Integrated OpenAI APIs to give restaurant operators natural-language insights over their guest data. Operators can ask questions like “which loyalty members haven’t visited in 60 days and what did they order last?” and get an actionable answer. Built the retrieval layer, the prompt infrastructure, and the guardrails that keep the model focused on the operator’s own data.

Technology selection and mentorship. Selected the core platform technologies (Vue 3, Pinia, Vite, Turborepo, Vitest, Playwright, RabbitMQ) and established the patterns used across every service. Mentored engineers on architecture, testing, and the tradeoffs behind each decision.

Technical Deep Dives

Shipping eight frontend apps as one engineer. Shipping a suite of eight production Vue 3 applications with a single primary frontend engineer doesn’t scale through hustle — it scales through architecture. The monorepo structure, the shared component library, shared Pinia stores, shared domain types, and shared authentication application are what make each individual app feel small. A new feature in the loyalty portal often reuses components, types, and state patterns that already exist in the operator dashboard or the GRM, so “building a feature” is frequently “composing a feature.” When a shared primitive needs to change, it changes once, and every app consuming it picks up the change on the next build. Discipline about what belongs in a shared package versus an app-specific implementation is the single most load-bearing engineering decision across the whole platform.

NCR Aloha integration. NCR Aloha is the largest POS system in restaurants, and integrating with it is legendarily difficult. The API surface is inconsistent, error responses are unreliable, and certain operations silently fail under load. We needed real-time sync of menu data, order events, and loyalty redemptions across thousands of store locations. The solution was a three-layer approach: idempotent event handlers at the application layer, a dedicated reconciliation queue for operations that fail in ways Aloha won’t admit to, and a scheduled sweep that compares expected vs. actual state nightly to catch the silent failures. The integration has run in production for over a year without a customer-facing data loss incident.

84,000-member loyalty verification at scale. The loyalty OTP flow looked simple on paper — enter your phone number, get a code, enter the code, redeem your reward. In practice, it had to handle bursts of tens of thousands of concurrent requests during promotions, survive SMS provider outages, and never double-charge loyalty points. We solved it with tight rate limiting at the edge, idempotent verification keyed on the OTP itself, a fallback SMS provider with automatic failover, and careful transaction boundaries around point redemption so a retry never costs the customer twice. The client-side flow was built to match: resilient to flaky mobile networks, recoverable across page reloads, and clear enough that an 84,000-member audience could complete it without support tickets.

Impact

Fishbowl shipped the new platform one year after the project began and turned profitable shortly after. The platform now serves 430+ restaurant clients across 5,500+ store locations, including nationally recognized brands. The SOC 2 certification and enterprise-ready architecture opened doors that were previously closed.

Reflections

The biggest lesson was the power of a small, aligned team making deliberate decisions. We shipped more than teams of ten I’ve been on before, because every architectural choice was made once and applied consistently. There was no politics, no competing visions, no “well, we do it this way in our service.”

On the frontend specifically, investment in shared infrastructure dwarfs investment in individual features. A component library that one engineer builds carefully in month one pays for itself across every feature in months two through thirty-six. Shipping eight apps with a single primary frontend author is only possible if the shared layer is strong enough to absorb most of the work.

Boring technology won. Every time we were tempted toward something novel — a new database, a new framework, a new pattern — we talked ourselves out of it, and every time we were right. Vue 3, Pinia, Vite, Symfony, Doctrine, RabbitMQ. None of it failed us.

SOC 2 paid for itself many times over. It felt like overhead at the time, but it’s the single largest factor in our ability to close enterprise deals. If you’re building a B2B SaaS platform, start the compliance work earlier than feels reasonable.