
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.
API GatewayFastAPIFlaskPythonReactTypeScriptAWS LambaAWS S3CloudFrontCloudWatchGitHub ActionsShopifyFull-stack EngineeringCypress E2E testingMulti-tenant architecture
Background
Brarista is an AI-powered bra-fitting platform that helps e-commerce brands offer personalised size recommendations to their customers. The platform served three lingerie brands simultaneously — including Womanhood and Wicked — each with its own sizing standards, brand colour scheme, and customer-facing UI.
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
Packaging the fitting tool as a reusable component library, rather than a standalone application, was a deliberate architectural choice: Brarista needed to embed it into three separate 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.
Before the library existed, integrating the tool into a new storefront meant building the frontend locally, manually picking out the relevant files from that build, uploading them into the Shopify theme's file system by hand, and wiring up a button in the theme that opened the tool. Packaging it as a versioned library replaced that manual file-picking and wiring with a standard import, which is what let two internal teams reuse it for additional storefronts without repeating that process each time.
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: Shopify webhooks fire in bursts rather than at a steady rate, and Lambda let the backend scale to handle those bursts without over-provisioning a standing server. AWS CloudWatch monitored function health, latency, and error rates in production.
Multi-Tenancy
The harder architectural problem was handling brand-specific variation without duplicating code paths for each tenant. Each brand (tenant) had:
Its own sizing table, since sizing conventions differ from brand to brand
Its own frontend UI variations and colour scheme
Slightly different conditional logic on both 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 caused silent failures for end users. It was hard to catch precisely because the system kept responding — nothing on the surface looked broken.
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 six-minute 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 team to 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, and rewriting the routing layer from scratch carried a real risk of breaking unrelated tenant configurations that were already 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.
Testing
The Cypress suite covered three journeys: the fitting flow from measurement input through to size recommendation, the per-tenant UI variations, and the fallback behaviour. These tests were directly relevant to catching regressions — after the routing bug fix, they 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 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 meant 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 what was already working while getting up to speed.