Enamul Hasan Shopify & ecommerce growth

I build the storefront, then I go and sell through it.

Six years in Dubai running premium furniture and home brands on Shopify — writing the Liquid, wiring the APIs, running the ads, reading the analytics. Same person, whole loop. It means the fix for a conversion problem is a code change I can ship on Tuesday, not a ticket that waits three weeks for someone else's sprint.

Open to new roles Dubai, UAE · Chennai, India Happy to relocate, hybrid or remote Currently at WS Living
AED 28–30MAnnual revenue of the storefront I own end to end at WS Living
5×Revenue at Ebarza, AED 600K to AED 3M over three years
+37%Revenue in 90 days, no extra ad spend
30%Of revenue from repeat buyers, inside 12 weeks
4–8×ROAS held on core collections
3,000+SKUs rebuilt for search and AI answers

The record

Two brands, one job description that kept growing.

I started as the person who fixed the store and ended up as the person who owns it — the theme, the catalogue, the paid media, the retention stack and the reporting that tells you whether any of it worked.

WS Living

Ecommerce Manager & Shopify Product Head

Sep 2023 — present · Dubai, UAE

Premium furniture and home decor doing AED 28–30 million a year, with a Dubai showroom, an in-house workshop of 55 artisans and a 3,000+ SKU Shopify catalogue. I'm the only person who touches the digital side — leadership, merchandising, marketing and operations all come through me, and whatever gets decided, I'm also the one who builds it. A store at that scale doesn't get taken offline for a rebuild, so everything below shipped against live traffic.

Most of what I ship here isn't a feature you'd find in an app store. It's furniture-shaped: a sofa that can be made 40cm longer, an armchair that comes in nine fabrics that can't be Shopify variants without exploding the catalogue, a bed size that's non-returnable and needs the customer to actually understand that before they pay. Every one of those started as an operations problem someone was solving by hand, over WhatsApp.

The other half is the boring, load-bearing work: a genuinely bilingual English and Arabic storefront with real RTL rather than a translation overlay, structured data that validates, a discount system that stops codes stacking on already-marked-down stock, and a reporting layer that pulls Shopify, Search Console and ad data into one place so the numbers in the meeting are the same numbers in the dashboard.

Ebarza

Ecommerce Manager & Shopify Developer

Jan 2020 — Aug 2023 · Dubai, UAE

Lifestyle and home decor retailer, and the place where I learned this properly. I took the store from AED 600K to AED 3M over three years by doing both halves of the job at once: rebuilding the storefront end to end, then pointing paid and organic traffic at something that was finally worth landing on.

The storefront rebuild went all the way down — information architecture, navigation, product templates, cart and checkout — and lifted conversion to category-leading levels for home and lifestyle. A reusable seasonal landing page system meant a new collection launch stopped being a build and started being an afternoon. Those pages, with Klaviyo capture wired into them, drove a 35% lift in email signups.

Alongside the web store there was a native iOS and Android app and a set of marketplace channels, so the catalogue had to be adapted rather than copy-pasted — titles, imagery, attributes and pricing tuned to how each platform actually ranks and sells. The retention stack ran across Omnisend, Klaviyo, WhatsApp and Zoho CRM, with a loyalty and rewards programme built into the storefront that lifted repeat revenue.

The ledger

Everything I've shipped, big and small.

Most portfolios show three flagship projects. This is the honest version — the flagship builds and the twenty small ones that quietly hold a store together. Where the work is public, the link goes straight to the live page so you can go and use it.

01 A bilingual English and Arabic storefront, with real RTL Not a translation overlay — a mirrored second-market experience across every template. Storefront ▾

A lot of "Arabic ready" stores are English stores with the words swapped. This one mirrors properly: layout direction, navigation, typography, product templates, the components inside them, and locale-aware URLs. The switcher runs off Shopify's native localisation — a dropdown in the desktop top bar and a pill toggle at the bottom of the mobile drawer — sitting on top of the translation layer rather than fighting it.

The interesting part was diagnosing why it was invisible. The code was already there and correct; Arabic simply wasn't published as a locale, so the storefront saw one available language and rendered nothing. Publishing the locale and attaching it to the UAE market brought the whole thing to life without a single theme edit. Every custom component I've built since ships with its Arabic copy in the same file, so the second language never falls behind the first.

LiquidShopify MarketsRTL CSSLocale routingGraphQL Admin API
02 Fabric swatch drawer for made-to-order upholstery Nine fabrics on one sofa, without nine variants and without touching the catalogue. Commerce logic ▾

Upholstery is the classic Shopify trap. A sofa in nine fabrics across three sizes is 27 variants that all cost the same and all need photography, and it drowns the catalogue and the feeds. So the fabric isn't a variant here. It's a slide-in drawer of grouped swatches with a large hover preview, and the choice is written onto the cart line as a line-item property that prints on the order for the workshop.

Clicking a swatch also swaps the main gallery image where a real photo of that combination exists, so a customer picking Sage on a Valencia sees the Sage sofa, not the studio default. Adding a sofa is a config edit rather than a build, and the collection cards badge themselves — each card lazily checks its own product page for a selector and pulls the first four swatches in as round mini-swatches. Give any product the drawer and it starts advertising itself everywhere in the store on its own.

LiquidVanilla JSLine-item propertiesIntersectionObserversessionStorage cache
03 Made-to-order gate on non-returnable bed sizes A confirmation pop-up that leaves an audit trail on the order. Commerce logic ▾

King is the stock size. Queen and Super King are made to order, take three to four weeks and can't be returned — and customers were finding that out after paying. The fix is a dialog on those two sizes with a scaled diagram drawn from the real mattress footprint, the measurements, the terms, a tick-box and a Confirm button. Add to Cart doesn't work until it's acknowledged.

What makes it defensible rather than decorative is what happens on confirm: it stamps a visible Made to order property plus a hidden ISO timestamp onto the line. When a customer disputes a three-week lead time, there's a record on the order showing they ticked the box and when. The gate runs as a document-level capture listener so it fires ahead of the theme's own form handler, and the dynamic checkout button is hidden while an unacknowledged size is selected — otherwise Shop Pay just walks straight around it.

I only drew diagrams for sizes the owner actually confirmed. Inferring bed dimensions from a standard chart would have put a wrong number on a non-returnable product.

Liquid snippetNative dialogCapture-phase eventsVariant metafieldsSVG
04 Sofa length customiser, 240cm to 325cm Arbitrary pricing on a cart line, which Shopify does not want to let you do. Commerce logic ▾

The workshop can build a sofa at any length in that range, so the store should sell it that way. I proposed variants twice and was told no twice — correctly, because 18 length variants per sofa would have wrecked the catalogue. The accepted model is one product, one price and a slider.

Price scales linearly from the sofa's own rate — its live price divided by its standard length — rounded to the nearest five dirhams, plus a flat customisation fee. Since the base price is read live, putting the sofa on sale needs no code change. The money itself rides on a separate AED 1 helper product, quantity equal to the upgrade in whole dirhams, unpublished from every channel except the online store so it never leaks into Google or Meta feeds.

Which creates the real problem: two cart lines that must live and die together. A cart guard ties them with a hidden key and runs on every page — orphaned charge gets removed silently, mismatched quantity gets corrected silently, and a customised sofa with no charge raises a pop-up rather than letting the order through underpriced. Turning it on for another sofa is two metafields, no code.

LiquidCart AJAX APIfetch patchingMetafield configpublishablePublish
05 Marble size picker with a scaled 3D preview Nine sizes of a travertine pedestal, drawn to scale before you commit. Commerce logic ▾

A custom-cut travertine table is a heavy, expensive, non-returnable object, and "40 × 40 × 45" means very little to most people standing in their living room. This one gives nine priced sizes and a pop-up that renders the block to scale as you change them, so the difference between two sizes is something you see rather than something you calculate — with the non-returnable terms behind a tick-box before it will add to cart.

LiquidCSS 3D transformsVariant pricing
06 Shop the Look, driven entirely by metafields Merchandisers curate it; it hides itself when a product goes out of stock. Storefront ▾

Furniture sells in rooms, not in single objects, so the product page needed a way to show the rest of the set. The section reads a metafield on the product, so the merchandising team curates it themselves without a developer, and it removes itself automatically when a linked product is deactivated. That last part matters more than it sounds: the usual failure of a hand-built "complete the look" block is that it quietly keeps pointing at a discontinued product for months.

MetafieldsLiquid sectionSchema settings
07 Infinite scroll with a spinner made from the store logo 1,000+ products, no pagination, and a loader that works on any background. Storefront ▾

Pagination on a furniture catalogue is where browsing sessions go to die. Collection pages, /collections/all and search now load continuously, and the pagination never appears. The script keys off the next-page link and re-scans on section load and DOM mutation, so it survives the theme's AJAX filter and sort swaps instead of breaking the first time someone sorts by price.

The loader was the fun bit. The brand logo is white on transparent — built for the dark header, so on a light collection page it's simply invisible. Rather than commission a second asset, the spinner paints the logo as a CSS mask filled with currentColor, so it inherits the page's text colour and works on any background, in either theme, forever.

Vanilla JSMutationObserverCSS mask-imageHistory API
08 Full-screen vertical slideshow on the home page Five stacked banners replaced with one editorial sequence. Storefront ▾

The home page was five category banners stacked vertically — the layout every Shopify furniture store defaults to, and the reason nobody scrolls past the second one. It's now a full-screen vertical slideshow closer to how Zara Home presents a season: one image at a time, at full height, with the category as a destination rather than a tile.

There's a second sequence for the commercial side of the business — a "For Projects" slideshow separating hospitality and fit-out enquiries from retail customers, because those two audiences want completely different things and were previously landing on the same page.

Liquid sectionScroll snapLazy mediaSchema blocks
09 Payment badges, trust blocks and a branded loading splash The small conversion furniture: local payment methods, government cards, a splash screen. Storefront ▾

In the UAE, trust signals are specific and local. The product-page badge section covers Apple Pay and Google Pay, Amex, Tabby for buy-now-pay-later, and the Fazaa, Esaad and DEWA employee programmes that a large slice of Dubai customers actually check for before buying furniture. It's a block-based section, so marketing adds a new provider by uploading a logo in the theme editor rather than asking me.

Same category of work: a branded shimmer splash on first load, and a bilingual showroom location section, because "where can I come and sit on it" is the single most common message a furniture store gets.

Liquid blocksTheme editor schemaCSS animationEN/AR
10 Stopping discount codes stacking on marked-down stock 86 codes rescoped, margin quietly recovered. Commerce logic ▾

Sale items were still accepting discount codes. Nobody had designed that; it accumulated, one campaign at a time, across manual codes, Flow-generated codes, loyalty rewards and email automations. The result was double discounts on exactly the stock with the least margin left.

I built a Full Price smart collection — everything without a compare-at price — and rescoped every affected code to it, then verified them. Credit notes and trade codes were deliberately left alone, because those are commercial commitments, not promotions. The structure means new products inherit the right behaviour automatically instead of depending on somebody remembering.

Smart collectionsDiscount APIShopify FlowSmile.ioOmnisend
11 3,000+ SKUs rewritten for search and for AI answers Inherited supplier copy, replaced with something a machine can quote. Search ▾

The catalogue arrived as supplier boilerplate — the same paragraph on forty products, no intent mapping, nothing an answer engine could extract a fact from. Rewriting 3,000 products by hand isn't a project, it's a career, so I built the pipeline instead: intent-mapped titles and descriptions, fact-dense body copy structured so a model can lift a specification and cite it, generated through Claude and MCP workflows with the store as the source of truth.

The measure isn't just rankings any more. It's whether the brand gets quoted — in AI Overviews, in ChatGPT, in Perplexity — and that needs facts stated plainly, marked up properly and linked internally, not adjectives.

Claude + MCPAdmin APIIntent mappingBulk operations
12 Structured data overhaul Duplicate and malformed JSON-LD, removed until the store validated. Search ▾

Years of apps had each helpfully injected their own schema. The store was emitting duplicate and conflicting Product and Organization blocks with syntax errors inside them, which is worse than emitting none — Google can't tell which one is true, so it trusts neither. I audited every template, removed the orphaned blocks, and rebuilt Product, FAQ, Organization and local business markup as valid JSON-LD with consistent NAP details for Dubai local search.

JSON-LDSearch ConsoleRich Results TestLiquid templates
13 A UAE-keyword blog engine Content built from question research, not from a content calendar. Search ▾

Rather than publishing on a schedule and hoping, I built a repeatable pipeline around what people in the UAE actually type and ask — high-intent local terms, People Also Ask questions, and the prompts people put to AI assistants when they're furnishing a place. Each piece is structured answer-first with FAQ markup, so it's eligible for a snippet and legible to an answer engine on the same page.

Claude skillsSearch ConsoleFAQ schemaInternal linking
14 Lifecycle stack: Klaviyo, Omnisend and WhatsApp Repeat purchase to 30% of revenue in 12 weeks, in a category built to resist it. Retention ▾

Furniture has a brutal retention profile — somebody buys a sofa and then genuinely doesn't need anything for two years. Treating that like fashion email gets you unsubscribes. So the flows are built around the room instead of the calendar: what goes with what you bought, when a delivery actually lands, and what a second room looks like.

Segmented email and WhatsApp flows took repeat purchase to 30% of revenue within 12 weeks. WhatsApp matters disproportionately in this market; it's where UAE customers already are, and open rates aren't comparable to email. The transactional and abandoned-checkout templates are hand-coded to match the storefront's design system rather than sitting in the default template a customer has seen from fifty other stores.

KlaviyoOmnisendWhatsApp Business APIZoho CRMSmile.io
15 Automated WhatsApp alerts when a delivery date moves Made-to-order lead times change; customer service was chasing them by hand. Ops & data ▾

With a workshop building to order, delivery dates move. Someone in customer service was noticing that and messaging the customer manually — which meant it happened reliably on quiet days and unreliably on busy ones, when it matters most. A Shopify Flow now detects the change on the order and fires the WhatsApp notification itself. Nobody has to remember, and the customer hears it from the brand before they have to ask.

Shopify FlowWhatsApp Business APIWebhooksZapier
16 Containing an invalid traffic incident Thousands of zero-conversion sessions poisoning the retargeting pools. Ops & data ▾

Sessions spiked, conversion rate cratered, and the dashboards said the store had suddenly got worse. It hadn't — automated traffic was flooding it. The real damage wasn't the analytics, it was that every retargeting audience was being built from bots, so ad spend was chasing impressions that could never buy.

I isolated the source, applied edge-level controls that blocked it without touching legitimate search crawlers, and then rebuilt the contaminated audiences from clean data. Restoring attribution accuracy mattered as much as stopping the traffic: until the pools were rebuilt, every optimisation decision downstream was being made on poisoned numbers.

CloudflareGA4Meta AdsBot mitigation
17 Clearing out the page-builder debt Three uninstalled apps, 78 orphaned theme files still being served. Ops & data ▾

Foxify, GemPages and PageFly had all been installed and uninstalled before I arrived, and each one left its files behind in the theme — around 78 sections, snippets and assets that nothing rendered but that every developer after me would have to read and be uncertain about. I traced every file to confirm nothing referenced it, then produced a verified delete list.

Worth being honest about the ending: the Admin API blocks file deletion, so the removal itself is a manual pass in the theme editor rather than something I could script. Half of theme maintenance is knowing exactly which files are safe to delete; the deleting is the easy part.

Theme auditAdmin APIDependency tracingGit
18 An 18-month sourcing analysis that changed what gets made Locally made SKUs earning 3.2× the revenue per SKU of imported lines. Ops & data ▾

The question from leadership was which lines to expand. I pulled 18 months of order data against the catalogue and split it by sourcing — made in the Dubai workshop versus imported — and normalised it per SKU, because raw revenue just tells you which category is biggest.

Locally made SKUs were generating 3.2× the revenue per SKU of imported lines. That's a production and catalogue decision, not a marketing one, and it's the kind of answer you only get when the person reading the data also knows how the catalogue is structured and where it lies to you.

ShopifyQLOrder exportsExcelCatalogue mapping
19 MCP reporting layer and a catalogue report tool Shopify, Search Console, Klaviyo and Meta answering questions in one place. Ops & data ▾

Weekly reporting used to be four dashboards, four exports and an afternoon of reconciling numbers that didn't agree. I connected Shopify, Search Console, Klaviyo and Meta Ads through MCP servers so the whole stack can be queried conversationally, and built a catalogue report tool that pulls products, images and performance into one reviewable artifact for merchandising decisions.

This isn't AI as a novelty. It's the difference between "I'll have that by Thursday" and answering the question while the meeting is still happening.

MCP serversClaude CodeShopifyQLNodeLooker Studio
20 Custom Order Printer templates for the workshop The picking slip has to carry the fabric code, or the sofa gets built wrong. Ops & data ▾

Every bit of customisation on the storefront — fabric choice, custom length, made-to-order acknowledgement — is worthless if it doesn't reach the floor. The Order Printer templates are rebuilt so line-item properties print clearly on the workshop's paperwork, in the order the people building it actually read. Storefront features that don't survive the trip to operations aren't features, they're demos.

Order Printer LiquidLine-item propertiesPrint CSS
21 Ebarza: full storefront rebuild Architecture through checkout, and the conversion rate that came with it. Storefront ▾

A rebuild that went all the way down — information architecture, navigation, product templates, cart and checkout — on a live store that had to keep selling throughout. It lifted conversion to category-leading levels for home and lifestyle and underpinned the growth from AED 600K to AED 3M.

Two habits came out of that job and stayed. First, page speed is a merchandising decision: I stripped legacy app code left behind by previous installs and reworked image and script delivery. Second, document the theme — component usage, conventions, deployment steps — so contractors can work on it safely. I also reviewed contractor code before it deployed, which is where most theme rot actually enters a store.

LiquidTheme architectureCheckoutLighthouseCode review
22 Ebarza: a landing page system, not landing pages +35% email signups, and collection launches that stopped being builds. Storefront ▾

Every seasonal campaign was a bespoke page, which meant every campaign had a lead time and every page aged differently. I replaced them with dynamic, data-driven templates: pick the collection, set the copy, launch. Klaviyo capture was built into the template rather than bolted on per page, which is where the 35% lift in email signups came from — the form went from being remembered sometimes to existing by default.

Liquid templatesKlaviyo formsA/B testingDynamic sections
23 Ebarza: loyalty programme built into the storefront Points and redemption inside the theme, not stranded in a widget. Retention ▾

Most loyalty programmes fail quietly because they live in a floating widget nobody opens. This one was built into the storefront with account-level logic and redemption displayed where customers were already looking, and it lifted repeat customer revenue. Rewards that customers can't see are just a liability on the balance sheet.

Smile.ioCustomer accountsLiquidZoho CRM
24 Ebarza: mobile apps and marketplace channels iOS, Android, and a catalogue adapted per platform rather than copy-pasted. Ops & data ▾

Alongside the web store there were native iOS and Android apps and a set of marketplace channels. Feeding the same catalogue into all of them badly is the standard mistake — each platform ranks differently and buyers arrive with different expectations, so titles, imagery, attributes and pricing were adapted per channel, along with Google Shopping and Meta catalogue feed management.

Feed managementAmazonNoonGoogle ShoppingMeta Catalogue

Engineering notes

Three bugs that each cost me a day.

Anyone can list the features that shipped. These are the ones that didn't work first time, and what was actually wrong — which is a better sample of how someone works than a feature list.

Scroll lock

The dialog event that never fired

The made-to-order gate uses a native <dialog>. Everything worked except cleanup on close — and the page would stay scroll-locked forever after a customer dismissed it. The cause: on this store's pages the native close event is never dispatched, even though showModal and close themselves are native and working. Anything that must run on close now watches the open attribute with a MutationObserver instead. Trusting the spec over the actual runtime cost me a day.

Race condition

A cart guard that ate its own charge

The custom-length sofa adds two cart lines that must stay in sync, policed by a guard that removes orphaned charges. Intermittently it deleted the charge it had just added — it was reading the cart mid-add, seeing a charge with no sofa yet, and correctly concluding it was an orphan. The fix was an in-flight counter around a patched window.fetch, so no consistency check runs while any /cart/* request is still open. Intermittent bugs in cart code are almost always this.

Invisible asset

A logo you couldn't see

The infinite-scroll spinner used the store logo and worked perfectly in the header, then vanished on collection pages. The asset is white on transparent, built for a dark header, and the collection background is a light stone. Instead of asking for a second asset that would drift out of sync, the spinner paints the logo as a CSS mask-image filled with currentColor, so it inherits the page's text colour and works on any background in any theme. One asset, no maintenance.

About

What I'm actually good at.

I came into ecommerce from the commercial side and learned to code because the things I needed didn't exist. That order matters — I've never written a feature without knowing what it was supposed to earn.

Shopify
Liquid, Online Store 2.0 sections and blocks, metafields and metaobjects, Shopify Markets and Flow, cart and checkout customisation, Shopify CLI, theme deployment
Front end
HTML5, CSS3, vanilla JavaScript (ES6+), responsive and RTL layouts, Core Web Vitals, accessibility fundamentals, Figma to code
APIs
Storefront and Admin GraphQL, REST, webhooks, cursor pagination and cost limits at catalogue scale, Zapier, MCP servers
Search
Technical and on-page SEO, JSON-LD and entity markup, AEO and GEO, keyword and question research, local SEO for the UAE
Paid media
Meta, Google and Pinterest Ads, ROAS and CAC optimisation, audience testing, creative refresh cycles, feed management
Retention
Klaviyo, Omnisend, WhatsApp Business API, segmentation, lifecycle automation, loyalty and rewards, Zoho CRM
Analytics
GA4, Google Search Console, Looker Studio, ShopifyQL, conversion tracking, funnel and drop-off analysis
AI workflows
Claude and Claude Code, MCP integrations, AI-assisted content and QA pipelines, Midjourney and Gemini for creative direction
Languages
English (professional), Hindi (native), Tamil (native), Arabic (basic) — enough Arabic to QA an RTL storefront properly
Learning
React, Next.js, Shopify Hydrogen and Remix, Node and TypeScript for custom app development
Education
Bachelor of Commerce, Sadakathullah Appa College, Manonmaniam Sundaranar University, Tamil Nadu, 2014–2017
Availability
Based in Dubai on a transferable UAE employment visa, with a base in Chennai. Open to relocation, hybrid and fully remote roles, including North America time zone overlap

Contact

If any of this looks like a problem you have, write to me.

I'm open to senior ecommerce, Shopify development and growth roles — on site, hybrid or fully remote, and I'm willing to relocate. Reachable on either number, and I answer email quickly.

Write to me LinkedIn
UAE
+971 56 860 1878Dubai · WhatsApp
India
+91 82487 24792Chennai · WhatsApp
Elsewhere
LinkedIn
GitHub
Email copied