Skip to content
Selected work

Client delivery · 2025 – Present

Multi-Location Restaurant Ordering Platform

Problem
A multi-location brand needed mobile and web ordering, provider sync and back-office tools to agree on one order lifecycle.
My role
Full-stack and mobile engineer within a Crave delivery team: Flutter app, Next.js web and admin, Express API, provider integrations.
What changed
Ordering contracts, POS catalog import and outbound orders, webhook status updates, and the fixes in the five stories below.
What was verified
Checkout double-charge, order history and Redis fixes confirmed in production; brand withheld, evidence sanitized.
See where this fits in the order lifecycle

Delivered through Crave for a multi-location restaurant brand: a Flutter ordering app, web ordering, an admin dashboard, and an Express API layer. The system connects store/menu discovery, basket and checkout flows, provider-backed order state, loyalty, notifications, support, and back-office operations. The brand is withheld; Crave publicly describes this work as the guest app for an 80-location restaurant brand.

01 / 04 · System overview

Connecting ordering and operations

Client delivery through Crave, with the brand withheld. Mobile and web ordering share services that connect the restaurant team and its providers.

01 Scope

The product context behind the work.

Problem

A multi-location restaurant brand needed customer ordering and internal operations to stay aligned. Customers needed a clean way to choose a location, browse menus, customize items, check out, and track orders. The operations team needed reliable menus, stores, customers, orders, exports, support, messaging, and analytics in one place instead of several disconnected surfaces.

My contribution

  • Worked across a Turborepo monorepo with a Flutter ordering app, Next.js web surfaces, an operations dashboard, and an Express BFF on shared packages
  • Built and connected ordering contracts for store selection, menu/category reads, modifiers, baskets, checkout context, payment handoff, order history, favorites, delivery addresses, and rewards
  • Supported operations dashboard modules for orders, menus, ingredients, stores, customers, analytics, order exports, support, messaging, notifications, and internal jobs
  • Connected provider APIs, webhooks, background queues, Redis, email/notification delivery, product analytics, error monitoring, support workflows, and Postgres/Prisma persistence so mobile history, order tracking, and admin views stay in sync

Selected production stories

  1. Story 01 · Payments

    Confirmed

    Customers were charged, then told their order failed

    Problem
    On the new-card checkout path the payment and order went through, but the app's final confirmation call sometimes failed. Customers believed the order had failed, and some ordered again.
    My contribution
    I traced it to the managed Redis scripting engine silently dropping a null field from the saved checkout session, fixed the read to treat a missing value as empty, verified it by replaying the failed call on the test environment, and rebuilt each reported customer's session from server logs to separate our bug from a bank decline and a deliberate re-order.
    Verified result
    Roughly 3 in 100 new-card checkouts had hit the error while it was live. No recurrences in the first two days after release, and the store received a per-customer breakdown.
    Proof boundary
    The null field came from one of my own earlier changes about a month before. The fix does not reverse earlier double payments; refunds were a support decision.
  2. Story 02 · Data integrity

    Confirmed

    Order history showed empty for thousands of customers

    Problem
    Signed-in customers who had ordered on the brand's website saw an empty order history in the app. The list required two account identifiers, and website orders carried only one.
    My contribution
    I proved the cause on production data and changed the history to include website orders linked to the account, view-only. Review caught two dangers I fixed first: a case-insensitive email match that treated underscore and percent as wildcards and could match other people's accounts, and orders stored twice after a cancellation.
    Verified result
    Thousands of customers went from an empty history to seeing their orders. Running the new code against production data showed zero duplicates and no regressions before release.
    Proof boundary
    A website guest order placed with a registered customer's email also appears in that customer's history. Twelve lower-priority review findings were deferred.
  3. Story 03 · Reliability

    Implementation confirmed

    Every signed-in feature failed until the server was restarted

    Problem
    Twice in one afternoon, every signed-in request returned errors for a few minutes and the menu failed too. A manual restart fixed it instantly both times.
    My contribution
    I used the restart clue to rule out the managed Redis itself, found the server's single long-lived Redis connection had no keepalive or reconnect strategy and was left half-open when idle connections were dropped, and built the fix: reconnect with backoff, keepalive pings, readiness based on ready rather than open, and fail-fast reads.
    Verified result
    The self-healing client is in production.
    Proof boundary
    Say the connection was made self-healing, not that outages were eliminated; recurrence data after the fix was not recorded.
  4. Story 04 · Integrations

    Confirmed

    Order updates never arrived from the ordering provider

    Problem
    In launch week, production orders stayed at received forever, and item-availability and menu-change events never arrived.
    My contribution
    I proved nothing was arriving at all: the handler rejects bad signatures before storing anything, so an empty event table plus zero signature failures meant zero deliveries. I confirmed our endpoint was ready, traced the gap to a webhook subscription never configured in the provider's dashboard, and wrote the exact setup: destination URL, shared secret and event list.
    Verified result
    Once the subscription was configured, production received order updates from every channel.
    Proof boundary
    The fix was provider-side configuration, not code.
  5. Story 05 · Systems integration

    Core integration confirmed

    Connected an external POS catalog and ordering workflow

    Problem
    The platform needed to import categories, modifiers, products, prices, location context, and nutrition data, then forward customer orders and process provider status updates.
    My contribution
    I implemented the catalog import and outbound-order path, aligned the provider SDK and data contracts, added pricing, location, and nutrition handling, and built the webhook-to-internal-status update path.
    Verified result
    A test order reached the provider dashboard, import fixes were deployed and acknowledged, and relevant deployment checks passed for the core flow.
    Proof boundary
    The complete status-to-notification loop was not independently verified; image import and broader security hardening are not claimed, and scheduling UI was shared work.

02 Approach

Important engineering decisions.

The product is a multi-location restaurant ordering and operations platform. It spans a Flutter customer ordering app, web ordering and marketing surfaces, an internal operations dashboard, and an Express API layer that normalizes the ordering contracts between the frontend products and provider systems.

On the customer side, the ordering experience covers the full path from location selection to menu browsing, product customization, basket management, checkout, rewards, saved addresses, order history, and order tracking. The app and web surfaces do not talk directly to raw provider APIs; they rely on the BFF to expose stable, app-shaped contracts.

On the operations side, the dashboard gives the team control over the back office: orders, menus, ingredients, stores, customers, analytics, exports, notifications, support, messaging, and internal jobs. That matters because restaurant ordering is operational software, not just a cart flow - store availability, menu overlays, payment handoff, order imports, webhooks, email/notification delivery, product analytics, error monitoring, support tickets, and customer history all have to stay aligned.

The architecture is a Turborepo monorepo with shared packages for UI, database access, environment validation, utilities, logging, and config. The API server connects provider-backed ordering, loyalty, checkout, email delivery, push/device notifications, product analytics, error monitoring, and support workflows to Postgres/Prisma persistence, Redis-backed queues, webhooks, cron jobs, and admin-facing read models.

This platform was delivered through Crave, where I work as an engineer and now Director of Engineering. Crave publicly describes the engagement as building and launching the guest app for an 80-location restaurant brand; see craveup.com. The brand is withheld here, and the evidence above stays sanitized.

03 Evidence

How it works.

02 / 04 · Product journey

From choosing food to tracking it

Customers choose food, pay, and track their order. Restaurant teams use the same saved order information.

03 / 04 · System boundary

One connection for web and mobile

Shared services give both apps a consistent way to handle stores, menus, baskets, and orders while connecting to ordering and loyalty providers.

04 / 04 · Delivery / verification

Keeping everyone up to date

Incoming updates and background tasks keep saved orders current for history, tracking, admin tools, support, analytics, and notifications.

04 Delivered & verified

What the public proof supports.

Delivered scope

Customer journey

Location -> menu -> basket -> checkout -> order tracking across mobile and web ordering surfaces

Operations

Orders, menus, stores, customers, analytics, exports, support, messaging, notifications, and internal jobs in one admin system

Architecture

Turborepo monorepo with Flutter, Next.js, Express, Prisma/Postgres, Redis queues, provider APIs, email/notifications, analytics, error monitoring, webhooks, and cron jobs

Stack

TurborepoFlutterNext.jsExpressTypeScriptPrismaPostgresRedisClerk

Reflections

  • The product challenge was not a single checkout screen - it was keeping mobile, web, API, provider integrations, order state, and operations tooling aligned around one ordering lifecycle.
  • The monorepo helped keep shared contracts, config, UI primitives, database access, and integration behavior consistent across a Flutter app, web surfaces, dashboard, and API.

Next step

Have a similar project?

If this looks close to the product work you need, book a short call and I will help scope the next step clearly.