Skip to main content
All projects

Case study

Role
Software Developer
Organisation
Greggs PLC

Greggs Kiosk

The customer-facing self-service screen in Greggs shops, running on Linux. Customers browse the full menu, build and customise an order, see allergen and nutrition information, pay, and collect against an order number. It was a flagship project on the Greggs 2025 roadmap.

Greggs Kiosk, Greggs PLC

01 / Overview

What I did.

I joined as one of the first developers and shaped the early prototype that the customer-facing features were built on, including the meal deal builder and the upselling flow. I later led development of reusable order-sharing, loyalty, promotions and analytics packages, published internally and now consumed by the POS and drive-through teams, along with a shared models library that keeps tills and backend services in agreement.

02 / Architecture

Layers listed outermost first.

How it's put together.

A single Flutter application on Linux, deliberately thin: the behaviour that other Greggs surfaces also need lives in versioned packages rather than in the kiosk. The kiosk was first to need loyalty and order sharing, but it was not going to be the only one, so those were built as libraries from the outset.

  1. Kiosk application

    The screen the customer touches.

    Flutter on Linux, driving the attract screen, menu browsing, customisation, basket, payment and the order-number handoff. Includes an accessibility mode that brings the interactive controls down into the lower half of the screen so they are within reach.

    • Flutter
    • Dart
    • Linux
  2. Shared feature packages

    Written once, consumed by several teams.

    Order sharing, loyalty, promotions and analytics as separately versioned packages published to an internal registry. POS and drive-through consume the same builds, so a fix to loyalty is a version bump rather than three separate patches.

    • Dart packages
    • OnePub
  3. Shared models library

    One definition of an order.

    The contract between kiosk, tills and backend services lives in one library both sides depend on, so the shape of an order cannot quietly diverge between the thing that creates it and the thing that fulfils it.

    • Dart
    • .NET
  4. Backend services

    Menu, basket, allergens, payment.

    The .NET services behind the screen. Requests go out through a single HTTP layer so timeouts, retries and error surfacing are handled in one place rather than per feature.

    • .NET
    • Dio
  5. Local test doubles

    Frontend work that does not wait on an environment.

    Allergen data served from Wiremock behind NGINX locally, so interface work could continue against a stable, known response set instead of a shared upstream environment.

    • Wiremock
    • NGINX
    • Postman
  6. Build & device delivery

    Getting it onto the hardware.

    Containerised builds through Azure DevOps, deployed to Linux kiosk hardware in shops.

    • Docker
    • Azure DevOps
    • SSH

03 / Decisions

Each of these had a credible alternative. The trade-off is stated, not hidden.

Choices, and what they cost.

  1. Shared behaviour as versioned packages, not copied code

    Chose
    Internal Dart packages on a private registry, consumed by kiosk, POS and drive-through
    Instead of
    • Each team implementing loyalty and order sharing themselves
    • A shared source folder copied between repositories
    Why
    Loyalty and order sharing are rules, not screens, and rules that exist in three codebases will be subtly different in three codebases within a year. A published, versioned package means one implementation and one place to fix a bug, and it lets each consuming team upgrade on their own schedule instead of being blocked by ours.
    Trade-off
    It demands release discipline. A breaking change now needs a major version and a migration path for teams you do not control, which is slower than editing your own code.
  2. A shared models library across frontend and backend

    Chose
    One library defining the order contract, depended on by both sides
    Instead of
    • Each application declaring its own DTOs
    • Generating clients from a schema per consumer
    Why
    The expensive bug in a system with tills, kiosks and a backend is not a broken screen. It is two components that disagree about what an order contains and only find out in a live shop. A single shared definition makes that disagreement a compile error.
    Trade-off
    It couples release trains together. Changing the model means coordinating a release across components that would otherwise ship independently.
  3. Flutter for the kiosk surface

    Chose
    One Flutter codebase targeting the Linux kiosk hardware
    Instead of
    • A native Linux application
    • A web application in a kiosk-mode browser
    Why
    The interface is highly custom and animation-heavy, and Flutter renders its own widgets rather than inheriting platform ones, so the design is identical on every unit and can be reused on other surfaces later. It also let one team cover the whole front end rather than splitting by platform.
    Trade-off
    Anything genuinely device-specific (payment terminals, receipt printers, kiosk peripherals) needs a platform channel, which is more work than calling a native API directly.
  4. Mocking allergen data locally rather than pointing at the real service

    Chose
    Wiremock behind NGINX serving allergen responses on the developer machine
    Instead of
    • Consuming the live upstream service during development
    • Hard-coded fixtures inside the application
    Why
    Allergen data is exactly the kind of dependency that blocks a team: shared, slow-moving, and something you must not get wrong. A local mock gives every developer the same known responses, including the awkward ones, without competing for a shared environment, and unlike in-app fixtures it exercises the real HTTP path.
    Trade-off
    A mock drifts from the real contract unless somebody deliberately refreshes it, and a mock that is out of date is worse than no mock at all.

04 / Stack

Grouped by the job they do rather than listed as a keyword run.

Technologies.

Application
  • Flutter
  • Dart
Services
  • .NET
  • C#
  • Dio
Platform
  • Linux
  • Docker
  • SSH
Delivery
  • Azure DevOps
Tooling
  • Wiremock
  • NGINX
  • Postman
  • OnePub
  • Cocoapods

06 / Outcome

Where it landed.

The kiosk launched into live shops, reaching three locations in the first week with two to six units each, and the rollout continued from there on feedback from customers, shop teams and stakeholders. The packages I led (order sharing, loyalty, promotions and analytics) were adopted by the POS and drive-through teams, so the work outlived the surface it was written for.