[{"data":1,"prerenderedAt":1227},["ShallowReactive",2],{"work-all":3},[4,120,208,321,411,505,575,665,740,798,900,954,1092],{"id":5,"title":6,"anonymised":7,"architecture":8,"body":30,"client":90,"description":85,"extension":91,"featured":92,"headline":93,"industry":94,"meta":95,"metrics":96,"navigation":92,"order":106,"path":107,"practice":108,"role":109,"screens":110,"seo":111,"stack":112,"status":116,"stem":117,"url":116,"year":118,"__hash__":119},"work\u002Fwork\u002Fstarbucks.md","Starbucks' first Shopify store",false,[9,15,20,24],{"name":10,"items":11},"Storefront",[12,13,14],"Liquid foundation","Rewards experience","Account",{"name":16,"items":17},"Commerce",[18,19],"Shopify","Shopify Subscriptions",{"name":21,"items":22},"Apps",[23],"Custom rewards login app",{"name":25,"items":26},"Integrations",[27,28,29],"Third-party rewards system","External identity provider","Vendor integrations",{"type":31,"value":32,"toc":84},"minimark",[33,38,42,46,49],[34,35,37],"h2",{"id":36},"the-brief","The brief",[39,40,41],"p",{},"Starbucks was building on Shopify for the first time, with Inviqa as delivery partner. The team had no in-house Shopify experience, and the store needed to work with a third-party rewards system and offer coffee subscriptions.",[34,43,45],{"id":44},"what-we-did","What we did",[39,47,48],{},"Mark joined as Principal Tech Lead, owning the technical architecture and acting as the team's Shopify lead.",[50,51,52,60,66,72,78],"ul",{},[53,54,55,59],"li",{},[56,57,58],"strong",{},"Architecture and integration strategy."," Worked with Inviqa's CTO, product managers and the client's leadership team to scope requirements and define the integration approach for each third-party vendor.",[53,61,62,65],{},[56,63,64],{},"Design feasibility."," Reviewed design proposals against what Shopify can and can't do, so issues were found before build rather than during it.",[53,67,68,71],{},[56,69,70],{},"Foundations."," Built the initial project foundation in Shopify Liquid and helped the dev team get up to speed on the platform.",[53,73,74,77],{},[56,75,76],{},"Rewards."," Integrated a third-party rewards system into the storefront and built a custom rewards login app, coordinating with the external provider.",[53,79,80,83],{},[56,81,82],{},"Subscriptions."," Implemented a coffee subscription service using Shopify Subscriptions.",{"title":85,"searchDepth":86,"depth":86,"links":87},"",2,[88,89],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Starbucks","md",true,"Technical architecture and Shopify leadership for the brand's first Shopify ecommerce build, including rewards and coffee subscriptions.","Food & Drink",{},[97,100,103],{"value":98,"label":99},"1st","Shopify build for the brand",{"value":101,"label":102},"Rewards","Third-party integration and custom login app",{"value":104,"label":105},"Subscriptions","Coffee subscriptions on Shopify Subscriptions",1,"\u002Fwork\u002Fstarbucks","commerce","Principal Tech Lead, via Inviqa (Havas)",[],{"title":6,"description":85},[18,113,19,114,115],"Liquid","Shopify Apps","TypeScript",null,"work\u002Fstarbucks","Dec 2024 – Nov 2025","PBkbt98CovAiwngqBXLS99qzUQeiaLyVZBZC2PENmTE",{"id":121,"title":122,"anonymised":7,"architecture":123,"body":143,"client":185,"description":85,"extension":91,"featured":92,"headline":186,"industry":187,"meta":188,"metrics":189,"navigation":92,"order":86,"path":199,"practice":108,"role":200,"screens":201,"seo":202,"stack":203,"status":116,"stem":205,"url":116,"year":206,"__hash__":207},"work\u002Fwork\u002Fgood-beams.md","In-store and online retail for Good Beams",[124,129,133,138],{"name":125,"items":126},"Channels",[127,128],"Online store","Physical store",{"name":16,"items":130},[18,131,132],"Supplements","drinks and merch",{"name":134,"items":135},"Services",[136,137],"Custom Shopify app","Backend middleware",{"name":139,"items":140},"Clinical",[141,142],"Prescription portal (Script Assist)","Consultations",{"type":31,"value":144,"toc":181},[145,147,150,152,155],[34,146,37],{"id":36},[39,148,149],{},"Good Beams is building a lifestyle brand for a UK pharmacy, aiming to normalise medical cannabis while meeting full regulatory compliance. Alongside a physical store, the brand needed an online shop for supplements, drinks and merchandise, and a clear path into a separate, regulated prescription journey.",[34,151,45],{"id":44},[39,153,154],{},"Working with Midnight, Stack Studio led the technical delivery.",[50,156,157,163,169,175],{},[53,158,159,162],{},[56,160,161],{},"Technical discovery."," Mapped the customer journey from landing page and eligibility check through to consultation and ongoing purchases, and set out which parts belonged in Shopify and which didn't.",[53,164,165,168],{},[56,166,167],{},"Keeping the flows apart."," Shopify handles retail products only; prescriptions run through a separate portal. That keeps the store within Shopify Payments' rules and keeps the regulated side where it belongs.",[53,170,171,174],{},[56,172,173],{},"Custom app and middleware."," Built a custom Shopify app and backend middleware so the store, the physical shop and the systems behind them work as one experience.",[53,176,177,180],{},[56,178,179],{},"In-store and online."," Planned the build so the physical store and the website share the same products and customers.",{"title":85,"searchDepth":86,"depth":86,"links":182},[183,184],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Good Beams","Technical delivery for a medicinal cannabis brand's Shopify build, with a custom app and backend middleware connecting the shop floor and the website.","Health & Wellbeing",{},[190,193,196],{"value":191,"label":192},"2","Channels: physical store and online",{"value":194,"label":195},"Custom","Shopify app and backend middleware",{"value":197,"label":198},"Separate","Retail and prescription journeys kept apart","\u002Fwork\u002Fgood-beams","Technical delivery, via Midnight",[],{"title":122,"description":85},[18,136,137,204],"Script Assist","work\u002Fgood-beams","2025 – 2026","dJZ-RoWrgfqSJZPUWnK2_TiS8B308Yb1aZW_OwZLUSE",{"id":209,"title":210,"anonymised":7,"architecture":211,"body":240,"client":297,"description":85,"extension":91,"featured":92,"headline":298,"industry":299,"meta":300,"metrics":301,"navigation":92,"order":311,"path":312,"practice":108,"role":313,"screens":314,"seo":315,"stack":316,"status":116,"stem":318,"url":116,"year":319,"__hash__":320},"work\u002Fwork\u002Fhebtroco.md","Moving HebTroCo from WooCommerce to Shopify",[212,217,221,227,234],{"name":10,"items":213},[214,215,216],"Premium theme","Pre-order messaging","Delivery dates in basket",{"name":16,"items":218},[18,219,220],"Shopify POS Pro","Click & collect",{"name":222,"items":223},"Post-purchase",[224,225,226],"Loop Returns","Store credit","Exchanges",{"name":228,"items":229},"Operations",[230,231,232,233],"ShipStation","Metorik","Zendesk","Xero",{"name":235,"items":236},"Migration",[237,238,239],"Matrixify imports","301 redirects","Customer re-activation",{"type":31,"value":241,"toc":293},[242,244,247,249],[34,243,37],{"id":36},[39,245,246],{},"HebTroCo, a British clothing brand, had outgrown WooCommerce. A lot of its range is made to order, so the new store needed to handle pre-orders properly, alongside returns, point of sale and the tools the team already relies on.",[34,248,45],{"id":44},[50,250,251,257,263,269,275,281,287],{},[53,252,253,256],{},[56,254,255],{},"Discovery and data audit."," Reviewed the WooCommerce site and data, checked POS hardware compatibility and mapped every integration before building anything.",[53,258,259,262],{},[56,260,261],{},"Data migration."," Products, variants, categories, customers and recent orders moved across with Matrixify, including page-builder content rebuilt as Shopify metaobjects.",[53,264,265,268],{},[56,266,267],{},"Pre-purchase flow."," Variant-level pre-orders across three stock scenarios, production and delivery dates shown in the basket, and pre-order items kept separate from in-stock items at checkout.",[53,270,271,274],{},[56,272,273],{},"Returns with Loop."," A returns and exchange portal that can refund to store credit, so more returns turn into new orders.",[53,276,277,280],{},[56,278,279],{},"Operations connected."," ShipStation for fulfilment, Metorik for reporting, Zendesk for customer service and Xero for accounts.",[53,282,283,286],{},[56,284,285],{},"Point of sale."," Shopify POS Pro set up on the existing hardware, with staff training.",[53,288,289,292],{},[56,290,291],{},"A careful launch."," 301 redirects, a launch-day runbook with a 14-point go \u002F no-go gate, real-money smoke tests and a rollback plan.",{"title":85,"searchDepth":86,"depth":86,"links":294},[295,296],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"HebTroCo","A full migration from WooCommerce to Shopify, with pre-orders, returns and the brand's operations tools connected from day one.","Fashion, British-made clothing",{},[302,305,308],{"value":303,"label":304},"3","Pre-order stock scenarios handled at variant level",{"value":306,"label":307},"5","Operations tools connected: Loop, Metorik, ShipStation, Zendesk, Xero",{"value":309,"label":310},"14-point","Go \u002F no-go gate before launch",3,"\u002Fwork\u002Fhebtroco","Lead, Stack Studio",[],{"title":210,"description":85},[18,219,224,231,230,232,233,317],"Matrixify","work\u002Fhebtroco","2026","eceJb2c5nnFBWevHLrKrJi-zJz_NZ-enzcYNP_i3YwA",{"id":322,"title":323,"anonymised":7,"architecture":324,"body":343,"client":385,"description":85,"extension":91,"featured":92,"headline":386,"industry":387,"meta":388,"metrics":389,"navigation":92,"order":398,"path":399,"practice":108,"role":400,"screens":401,"seo":402,"stack":403,"status":116,"stem":407,"url":408,"year":409,"__hash__":410},"work\u002Fwork\u002Fbrimm.md","Membership, store credit and a Planet Fund for BRiMM",[325,330,335,339],{"name":10,"items":326},[327,328,329],"Membership tiers","Credit balance","Planet Fund messaging",{"name":331,"items":332},"Automation",[333,334],"Shopify Flow","Regios Discounts",{"name":21,"items":336},[337,338],"Store Credit app","Planet Fund app",{"name":340,"items":341},"Marketplace",[342],"ShipTurtle multi-vendor",{"type":31,"value":344,"toc":381},[345,347,350,352,355],[34,346,37],{"id":36},[39,348,349],{},"BRiMM is an environmental marketplace working with ethical brands, helping shoppers change how they buy. The store needed to connect multiple vendors, reward loyal members and show the impact of each order.",[34,351,45],{"id":44},[39,353,354],{},"Mark was responsible for the overall project and tech stack.",[50,356,357,363,369,375],{},[53,358,359,362],{},[56,360,361],{},"A pragmatic marketplace decision."," After trying a custom vendor integration, we moved to ShipTurtle for the multi-vendor layer. That freed the budget for the features that make BRiMM different.",[53,364,365,368],{},[56,366,367],{},"Tiered membership."," Built with Shopify Flow and Regios Discounts, so offers respond to what's in the cart and the team can adjust rules themselves.",[53,370,371,374],{},[56,372,373],{},"Store Credit app."," A custom Shopify app that accrues store credit, giving members in certain tiers extra benefits on repeat purchases.",[53,376,377,380],{},[56,378,379],{},"Planet Fund app."," Takes a set percentage of each order and contributes it to Planet Fund operations, helping brands build more sustainable businesses.",{"title":85,"searchDepth":86,"depth":86,"links":382},[383,384],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"BRiMM","A Shopify marketplace for ethical brands, with tiered membership and two custom apps for store credit and a sustainability fund.","Sustainable marketplace",{},[390,392,395],{"value":303,"label":391},"Core features: membership, store credit, Planet Fund",{"value":393,"label":394},"% \u002F order","Contributed to Planet Fund operations",{"value":396,"label":397},"Buy, not build","Marketplace layer via ShipTurtle",4,"\u002Fwork\u002Fbrimm","Tech Lead",[],{"title":323,"description":85},[18,114,333,334,404,405,406],"ShipTurtle","Node.js","GCP","work\u002Fbrimm","https:\u002F\u002Fwearebrimm.com","Feb – Dec 2025","epSQ8ULphyqZqq_dPMkGEnuXpueQqUFK9pFwNbC5WB0",{"id":412,"title":413,"anonymised":7,"architecture":414,"body":437,"client":478,"description":85,"extension":91,"featured":92,"headline":479,"industry":480,"meta":481,"metrics":482,"navigation":92,"order":491,"path":492,"practice":108,"role":493,"screens":494,"seo":495,"stack":496,"status":116,"stem":502,"url":116,"year":503,"__hash__":504},"work\u002Fwork\u002Fdbco.md","Native Shopify loyalty for premium fashion brands",[415,422,428,434],{"name":10,"items":416},[417,418,419,420,421],"Points dashboard","VIP tiers","Referrals","Ways to earn","Rewards carousel",{"name":423,"items":424},"Edge",[425,426,427],"Shopify App Proxy","HMAC verification","Endpoint allow-list",{"name":429,"items":430},"Runtime",[431,432,433],"Remix v2 + Prisma","Express","Cloudflare Worker",{"name":134,"items":435},[436],"Yotpo Loyalty & Referrals API v2",{"type":31,"value":438,"toc":473},[439,441,444,446,466,470],[34,440,37],{"id":36},[39,442,443],{},"DBCO is a Shopify-focused agency working with premium fashion and lifestyle brands. A client's loyalty programme ran on Yotpo's embedded widgets, which limited the design and UX the brand wanted.",[34,445,45],{"id":44},[50,447,448,454,460],{},[53,449,450,453],{},[56,451,452],{},"Loyalty as native sections."," Built the programme as 12 Shopify Liquid sections: points dashboard, four VIP tiers, a referral programme with social sharing, ways to earn, a redeemable product carousel, FAQs and member-only content. Customer data renders server-side and the brand controls the design.",[53,455,456,459],{},[56,457,458],{},"A secure App Proxy."," A Remix v2 and Prisma app proxies every Yotpo API call, with HMAC signature verification, endpoint allow-listing and server-side API keys.",[53,461,462,465],{},[56,463,464],{},"A reusable pattern."," The proxy also exists as an Express app and a Cloudflare Worker, so it can be deployed wherever suits the project.",[34,467,469],{"id":468},"also-at-dbco","Also at DBCO",[39,471,472],{},"Active development on Mina Baie's Shopify theme — 123 sections, 137 snippets, 65 templates and 32 locales — including a \"Pairs With\" recommendation system, cart-drawer cross-sells and complex collection filtering.",{"title":85,"searchDepth":86,"depth":86,"links":474},[475,476,477],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},{"id":468,"depth":86,"text":469},"DBCO","A Yotpo loyalty and referrals programme rebuilt as native Shopify sections, with a secure App Proxy for the API calls.","Fashion & Lifestyle",{},[483,486,489],{"value":484,"label":485},"12","Bespoke loyalty sections replacing widgets",{"value":487,"label":488},"4","VIP reward tiers",{"value":303,"label":490},"Proxy implementations: Remix, Express, Workers",5,"\u002Fwork\u002Fdbco","Tech Lead, Ecommerce",[],{"title":413,"description":85},[18,113,497,498,499,432,500,501],"App Proxy","Remix","Prisma","Cloudflare Workers","Yotpo API","work\u002Fdbco","Ongoing","LVVzVoNIedbVWDqkAhJc7ni5PucYvtPXa0XjVLKd1aM",{"id":506,"title":507,"anonymised":7,"architecture":508,"body":518,"client":551,"description":85,"extension":91,"featured":92,"headline":552,"industry":553,"meta":554,"metrics":555,"navigation":92,"order":565,"path":566,"practice":108,"role":400,"screens":567,"seo":568,"stack":569,"status":116,"stem":572,"url":573,"year":116,"__hash__":574},"work\u002Fwork\u002Fmyom.md","AI search visibility for MYOM Oat Drinks",[509,513],{"name":10,"items":510},[511,512],"Crawler-visible product data","Incremental theme edits",{"name":514,"items":515},"Insight",[516,517],"AI visibility dashboard","Phrase scoring",{"type":31,"value":519,"toc":547},[520,522,525,527],[34,521,37],{"id":36},[39,523,524],{},"MYOM delivers oat milk drinks in letterbox-sized packs. Their brand was disappearing behind competitors in AI-driven search.",[34,526,45],{"id":44},[50,528,529,535,541],{},[53,530,531,534],{},[56,532,533],{},"Measured it first."," Built a monitoring dashboard so the team could score phrases and words in AI search and see what was and wasn't working.",[53,536,537,540],{},[56,538,539],{},"Small, safe changes."," Made incremental edits to the Shopify store so the right data was visible to AI crawlers.",[53,542,543,546],{},[56,544,545],{},"An ongoing relationship."," MYOM were happy with the outcome and we continue to work together on improving the store.",{"title":85,"searchDepth":86,"depth":86,"links":548},[549,550],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"MYOM","Making a DTC Shopify store visible to AI search, with a dashboard to track which phrases perform.","Food & Drink, DTC",{},[556,559,562],{"value":557,"label":558},"AI","Visibility score improved",{"value":560,"label":561},"1","Monitoring dashboard for AI search phrases",{"value":563,"label":564},"Incremental","Store changes for AI crawlers",6,"\u002Fwork\u002Fmyom",[],{"title":507,"description":85},[18,113,570,571],"Structured data","Monitoring dashboard","work\u002Fmyom","https:\u002F\u002Fmyom.shop","ENJ78yk7r8QTqqnKwGlpYplgaoPW6KKOT4bwyA1FbKA",{"id":576,"title":577,"anonymised":7,"architecture":578,"body":596,"client":637,"description":85,"extension":91,"featured":7,"headline":638,"industry":639,"meta":640,"metrics":641,"navigation":92,"order":651,"path":652,"practice":108,"role":653,"screens":654,"seo":655,"stack":656,"status":116,"stem":661,"url":662,"year":663,"__hash__":664},"work\u002Fwork\u002Fbleach-london.md","Platform build for Bleach London",[579,584,588,592],{"name":580,"items":581},"Frontend",[582,583],"Vue storefront","Customer service tools",{"name":585,"items":586},"Content",[587,18],"Contentful",{"name":589,"items":590},"API",[591],"Rails + GraphQL services",{"name":134,"items":593},[594,595],"Twilio chat","AWS Cognito",{"type":31,"value":597,"toc":632},[598,600,603,605,625,629],[34,599,37],{"id":36},[39,601,602],{},"Bleach London was growing quickly and needed its ecommerce and customer platform to keep up.",[34,604,45],{"id":44},[50,606,607,613,619],{},[53,608,609,612],{},[56,610,611],{},"Ecommerce integration"," across Contentful, Shopify and a Vue front end, sitting on a Ruby on Rails API that exposed data through GraphQL.",[53,614,615,618],{},[56,616,617],{},"Customer service platform."," A chat interface using Twilio and a custom Vue UI for customers and agents.",[53,620,621,624],{},[56,622,623],{},"Authentication."," Rolled out customer authentication with AWS Cognito, keeping the user database separate from the application code.",[34,626,628],{"id":627},"outcome","Outcome",[39,630,631],{},"The team's work supported Bleach London's growth from under £1m to over £4m turnover within 18 months.",{"title":85,"searchDepth":86,"depth":86,"links":633},[634,635,636],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},{"id":627,"depth":86,"text":628},"Bleach London","Shopify, Contentful and Vue on a Rails GraphQL API, plus customer chat and authentication, during a period of fast growth.","Beauty",{},[642,645,648],{"value":643,"label":644},"\u003C£1m → >£4m","Turnover growth in 18 months",{"value":646,"label":647},"Chat","Twilio-based customer service tool",{"value":649,"label":650},"Cognito","Customer authentication rollout",7,"\u002Fwork\u002Fbleach-london","Senior Engineer",[],{"title":577,"description":85},[18,587,657,658,659,660,595],"Vue","Ruby on Rails","GraphQL","Twilio","work\u002Fbleach-london","https:\u002F\u002Fbleachlondon.com","Mar – Dec 2020","bFZWGEPUjhflvOlnYsCmtn6rsForODMQhD2nD5sinUg",{"id":666,"title":667,"anonymised":7,"architecture":668,"body":684,"client":717,"description":85,"extension":91,"featured":7,"headline":718,"industry":719,"meta":720,"metrics":721,"navigation":92,"order":728,"path":729,"practice":108,"role":730,"screens":731,"seo":732,"stack":733,"status":116,"stem":736,"url":737,"year":738,"__hash__":739},"work\u002Fwork\u002Fviaduct.md","Re-platforming Viaduct Furniture",[669,673,676,680],{"name":10,"items":670},[671,672],"Craft templates","Product content",{"name":16,"items":674},[675],"Craft Commerce",{"name":25,"items":677},[678,679],"FedEx API","DPD API",{"name":681,"items":682},"Infra",[683],"AWS",{"type":31,"value":685,"toc":713},[686,688,691,693],[34,687,37],{"id":36},[39,689,690],{},"Magento wasn't working for Viaduct. They needed a new platform without losing SEO, product data or existing customers.",[34,692,45],{"id":44},[50,694,695,701,707],{},[53,696,697,700],{},[56,698,699],{},"Migration strategy."," Moved all content and products to Craft CMS and Craft Commerce, planned around SEO and customer continuity.",[53,702,703,706],{},[56,704,705],{},"Design partnership."," Brought in a design and UX team for the new site and built the back end alongside them.",[53,708,709,712],{},[56,710,711],{},"Long-term care."," Looked after the platform for six years, hiring and onboarding developers as it grew.",{"title":85,"searchDepth":86,"depth":86,"links":714},[715,716],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Viaduct Furniture","A migration from Magento to Craft Commerce, then six years looking after the platform.","Furniture",{},[722,725],{"value":723,"label":724},"6 yrs","Looking after the platform",{"value":726,"label":727},"Magento → Craft","Full content and product migration",8,"\u002Fwork\u002Fviaduct","Lead Developer \u002F CTO",[],{"title":667,"description":85},[734,675,735,659,683,678,679],"Craft CMS","PHP","work\u002Fviaduct","https:\u002F\u002Fwww.viaduct.co.uk","2017 – 2023","1r8UGnBOjwWvBHDfZYbD4J71CKtLsgw20kn6r7e8JYE",{"id":741,"title":742,"anonymised":7,"architecture":743,"body":760,"client":776,"description":85,"extension":91,"featured":7,"headline":777,"industry":778,"meta":779,"metrics":780,"navigation":92,"order":783,"path":784,"practice":785,"role":786,"screens":787,"seo":788,"stack":789,"status":116,"stem":794,"url":795,"year":796,"__hash__":797},"work\u002Fwork\u002Fcubitts.md","Stabilising the Cubitts tech stack",[744,749,753,757],{"name":745,"items":746},"Customer",[747,748],"Website","POS",{"name":228,"items":750},[751,752],"Production","Inventory",{"name":754,"items":755},"Data",[756],"Customer management",{"name":681,"items":758},[683,759],"Docker",{"type":31,"value":761,"toc":772},[762,764,767,769],[34,763,37],{"id":36},[39,765,766],{},"Cubitts' technology spanned five areas, built over time in Python, PHP, Vue, React and AWS. Legacy issues were causing real problems for the business, and the production labs depended on these systems to route orders and make lenses.",[34,768,45],{"id":44},[39,770,771],{},"Working cross-functionally with the Cubitts team, we stabilised all five areas, consolidating where we could and rebuilding from first principles where it was safe to — always mindful that the labs couldn't stop.",{"title":85,"searchDepth":86,"depth":86,"links":773},[774,775],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Cubitts","Stabilising five connected systems — website, production, POS, inventory and customer management — that the lens labs rely on.","Eyewear",{},[781],{"value":306,"label":782},"Business areas stabilised",9,"\u002Fwork\u002Fcubitts","partner","Principal Full Stack Engineer",[],{"title":742,"description":85},[18,790,735,791,657,792,683,793,759],"Python","Symfony","React","MySQL","work\u002Fcubitts","https:\u002F\u002Fcubitts.com","Feb – Nov 2024","LRptkTAEnC_Ki-fiN4oKML85SsasdkoRw--fgGm1yvY",{"id":799,"title":800,"anonymised":7,"architecture":801,"body":825,"client":873,"description":85,"extension":91,"featured":7,"headline":874,"industry":875,"meta":876,"metrics":877,"navigation":92,"order":887,"path":888,"practice":785,"role":400,"screens":889,"seo":890,"stack":891,"status":116,"stem":896,"url":897,"year":898,"__hash__":899},"work\u002Fwork\u002Fbirl.md","Engineering process for an AI resale startup",[802,808,814,821],{"name":803,"items":804},"Process",[805,806,807],"Trunk-based branching","Staging gate","Two-layer review",{"name":809,"items":810},"Delivery",[811,812,813],"GitHub Actions","Turborepo","Vercel previews",{"name":815,"items":816},"Platform",[817,818,819,820],"Cloud Run","GKE","OIDC auth","Cloud Logging",{"name":557,"items":822},[823,824],"Vertex AI image pipeline","Product embeddings",{"type":31,"value":826,"toc":869},[827,829,832,834,837],[34,828,37],{"id":36},[39,830,831],{},"Birl builds an AI platform that helps brands manage second-hand inventory across eBay, Shopify and Depop. The team was growing and needed engineering process to match.",[34,833,45],{"id":44},[39,835,836],{},"Mark led a team of four engineers across three repositories, while contributing directly to the code.",[50,838,839,845,851,857,863],{},[53,840,841,844],{},[56,842,843],{},"Process."," Documented Birl's first development process: dev, staging and production environments, trunk-based branching with a staging gate, AI-plus-human code review, CI\u002FCD, QA sign-off and a release cadence. The founders signed off an 8-week rollout plan.",[53,846,847,850],{},[56,848,849],{},"Infrastructure."," A three-environment GCP setup with Cloud Run, GKE namespaces and Vercel previews, and GitHub Actions pipelines with affected-only Turborepo builds.",[53,852,853,856],{},[56,854,855],{},"Production readiness."," An audit of the resale API found 18 issues, from missing route authentication to webhook signature checks. We then implemented the fixes, including GCP OIDC authentication across all routes.",[53,858,859,862],{},[56,860,861],{},"AI pipeline."," Migrated image processing from direct Gemini calls to Vertex AI, with clearer prompts, error handling and logging.",[53,864,865,868],{},[56,866,867],{},"Desktop app."," Helped take the Electron studio app from prototype to production, with 14 releases in a month.",{"title":85,"searchDepth":86,"depth":86,"links":870},[871,872],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Birl Technology","Setting up Birl's first formal engineering process while contributing to the AI pipeline and desktop studio app.","AI \u002F Fashion resale",{},[878,881,884],{"value":879,"label":880},"18","Readiness issues found and remediated",{"value":882,"label":883},"~275","Team commits a month across three repos",{"value":885,"label":886},"14","Desktop app releases in one month",10,"\u002Fwork\u002Fbirl",[],{"title":800,"description":85},[115,405,892,893,406,894,895,811,812],"Next.js","Electron","Vertex AI","Supabase","work\u002Fbirl","https:\u002F\u002Fwearebirl.com","Jan – Apr 2026","jSxq68eRqMrghrM2gRA__I9R5l11j6bnZ_cJLTdqKfM",{"id":901,"title":902,"anonymised":7,"architecture":903,"body":912,"client":936,"description":85,"extension":91,"featured":7,"headline":937,"industry":938,"meta":939,"metrics":940,"navigation":92,"order":944,"path":945,"practice":785,"role":400,"screens":946,"seo":947,"stack":948,"status":116,"stem":951,"url":952,"year":116,"__hash__":953},"work\u002Fwork\u002Fpluro.md","Closing the gap between founder and team at Pluro",[904,908],{"name":905,"items":906},"Leadership",[907],"CEO visibility",{"name":809,"items":909},[910,911],"PM + dev collaboration","Shared ownership",{"type":31,"value":913,"toc":932},[914,916,919,921],[34,915,37],{"id":36},[39,917,918],{},"Pluro's CEO wanted a clearer view of how the development team delivered projects, and better collaboration between developers and project management.",[34,920,45],{"id":44},[50,922,923,926,929],{},[53,924,925],{},"Identified gaps in how the tech team communicated with the PM and CEO.",[53,927,928],{},"Set up a way of working where everyone collaborates on projects and each person owns a particular part of it.",[53,930,931],{},"Rather than building a bridge between founder knowledge and team knowledge, we brought the two closer together. It's been a positive change for Pluro and an ongoing relationship.",{"title":85,"searchDepth":86,"depth":86,"links":933},[934,935],{"id":36,"depth":86,"text":37},{"id":44,"depth":86,"text":45},"Pluro","Working with the CEO to shape how projects run, and bring founder and team knowledge closer together.","Agency",{},[941],{"value":942,"label":943},"Ownership","Each team member owns part of every project",11,"\u002Fwork\u002Fpluro",[],{"title":902,"description":85},[949,950],"Delivery process","Team structure","work\u002Fpluro","https:\u002F\u002Fpluro.co.uk","w3C-Gu-BPeHpb5YPavESV8tSI8g1Kw7aMTvtPm2fCIc",{"id":955,"title":956,"anonymised":7,"architecture":957,"body":982,"client":1048,"description":85,"extension":91,"featured":92,"headline":1049,"industry":1050,"meta":1051,"metrics":1052,"navigation":92,"order":1061,"path":1062,"practice":1063,"role":1064,"screens":1065,"seo":1081,"stack":1082,"status":1088,"stem":1089,"url":1090,"year":116,"__hash__":1091},"work\u002Fwork\u002Fmuro.md","Muro, a social app for padel players",[958,963,968,972,977],{"name":21,"items":959},[960,961,962],"iOS","Android","Web",{"name":580,"items":964},[965,966,967],"Nuxt 4 static build","Capacitor 8 wrapper","Tailwind",{"name":589,"items":969},[970,971],"Nitro server routes","Realtime comments + scores",{"name":754,"items":973},[974,975,976],"Supabase Postgres","Row-level security","Magic OTP + Google sign-in",{"name":134,"items":978},[979,980,981],"Resend email","Firebase push","Vercel",{"type":31,"value":983,"toc":1043},[984,988,991,995,1033,1037,1040],[34,985,987],{"id":986},"why-we-built-it","Why we built it",[39,989,990],{},"Most padel groups run on a WhatsApp chat and a spreadsheet. Someone has to chase who's playing, find a sub when someone drops out and keep track of who's winning. Muro takes care of that for regular groups.",[34,992,994],{"id":993},"what-it-does","What it does",[50,996,997,1003,1009,1015,1021,1027],{},[53,998,999,1002],{},[56,1000,1001],{},"Recurring games."," Set up a weekly match once and invitations go out automatically to the regulars.",[53,1004,1005,1008],{},[56,1006,1007],{},"Filling open spots."," When someone cancels, the spot is offered to the wider crew. Invitees can RSVP without creating an account.",[53,1010,1011,1014],{},[56,1012,1013],{},"Live scoring."," A one-thumb scoring screen designed for courtside use, with golden point, undo and a match timeline. Followers see the score update in real time.",[53,1016,1017,1020],{},[56,1018,1019],{},"Leagues from regular play."," Standings, head-to-heads and season recaps are built automatically from the games a group already plays.",[53,1022,1023,1026],{},[56,1024,1025],{},"Teams."," Rosters with core, sub and injured players, and invites by email or a shareable link.",[53,1028,1029,1032],{},[56,1030,1031],{},"After the match."," Comments, ratings for the match and players, and a follow feature to keep up with friends.",[34,1034,1036],{"id":1035},"how-its-built","How it's built",[39,1038,1039],{},"Muro is a Nuxt 4 app in a pnpm monorepo. The same static build runs on the web and inside Capacitor 8 for iOS and Android, so there's one codebase for all three. Supabase provides Postgres, auth (magic one-time codes and Google sign-in) and realtime updates, with row-level security on every table. Server routes handle anything that writes data, Resend sends invites and Firebase handles push notifications.",[39,1041,1042],{},"The product design, illustration direction and design system were done in-house alongside the build.",{"title":85,"searchDepth":86,"depth":86,"links":1044},[1045,1046,1047],{"id":986,"depth":86,"text":987},{"id":993,"depth":86,"text":994},{"id":1035,"depth":86,"text":1036},"Muro","An app for regular padel groups to organise games, score matches live and build friendly leagues, built on iOS, Android and the web.","Sport, Social",{},[1053,1055,1058],{"value":303,"label":1054},"Platforms from one codebase: iOS, Android, web",{"value":1056,"label":1057},"Live","One-thumb courtside scoring, updated in real time",{"value":1059,"label":1060},"Auto","Leagues and standings built from regular play",12,"\u002Fwork\u002Fmuro","product","Founder, design and build",[1066,1069,1072,1075,1078],{"src":1067,"alt":1068,"framed":92},"\u002Fimages\u002Fwork\u002Fmuro\u002Fdashboard.webp","Home: next match and this week",{"src":1070,"alt":1071,"framed":92},"\u002Fimages\u002Fwork\u002Fmuro\u002Flive-score.webp","Live scoring",{"src":1073,"alt":1074,"framed":92},"\u002Fimages\u002Fwork\u002Fmuro\u002Fstandings.webp","League standings",{"src":1076,"alt":1077,"framed":92},"\u002Fimages\u002Fwork\u002Fmuro\u002Fteam.webp","Team roster",{"src":1079,"alt":1080,"framed":92},"\u002Fimages\u002Fwork\u002Fmuro\u002Frecurring.webp","Recurring matches",{"title":956,"description":85},[1083,1084,1085,895,1086,1087,980,981],"Nuxt 4","Vue 3","Capacitor 8","Tailwind CSS","Resend","iOS and Android, early access","work\u002Fmuro","https:\u002F\u002Fwww.muro.social","bXZeQP90VSYvFHUh7fybbH02a7IytpoU9E7dFSfyHOY",{"id":1093,"title":1094,"anonymised":7,"architecture":1095,"body":1114,"client":1174,"description":85,"extension":91,"featured":92,"headline":1175,"industry":1176,"meta":1177,"metrics":1178,"navigation":92,"order":1187,"path":1188,"practice":1063,"role":1064,"screens":1189,"seo":1219,"stack":1220,"status":1224,"stem":1225,"url":116,"year":116,"__hash__":1226},"work\u002Fwork\u002Fnoori.md","Noori Health, an iPhone app for tracking symptoms",[1096,1100,1106,1110],{"name":21,"items":1097},[1098,1099],"iPhone app (Ionic + Capacitor)","Web app (Nuxt 4)",{"name":1101,"items":1102},"Safety",[1103,1104,1105],"Consent capture","Red-flag routing to NHS 111 \u002F 999","Educational-only AI labels",{"name":557,"items":1107},[1108,1109],"Assistant chat","Retrieval service",{"name":754,"items":1111},[974,1112,1113],"Default-deny row-level security","Drizzle for server writes",{"type":31,"value":1115,"toc":1169},[1116,1118,1121,1123,1161,1163,1166],[34,1117,987],{"id":986},[39,1119,1120],{},"People living with ongoing symptoms often arrive at appointments without a clear picture of how they've been. Noori is a private record between appointments: log how you feel, see the pattern and walk in prepared.",[34,1122,994],{"id":993},[50,1124,1125,1131,1137,1143,1149,1155],{},[53,1126,1127,1130],{},[56,1128,1129],{},"Intake."," A short, guided intake covering focus areas and symptoms, which tailors what Noori tracks.",[53,1132,1133,1136],{},[56,1134,1135],{},"Daily check-in."," About 30 seconds to rate the symptoms being tracked, pre-filled from the last log.",[53,1138,1139,1142],{},[56,1140,1141],{},"Timeline."," Trends over 30 days, 90 days or all time, with a breakdown by symptom.",[53,1144,1145,1148],{},[56,1146,1147],{},"Noori assistant."," A chat assistant that asks for consent first, keeps data private and flags when someone should contact NHS 111 or 999.",[53,1150,1151,1154],{},[56,1152,1153],{},"Find a nutritionist."," A directory of registered nutritionists, with requests, saved profiles and in-app chat.",[53,1156,1157,1160],{},[56,1158,1159],{},"For nutritionists."," A dashboard of requests and clients, intake snapshots, shared check-in ratings and notes.",[34,1162,1036],{"id":1035},[39,1164,1165],{},"The iPhone app is built with Ionic, Vue and Capacitor, sharing design tokens, content and types with a Nuxt 4 web app through a pnpm workspace. Supabase provides Postgres and auth: client reads go through row-level security that denies by default, and all writes go through server routes using Drizzle.",[39,1167,1168],{},"Safety is designed in rather than added later. Every AI-derived screen carries an \"educational, not diagnostic\" label, red-flag routing lives in one place in code, and consent is captured before anything is shared with a nutritionist.",{"title":85,"searchDepth":86,"depth":86,"links":1170},[1171,1172,1173],{"id":986,"depth":86,"text":987},{"id":993,"depth":86,"text":994},{"id":1035,"depth":86,"text":1036},"Noori Health","A health companion that helps people log symptoms between appointments, see patterns over time and connect with a registered nutritionist.","Digital health",{},[1179,1181,1184],{"value":191,"label":1180},"Sides: patients and nutritionists",{"value":1182,"label":1183},"30s","Daily check-in",{"value":1185,"label":1186},"Not diagnostic","Every AI surface clearly labelled as educational",13,"\u002Fwork\u002Fnoori",[1190,1193,1196,1199,1201,1204,1207,1210,1213,1216],{"src":1191,"alt":1192},"\u002Fimages\u002Fwork\u002Fnoori\u002F01-signed-out-welcome.webp","Welcome",{"src":1194,"alt":1195},"\u002Fimages\u002Fwork\u002Fnoori\u002F04-intake-symptoms.webp","Intake: symptoms",{"src":1197,"alt":1198},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-home.webp","Home",{"src":1200,"alt":1183},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-check-in.webp",{"src":1202,"alt":1203},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-timeline.webp","Symptom timeline",{"src":1205,"alt":1206},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-noori.webp","Noori assistant, with consent",{"src":1208,"alt":1209},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-chat-thread.webp","Chat with a nutritionist",{"src":1211,"alt":1212},"\u002Fimages\u002Fwork\u002Fnoori\u002F02-patient-directory.webp","Find a nutritionist",{"src":1214,"alt":1215},"\u002Fimages\u002Fwork\u002Fnoori\u002F03-practitioner-dashboard.webp","Nutritionist dashboard",{"src":1217,"alt":1218},"\u002Fimages\u002Fwork\u002Fnoori\u002F03-practitioner-client-detail.webp","Client request and intake",{"title":1094,"description":85},[1221,1084,1222,1083,895,1223,115,1086],"Ionic","Capacitor","Drizzle ORM","iPhone app in development","work\u002Fnoori","hOqyYyo3JOduUpUpbdKdK0iHwyL-Uy3YHTXMAc-3Yy8",1790883139391]