Technical Review
Context Acceleration Program (Demo Project)

Technical Review
FoodRunner AB

Independent technical due diligence of FoodRunner's B2B SaaS platform for food delivery logistics, covering code quality, architecture, security, scalability, and DevOps maturity.

5 high-risk, 8 moderate-risk, 4 low-risk
1
Executive Summary
Solid technical foundation

FoodRunner's core engine for route optimization and order management is well-built and production-stable. The platform currently serves 200+ restaurants in Stockholm with good performance. The TypeScript codebase follows modern patterns, and the NestJS-based API is logically structured with clear separation of business logic.

The platform demonstrates strong product-market fit and a competent technical foundation, but a number of deficiencies need to be addressed before the next growth phase.

Areas for improvement

The review identified four key areas requiring action:

  • Missing rate limiting: The API is exposed without rate limiting, opening it to abuse and DDoS attacks against the order engine.
  • No automated tests: 12% test coverage means regressions go undetected — critical when refactoring route logic.
  • Monolithic architecture: The entire platform runs as a single NestJS instance, limiting independent scaling of e.g. courier tracking.
  • GDPR gaps: Personal data for customers and couriers is stored without encryption at rest and lacks automated deletion routines.

Summary

Area Status Comment
Core logic OK Route optimization and order flow are robust and handle real-time data well.
Security Weak Missing rate limiting, GDPR compliance gaps, API keys hardcoded in config.
Scalability Partial Monolith's coupled modules prevent horizontal scaling under high load.
Code Good Consistent TypeScript, clear folder structure, good naming conventions.
CI/CD Halfway GitHub Actions configured but lacks staging environment and automated rollbacks.

What does this mean?

FoodRunner has a strong technical core that proves the product's market value. The identified risks are typical for a company in expansion phase and can be addressed with targeted investments. None of the deficiencies constitute a structural barrier to scaling — but security and testing should be prioritized before expanding to new cities.

1
What is FoodRunner AB?

FoodRunner AB is a B2B SaaS platform for food delivery logistics that connects restaurants, couriers, and end customers in real time. Founded in 2022 in Stockholm, the platform currently serves over 200 restaurants with a team of four developers.

Codebase

48,000 lines of TypeScript, uniform style with ESLint and Prettier

Well-structured
Architecture

Monolithic NestJS application deployed on AWS ECS with Docker

Monolithic
Database

PostgreSQL for persistent data, Redis for caching and real-time queues

Mature
Testing

12% test coverage, mainly manual tests

Under-tested

Core Functionality

Route Optimization

Real-time calculation of optimal delivery routes based on courier position, traffic data, and delivery windows. Handles multi-stop logic.

Order Management

Complete order lifecycle from restaurant confirmation to delivery receipt with WebSocket-based real-time status and automatic assignment to nearest courier.

Restaurant Portal

Web-based dashboard where restaurants manage menus, opening hours, orders, and view statistics on delivery times and customer satisfaction in real time.

Courier App

React Native app for couriers with GPS tracking, delivery queue management, earnings overview, and push notifications for new assignments.

2
Overview

What works well

Area Status Details
Route optimization Production-ready Routing implementation is designed for low-latency response and handles multi-stop efficiently with Redis cache support.
Order flow Solid Complete lifecycle management with WebSocket updates and automatic courier assignment.
Code quality Functional Consistent TypeScript code style, ESLint rules followed, clear module structure in NestJS.
Database design Complete Normalized PostgreSQL schema with correct indexes on frequently queried columns.
Real-time tracking Implemented GPS positions from couriers are streamed via WebSocket to provide near real-time updates to customers.

What needs to be addressed

Issue Severity Business Impact
Missing rate limiting High Security: The API can be overloaded by malicious actors or buggy client implementations, risking downtime during lunch peaks with 200+ active restaurants.
12% test coverage High Quality: Route optimization logic lacks regression tests. Changes to the core algorithm can cause incorrect delivery calculations without being detected before production.
GDPR compliance High Legal: Customer data and courier GPS history stored unencrypted without automated deletion. May result in fines upon audit and constitutes a barrier to expansion into more EU markets.
Monolithic architecture High Scalability: A single NestJS instance handles everything from route calculation to the restaurant portal. Individual modules cannot be scaled independently under increased load.
Hardcoded API keys High Security: Third-party keys for map data and payments reside in config files in the repo instead of a secrets manager, exposing them in case of code leaks.
No staging environment Moderate DevOps: Code is deployed directly to production without intermediate steps. No ability to verify new features under production-like conditions before launch.
Manual database migration Moderate Operations: Schema changes are run manually against the database without version-controlled migration scripts, creating risk of inconsistency between environments.
Incomplete logging Moderate Debugging: Structured logging is missing for the order flow. Debugging delivery issues requires manual review of unformatted logs in CloudWatch.
Missing WebSocket error handling Moderate Stability: WebSocket connections for real-time tracking lack reconnect logic and graceful degradation during network interruptions.
Insufficient documentation Moderate Onboarding: API documentation and architecture descriptions are largely missing, making it difficult to onboard new developers during team expansion.

Conclusion

FoodRunner has a proven core product with functioning route optimization and order management serving 200+ restaurants in production.

Recommended investment priorities:
Phase 1 (Critical): Implement rate limiting, move secrets to AWS Secrets Manager, begin GDPR remediation
Phase 2 (High): Raise test coverage to 60%+ on core modules, set up staging environment
Phase 3 (Medium): Begin gradual decomposition of the monolith, starting with the routing module as a separate service

Overall, the technical debt is estimated to be manageable with an investment of 1–2 senior developers. The platform is well-positioned for expansion after remediation.

1
System Architecture

FoodRunner AB's platform is built on a service-oriented architecture with a NestJS monolith at its core, exposed via Kong API Gateway. Client applications include a React-based dashboard for restaurants, a React Native app for drivers, and a Next.js site for B2B customers.

System Topology
Client
React DashboardRestaurant Portal
Mobile
React NativeDriver App
Web
Next.jsB2B Customer Portal
Internal
Admin PanelBackoffice
API Gateway
Kong GatewayRate limiting, auth, routing
NestJS MonolithBusiness Logic & REST API
WebSocket ServerReal-time Tracking
PostgreSQLPrimary Database
RedisCache & Queue Management
S3File Storage
All client applications communicate via Kong API Gateway. The WebSocket server handles real-time updates for delivery tracking. PostgreSQL stores all business data, Redis is used for session cache and Bull queues.
2
Tech Stack

Backend

Component Version Details
NestJS 10.4.2 @nestjs/core: ^10.4.2
TypeScript 5.3.3 typescript: ^5.3.3, @types/node: ^20.11.5
Prisma 5.8.1 prisma: ^5.8.1, @prisma/client: ^5.8.1
PostgreSQL 15.4 pg: ^8.11.3
Redis 7.2 ioredis: ^5.3.2
Bull 5.12.1 @nestjs/bull: ^10.0.1, bull: ^5.12.1
Socket.IO 4.7.2 @nestjs/websockets: ^10.4.2, socket.io: ^4.7.2
bcrypt 5.1.1 bcrypt: ^5.1.1

Frontend

Component Version Details
React 18.2.0 react: ^18.2.0, react-dom: ^18.2.0
Next.js 14.1.0 next: ^14.1.0
React Native 0.73.2 react-native: ^0.73.2
TailwindCSS 3.4.1 tailwindcss: ^3.4.1
Zustand 4.5.0 zustand: ^4.5.0
MapboxGL 3.1.2 mapbox-gl: ^3.1.2, react-map-gl: ^7.1.7
React Query 5.17.9 @tanstack/react-query: ^5.17.9
Expo 50.0 expo: ^50.0.0
3
Codebase Statistics
TypeScript Files

342 files (.ts/.tsx) total in the repo

Prisma Migrations

47 migrations since project inception

React Components

89 components in shared + features

API Endpoints

64 endpoints (REST + WebSocket)

Backend Modules

Module Description
ordersOrder management, status flow, and delivery assignment
routingRoute optimization and driver allocation via Mapbox API
restaurantsRestaurant registry, menus, and opening hours
paymentsStripe integration, invoicing, and refunds
authJWT authentication, role management (RBAC), and sessions
notificationsPush notifications (FCM/APNs), email via SendGrid
trackingReal-time driver tracking via WebSocket
analyticsDelivery statistics, KPI calculation, and report generation

Frontend Modules

Module Description
dashboardRestaurant overview with order status and revenue charts
order-flowOrder view with step-by-step status and timeline
map-viewMapbox map view with real-time driver positions
driver-appReact Native view for delivery assignments and navigation
restaurant-portalMenu management, opening hours, and order confirmation
adminBackoffice for customer management and system configuration
analytics-uiReports and KPI dashboard with filtering functionality
auth-uiLogin, registration, and password reset
sharedShared UI components, hooks, and utilities
notifications-uiNotification center and push notification settings
settingsUser profile, company settings, and API keys
4
Security Review
Control Status Details
JWT authentication OK Access + refresh tokens, 15 min expiry, bcrypt password hashing
HTTPS/TLS OK TLS 1.3 via Cloudflare, HSTS headers configured
Stripe PCI-DSS OK Card data handled entirely by Stripe, no PANs stored locally
RBAC OK Role-based access control: admin, restaurant_owner, driver, customer
Rate limiting Not active Missing at API level; Kong configuration exists but is not enabled
PII handling Not active Personal data (name, address, phone) logged in plaintext in application logs
Security tests Not active No automated security tests, no OWASP scanning in pipeline
API key rotation Deferred Third-party keys (Mapbox, SendGrid, Stripe) lack rotation schedule
Security Issues

PII in logs: Personal data such as customer names, delivery addresses, and phone numbers are written in plaintext to application logs stored in CloudWatch. This violates GDPR Article 32 and should be remediated immediately through masking or pseudonymization.

Rate limiting: Lack of rate limiting on public API endpoints exposes the platform to brute force attacks against login and abuse of the order flow. Configuration exists in Kong but is not enabled in production.

5
CI/CD Pipeline
Component Status Details
GitHub Actions CI Incomplete Lint (ESLint) + typecheck (tsc) + build configured. No tests run in pipeline.
Dockerfile Functional Multi-stage Dockerfile exists for backend. Images not pinned to specific version.
Deploy Incomplete Manual deploy via SSH to production server. No automatic rollback mechanism.
Recently added
Staging Missing No staging environment configured. All testing done locally or directly in production.
Deployment Pipeline
Backend Pipeline
git push
Lint + Typecheck
Docker Build
SSH Deploy
Frontend Pipeline
git push
Lint + Typecheck
Next.js Build
Vercel Deploy
1
Business Flows

FoodRunner AB connects restaurants and couriers in real time through a centralized B2B SaaS platform for food delivery logistics. The system handles the entire chain from order to delivery with automated route optimization and integrated payment flows.

User Flows
1. Order
Customer orders
Restaurant confirms
Route optimized
2. Delivery
Courier assigned
Delivery tracked
Customer receives
3. Key Metrics
28 min delivery time
285 SEK avg order
214 restaurants
89 couriers
4. Completion
Payment split
Receipt sent
Review collected
2
Key Features

Order Management

Centralized order view

All orders from connected restaurants are collected in a centralized view with real-time status. Each order is tracked through the entire lifecycle from received to delivered.

Real-time order tracking

Map-based tracking of active deliveries via WebSocket connection. Courier position updates every five seconds with sub-second latency.

Feature Overview

Feature Status Description
Order flow Stable Complete handling from order to delivery confirmation
GPS tracking Stable Real-time positioning of couriers with map integration
Payments Stable Stripe Connect with automatic split between parties
Menu management Beta Restaurant menu updates with image handling and pricing
Analytics tools Alpha Dashboard with delivery statistics, revenue reports, and trend analysis
Push notifications Stable Status updates to customer, restaurant, and courier in real time

Route optimization with graph algorithms

The platform uses graph-based algorithms to optimize delivery routes in real time based on geographic position, traffic data, and restaurant preparation time:

  • Shortest path: Dijkstra-based route calculation with weighted edges for traffic conditions and road type
  • Cluster optimization: Grouping of nearby deliveries to minimize total driving distance per courier
  • Dynamic replanning: Automatic recalculation upon new orders, traffic disruptions, or courier changes during active delivery

Payment via Stripe Connect

Split payments

Stripe Connect handles automatic splitting of each transaction: the platform takes 15%, the restaurant receives 75%, and the courier is allocated 10% of the order amount.

Payout cycle

Restaurants receive daily payouts via Stripe. Couriers receive weekly summaries with detailed transaction history per delivery.

3
User Roles and Permissions

The platform uses a hierarchical role system with five defined roles. Each role has specific permissions that control access to features and data.

Role Hierarchy

Level 1
System Admin
Level 2
Restaurant Owner
Operations Manager
Level 3
Kitchen Manager
Courier / Driver

Permission Types

Permission Description Module
order:read View orders and delivery status order.service.ts
order:manage Create, update, and cancel orders order.service.ts
menu:write Edit menu content and pricing menu.service.ts
delivery:assign Assign and reassign couriers to deliveries delivery.service.ts
platform:admin Full system access including user management admin.service.ts

Row-level Security

Data protection is implemented through row-level security in PostgreSQL where each restaurant only sees its own order and transaction data. Couriers have access limited to active and historical deliveries linked to their account.

  • Automatic filtering: Restaurant owners only see orders linked to their own restaurant ID
  • Audit logging: All data access attempts are logged with timestamp, user ID, and resource path
  • Role-based: System admin has full platform access while other roles are limited to their organization's data
1
Onboarding Estimate

FoodRunner has a relatively streamlined onboarding thanks to a Docker Compose-based local development environment and an existing README. A new developer can quickly run the entire platform locally.

Working local setup

The project has a well-written README with step-by-step instructions and a complete Docker Compose setup that starts the API, database (PostgreSQL), Redis, and a seed service. A new developer can clone the repo and run docker-compose up to have a working environment including seed data for test restaurants and routes.

Time to local env

Clone repo + docker-compose up + seed data

Quick setup
Documentation

Internal wiki for architecture and flows

Notion wiki
Dev Environment

API, PostgreSQL, Redis, seed data

Docker Compose
Code Review

All changes via pull requests

PR-based review

Recommended onboarding sequence

  • Step 1: Clone the repo, run docker-compose up, verify that the API responds and that the frontend loads against the local backend
  • Step 2: Run the seed script for test data with restaurants, menus, and delivery routes in the Stockholm area
  • Step 3: Review the Notion wiki focusing on system architecture, database schema, and the route optimization flow
  • Step 4: Pair programming with CTO or Senior Backend on a scoped feature or bug fix
  • Step 5: Independent implementation of a smaller task with PR review from the team

Identified onboarding gaps

Areas for improvement

Despite a working local setup, there are gaps that delay effective onboarding:

  • Lacks integration tests showing how modules connect, making it difficult for new developers to understand side effects
  • The Notion wiki is partially outdated, especially regarding the route optimization algorithm and the delivery API's webhook flow
  • No formal code standard or linting configuration documented, leading to inconsistent code style between developers
  • Absence of ADRs (Architecture Decision Records) makes it unclear why certain technical choices were made
2
Current Team

FoodRunner currently has a compact development team of 4 people. The team has built the entire platform from scratch and possesses deep domain knowledge, but the size means limited redundancy and high key-person dependency.

Team Composition

Role Count Area of Expertise
CTO / Founder 1 Fullstack development, system architecture, route optimization. Sole owner of the core algorithm.
Senior Backend 1 NestJS, database design, optimization algorithms. Works closely with CTO on backend logic.
Frontend Lead 1 React (web dashboard), React Native (driver app). Responsible for all client interfaces.
Junior Fullstack 1 Started September 2025. Works cross-functionally with support from other team members.
Key-person Risk

The CTO constitutes a single point of failure for the route optimization algorithm, which is FoodRunner's most business-critical component. The algorithm lacks complete documentation and there is no other team member who can independently develop or debug it. In the event of departure, significant risk to delivery capability arises.

3
Recruitment Needs

Priority Hires

Role Priority Justification
Senior Backend High Offload CTO, build redundancy around the route algorithm, and scale backend for more cities.
DevOps / SRE High Establish CI/CD pipeline, infrastructure automation, and monitoring strategy. Operations currently handled ad hoc by CTO.
Mobile Developer Medium Support Frontend Lead with the React Native app growing in complexity with real-time tracking.
QA Engineer Medium Introduce structured testing, automated E2E tests, and quality assurance ahead of scaling.

Recommendation

Hire a Senior Backend developer and a DevOps/SRE engineer. Senior Backend reduces key-person risk around the route algorithm and enables parallel feature development. DevOps/SRE professionalizes the deployment process and creates conditions for reliable operations under increased traffic. These two roles are critical for FoodRunner to scale without degrading quality or delivery pace.

1
Risk Register
ID Severity Finding Impact, Consequences & Verification
R1 High Rate limiting not applied Dependency exists in package.json but is never imported or used. Consequence: Auth endpoints lack protection against brute-force attacks and automated login attempts. Attackers can test password combinations without restriction, creating direct risk of account takeover.
R2 High Eager loading in services The service layer loads all entities + relations for each object. Consequence: For large datasets (10,000+ records) this results in 3-5 second load times. Users experience a "sluggish" interface, leading to frustration. High server load times can cause timeouts during peak traffic.
R3 High Personal data logged in plaintext Sensitive user data (email, IP addresses, usernames) written directly to application logs without masking. Consequence: Violates GDPR Articles 5 and 32 regarding data minimization and security measures. In case of log leakage, personal data is exposed.
R4 High No backup verification Daily PostgreSQL dumps run via cron but have never been tested through restore. No automated verification that the backup is complete or consistent. Consequence: In case of data loss (corrupt disk, failed migration) there is no guarantee that recovery works. Potentially total loss of order and customer data.
R5 High CTO single point of failure All infrastructure, deploy processes, and architecture decisions rest on one person. No documentation of system architecture or operational procedures. Consequence: If CTO becomes unavailable, the team lacks ability to debug production, perform releases, or handle incidents. Critical key-person risk directly affecting business continuity.
R6 Moderate Stripe API keys without rotation The same Stripe secret key has been used since project inception (18+ months). The key is hardcoded in environment configuration rather than managed in a secrets manager. Consequence: A compromised key gives full access to payment flows. Without rotation, the exposure window increases in case of leakage.
R7 Moderate 12% test coverage Only 12% of the codebase is covered by automated tests. Existing tests focus on helper functions, not business logic such as route optimization or price calculation. Consequence: Refactoring and new features risk regressions that are not caught before deploy. Increases cost and time for each release cycle.
R8 Moderate Manual deploy via SSH Releases are done through manual SSH to production server, git pull, and pm2 restart. No CI/CD pipeline, no automated tests during deploy. Consequence: Risk of human error during deploy, no rollback mechanism, and lack of audit trail. Downtime from failed releases requires manual debugging.
R9 Moderate WebSocket without authentication Real-time updates for delivery tracking sent via WebSocket without token validation on connection. Anyone who knows the endpoint can listen to events. Consequence: Unauthorized parties can follow delivery positions in real time. Potential privacy issue for restaurants and customers.
R10 Moderate No monitoring/alerting The application lacks centralized logging, APM, and alerting. Errors are only discovered when customers report problems. No visibility into response times, error rates, or resource usage. Consequence: Outages can persist for hours without the team being notified. Impossible to proactively identify performance issues or capacity needs.
R11 Moderate N+1 queries in order listing The order overview generates a separate database query per order to fetch restaurant and driver details. With 500 active orders, this results in 1,500+ queries per page load. Consequence: Response times of 3-8 seconds during lunch peaks. Database load creating bottlenecks for the entire system.
R12 Moderate Docker images not pinned Dockerfiles use :latest tags for base images (node:latest, nginx:latest). No lockfiles for exact versions. Consequence: Builds are non-reproducible. An upstream update can break the build or introduce incompatibilities without warning.
R13 Moderate No staging environment All changes are deployed directly to production. No separate staging or QA environment for verification before release. Consequence: Bugs and regressions are only discovered in production by end users. Combined with lack of CI/CD, the risk of outages increases significantly.
R14 Low Outdated README The project's README describes an older architecture and lacks current installation instructions, environment variables, and API documentation. Consequence: New developers need extensive onboarding with direct guidance from CTO. Reinforces the single-point-of-failure issue (R5).
R15 Low No code standard The project lacks ESLint configuration, Prettier, and defined code conventions. Inconsistent naming and formatting in the codebase. Consequence: Code review takes longer and focuses on style instead of logic. Increases technical debt over time and complicates onboarding.
R16 Low Bundle size 2.1 MB The frontend bundle is 2.1 MB (gzipped 680 KB) due to unused libraries, non-tree-shaken dependencies, and embedded map images. Consequence: Load time on 3G connection exceeds 4 seconds. Primarily affects drivers in the field with limited connectivity.
R17 Low No a11y audit No accessibility audit has been conducted. Contrast, keyboard navigation, and screen reader support are not verified. Consequence: Risk of not meeting the EU Accessibility Directive (2025). Limits potential customer base and may entail legal exposure in public procurement.
2
Priority Remediations

Critical Priority - Security & Data Integrity

Action Risk Description
Implement rate limiting R1 Enable express-rate-limit on auth endpoints (login, register, reset-password) with max 5 attempts per minute per IP. Dependency already exists in package.json.
Mask PII in logs R3 Introduce log sanitization that masks email, phone numbers, and IP addresses before writing to application logs. Ensure GDPR compliance with Articles 5 and 32.
Verify backup restore R4 Automate weekly restore test of PostgreSQL backup to a separate instance. Validate database integrity and verify row counts against production.

High Priority - Performance & Scalability

Action Risk Description
Authenticate WebSocket R9 Require JWT token during WebSocket handshake for delivery tracking. Validate token server-side and terminate connection on invalid/expired token.
CI/CD pipeline + staging R8, R13 Set up GitHub Actions with automated tests, Docker build, and deploy to staging environment. Production deploy via approved PR, not manual SSH.
Monitoring & alerting R10 Introduce centralized logging (e.g. Datadog or Grafana+Loki), APM for response times and error rates, and PagerDuty alerting for anomalies and outages.
Key rotation for Stripe R6 Migrate API keys to secrets manager (AWS SSM / Vault). Introduce quarterly rotation and revoke existing keys shared via Slack.

Medium Priority - Infrastructure & Quality

Action Risk Description
Test coverage to 60% R7 Prioritize integration tests for route optimization, price calculation, and order flow. Introduce coverage gate in CI that blocks PRs below threshold.
Document core algorithms R5, R14 Document route optimization logic, delivery allocation, and price calculation model. Reduce dependency on CTO as sole knowledge holder.
Microservices migration R2 Extract order management and delivery tracking into separate services behind API gateway. Enable horizontal scaling of critical components independently.
Optimize database queries R11 Fix N+1 queries with eager loading/joins in order listing. Add indexes on frequently filtered columns (status, created_at, restaurant_id).

Lower Priority - Optimization & Compliance

Action Risk Description
Bundle optimization R16 Introduce code splitting, tree shaking, and lazy loading of map components. Target: bundle under 500 KB gzipped for faster loading in the field.
Accessibility audit R17 Conduct WCAG 2.1 AA audit of restaurant and driver interfaces. Prioritize contrast, keyboard navigation, and screen reader support ahead of the EU directive.
Onboarding documentation R14, R15 Create developer guide with architecture overview, local setup, code conventions (ESLint/Prettier), and deploy procedures. Goal: new developer quickly productive.

Conclusion

FoodRunner AB has a solid core product with functioning delivery logistics and proven market fit among B2B customers in the Stockholm area. Security risks (R1, R3, R4) are critical but remediable with limited effort. Scalability gaps (R2, R8, R10) represent typical debt in growth-stage companies that should be addressed before the next growth phase. Key-person risk (R5) requires immediate knowledge transfer and documentation. All identified risks are assessed as remediable with the existing team reinforced by one senior backend developer.