Skip to content
Back to Projects// case study

Brarista AI Fitting Tool

An AI-powered size-calculation backend for an e-commerce bra-fitting platform, serving real-time predictions to live Shopify storefronts.

PythonFlaskFastAPIAWS LambdaAPI GatewayReactTypeScript

Background

Brarista is an AI-powered bra-fitting platform that helps e-commerce brands offer personalised size recommendations to their customers. The platform serves multiple lingerie brands simultaneously — each with their own sizing standards, brand colour schemes, and slightly different customer-facing UI — making it a genuinely multi-tenant system with meaningful variation across clients.

The project was delivered through Concept Recall, a software development agency based in Karachi. I joined as a Software Engineer on the Brarista engagement, initially focused on the frontend, and over time became a full-stack contributor across the React component layer, Python backend APIs, and the AWS serverless infrastructure.

My Role

My involvement spanned the full stack across two areas of the product:

  • Frontend engineering — building and maintaining the React + TypeScript fitting tool UI, which shipped as a reusable component library deployed directly into Shopify themes.

  • Backend engineering — stepping in to own API development and AWS infrastructure when the backend team was short-staffed, learning Lambda and API Gateway on the job while keeping feature delivery on schedule.

Beyond feature work, I conducted pull request reviews, onboarded new engineers onto the codebase, and built out the Cypress E2E test suite covering the core user flows.

Technical Architecture

Frontend — React Component Library on Shopify

The fitting tool UI was built in React and TypeScript and packaged as a reusable component library rather than a standalone application. This was a deliberate architectural choice: Brarista needed to embed the tool into multiple Shopify storefronts, each belonging to a different brand. Shipping it as a library meant each storefront could pull in the same core logic while applying its own theme tokens and UI variations without forking the codebase.

The library was adopted by two internal teams at Concept Recall for additional storefronts, which validated the approach — new integrations could be completed significantly faster than if each team had been working from a separate implementation.

Static builds were deployed to AWS S3 and distributed via CloudFront, then injected into the relevant Shopify theme. GitHub Actions handled the CI/CD pipeline, including environment-specific pipelines for staging and production.

Backend — Python APIs and AWS Serverless

The backend was built in Python using Flask and FastAPI, exposing REST endpoints that integrated with an ML model to generate size recommendations. The endpoints handled user measurement inputs, passed them through the model, and returned structured size outputs mapped to the specific brand's sizing table.

The highest-traffic entry points were exposed as AWS Lambda functions behind API Gateway. This was particularly useful for Shopify event ingestion — Shopify webhooks fire in bursts rather than at a steady rate, and Lambda meant the backend scaled to handle those bursts without over-provisioning a standing server. AWS CloudWatch was used to monitor function health, latency, and error rates in production.

Multi-Tenancy

One of the more architecturally interesting aspects of the system was how it handled multiple brands simultaneously. Each brand (tenant) had:

  • Its own sizing table — because bra sizing varies meaningfully between brands

  • A corresponding set of frontend UI variations and colour scheme

  • Slightly different conditional logic on both the frontend and backend to handle brand-specific edge cases

This tenant configuration was maintained in backend mapping tables, and every ML response had to be correctly matched to the requesting tenant before a size recommendation could be returned to the user.

A Problem Worth Describing in Detail

The most complex issue I worked on was a multi-tenant response-routing bug that was causing silent failures for end users — a class of bug that is particularly hard to catch because the system appears to be functioning on the surface.

What was happening

The ML model was returning results to the backend, but the backend's conditional logic for routing those results was not correctly scoped per tenant. Different brands shared overlapping code paths, and under certain conditions, the wrong branch would execute — meaning the result returned to the frontend was either for the wrong tenant's sizing schema, or no result was returned at all.

To make things worse, the frontend had a fallback flow: if no result arrived within a timeout window, it would prompt the user with a "we'll email your results" message. Two problems with that: first, the email-sending logic was never triggered correctly because the backend had already technically responded — it just responded with the wrong result. Second, users who triggered the fallback never received the email. So the failure mode was: user completes the fitting flow, receives a wrong result or a "check your email" message, and the email never arrives. A dead end, with no error surface for the engineering team to easily detect.

How I diagnosed it

Because the bug crossed the frontend/backend boundary, I had to trace the entire response lifecycle — from the user input on the React side, through the API Gateway endpoint, into the Lambda function, through the ML model call, and back out through the tenant-routing logic. I mapped out the conditional branches for each tenant and identified where the routing logic was making incorrect assumptions about which tenant's configuration to apply.

How I fixed it

The fix was targeted rather than a full rewrite. The codebase had many interdependent parts — rewriting the routing layer from scratch carried a real risk of breaking unrelated tenant configurations that were working correctly. Instead, I corrected the specific conditional logic responsible for tenant identification in the ML response handler, and separately fixed the email-fallback trigger so it only fired when a result had genuinely not been produced, not when a result existed but was malformed.

Working across frontend and backend simultaneously was necessary here: the fix required understanding both what the backend was sending and what the frontend was interpreting, since the bug lived in the gap between the two.

Testing

I built out the Cypress E2E test suite covering the core user journeys: the fitting flow from measurement input through to size recommendation, the per-tenant UI variations, and the fallback behaviour. Having these tests in place was directly relevant to catching regressions — after the routing bug fix, the tests confirmed that all previously-working tenant configurations continued to behave correctly.

What I Took Away From This Project

Brarista was the project that shifted how I think about multi-tenancy. The surface-level implementation — different configs per client — is straightforward. The harder part is maintaining correctness guarantees as the number of tenants grows, especially when conditional logic branches accumulate over time and start to interact in ways the original design didn't anticipate. Building defensively against that kind of drift, and having test coverage that reflects the real per-tenant variation rather than a single generic happy path, is something I now treat as a first-order concern rather than a nice-to-have.

Stepping into backend ownership from a frontend-first role on a live production system was also a meaningful experience. It required being honest about what I didn't yet know (Lambda, API Gateway), learning quickly from real documentation rather than tutorials, and being careful not to break things that were already working while getting up to speed.