06/Wooden Ships

Three yearsin production.

One Shopify storefront, live the whole time. Between July 2023 and now the job title underneath it changed four times — Shopify Admin, Lead Graphic Designer, Lead Web Developer, AI & Automation Engineer — and the store went on taking orders through every one of them.

This page is about that: a single environment I kept changing while my relationship to it changed underneath. It is not a claim to have built the business it belongs to.

SPAN
Jul 2023 – present
STOREFRONTS
One, continuously live
ROLES HELD
Four, no gap between them

What this page is claiming

Exactly this much,and no more.

Storefront work is easy to overstate, because the store is public and the person describing it is not. So the scope goes first, in the same words I would use in a room with the people who were there.

DISTANCE FROM THE STORE

In the storefront
Code that ships to the live theme.
Reads from the store
A separate system whose input is the store’s own data.
Alongside it
Same product line, different platform — not the storefront at all.

WHAT THE WORK WAS

  • Led web development on a live commercial storefront from September 2024.
  • Implemented storefront changes in Liquid and shipped them to production.
  • Integrated apps and services against the Shopify Admin GraphQL API.
  • Coordinated production releases and release QA on a store that cannot go down.
  • Built internal systems around the store — merchandising, production planning, catalogue.
  • Automated operational workflows, and wrote the runbooks so someone else could run them.

WHAT IT WAS NOT

  • Founding, owning or building the company. It existed before me and does not depend on me.
  • Writing the merchandising policy. That belongs to the merchandising team; I encoded their flowchart faithfully, including the branches I would have drawn differently.
  • Authoring the whole theme. The storefront change documented further down touched 8 files out of 721.
  • Showing anything internal. No admin screen, no order, no customer record and no employee record appears anywhere on this site.
  • Claiming a measured outcome. None of these systems instrument themselves, so there is no percentage on this page and there will not be one.

The same store, four times

One storefront.
Four job titles underneath it.

Move through the years. The frame does not move — it is the same store the whole way, and it stayed live the whole way. What changes is how much of it I was responsible for, and what had started reading from it.

Jul 2023 – Mar 2024

Shopify Admin

Configuration first, then the first line of Liquid.

Storefront content and theme configuration on a live commercial store — the settings, the copy, the collections. Then theme customisation, then writing Liquid and JavaScript to change how the store behaved rather than only what it said.

WHAT CHANGED IN THE FRAMEThe store is configured, not yet programmed.

THE LADDER, IN FULL

  1. Jul 2023 – Mar 2024

    Shopify Admin

    Storefront content and theme configuration on a live commercial store. First production Liquid, HTML, CSS and JavaScript.

  2. Mar 2024 – Sep 2024

    Lead Graphic Designer

    Owned the e-commerce visual system across storefront, campaign and product imagery.

  3. Sep 2024 – Nov 2025

    Lead Web Developer

    Theme and storefront development, integrations, and release QA — plus the internal tools built around the store.

  4. Nov 2025 – Present

    AI & Automation Engineer (formal title: AI Project Manager)

    Internal systems first. Storefront changes still implemented and shipped, most recently in August 2026.

One employer, one storefront, four titles, no gap between them. The dates are the roles as held; nothing between them is inferred.

Evidence

Four things
I can point at.

One lives inside the storefront. Two read from it. One sits beside it. The distance from the store is written on each, because it is the part that is easiest to blur and the part that matters most.

  1. 01/In the storefront

    Storefront search relevance

    Shopify expands a storefront query semantically, so /search returned products that shared only a theme with what the customer typed. Two Liquid snippets post-filter the result set: normalise both sides of the comparison, then drop any item that does not carry every word typed. Deliberate tag matches survive; the product description is excluded from the haystack, because a stray mention there is exactly what makes a result look wrong.

    • 8 files touched, out of 721 in the theme
    • 6 commits, all on 19 August 2026
    • 22 punctuation characters normalised on both sides of every comparison
    • Shipped as an opt-out theme setting, so it cannot be silently disabled

    THE WHOLE CHANGE, CLOSE UP

  2. 02/Reads from the store

    Merchandising decision engine

    One button reads the seasonal merchandising sheet, pulls the store’s own last 14 and 30 complete days of Shopify sales in the store’s timezone, resolves each sheet row to a Shopify style and colour across two naming systems, runs the merchandising team’s flowchart as a pure function, and writes three columns back into a sheet people are actively using. Preview, then apply.

    • 221-line pure decision engine, every threshold in one exported object
    • 5 decision actions across 16 branch outcomes
    • Byte-identical output on a re-run over unchanged data — the engine touches no network, no sheet and no clock
    • ~14,000 lines of TypeScript across 69 modules and 15 test files

    RUN IT ON SYNTHETIC DATACASE STUDY

  3. 03/Reads from the store

    Order export for production planning

    A production-planning team exports orders themselves, behind company Google sign-on, instead of asking a developer. Two parts are genuinely hard. Fetching is two-pass, paginated and cost-aware, because a fully nested query blows straight past Shopify’s 1000-point per-query cap. And the warehouse for each unit is decided by a fixed preference — where units were actually shipped from, else where unfulfilled units are meant to ship from, else Unknown — never from inventory levels, which only say where stock sits.

    • One row per line item; a line split across locations becomes multiple rows so the split stays visible
    • Stock verdicts are conservative by construction — a missing part of the key returns REVIEW, never a false yes
    • 37 tests, all offline against fixture-built GraphQL shapes
    • 13 numbered design documents, including a non-technical user guide

    CASE STUDY

  4. 04/Alongside the store

    Seasonal catalogue pipeline

    Not the storefront — a wholesale catalogue for the same season’s product line, on WordPress. Eight single-purpose command-line tools take a season’s swatch and colourway images from local folders to live product records: rename, upload what is missing, pair each colourway to its swatch by a global best-total-score assignment rather than greedily, then three read-only audits and one careful refresher for the destructive case.

    • 2,145 lines of Python across 8 tools
    • 165 products and roughly 2,100 image files handled per season
    • Every write tool dry-run by default; three are read-only by construction
    • Bitmask dynamic program for 16 swatches or fewer, greedy above

    CASE STUDY

One change, close up

A search for “lobster”that returned no lobster.

Shopify’s storefront search expands a query semantically. The result is a page of products that share a theme with what was typed and not the word itself — which, to a customer, reads as a broken site rather than a clever one.

Two searches on the live storefront and the products they returned that do not carry the typed word anywhere.
TYPEDALSO RETURNEDCARRIES THE WORD?
lobsterOH CRAB!, KEY WESTNo
pumpkinMAUI V, CROP BOYFRIENDNo

Public product titles from the public storefront. Neither word appears in the title, tags, description or variants of any of them.

THE FIX, IN THREE PIECES

  1. 01snippets/search-normalize.liquid

    Downcases, strips HTML, and rewrites 22 punctuation characters to spaces — applied to both sides of every comparison, so OH CRAB! equals “oh crab”, and a SKU reading PURE SNOW/BLACK becomes findable by “snow”.

  2. 02snippets/search-term-match.liquid

    Builds the haystack from curated fields only: title, vendor, type, joined tags, joined variant titles, joined variant SKUs. The description is excluded on purpose — a stray mention there is exactly what makes a result look wrong.

  3. 03the three search templates

    Split the query into words and return false the moment one of them is missing. Every typed word must be present; the order does not matter. Main search, predictive search and suggestions render only the items that come back true.

Curated tag matches still work — a colourway, a team name, a collaboration tag — because those are fields the merchandiser chose. Only the semantic guesses and the stray description mentions stop appearing.

  • 8 of the theme’s 721 files touched, isolated into a changes-only folder for handover
  • A later commit made the filter opt-out rather than opt-in, “so it cannot be silently disabled”
  • Line-ending normalisation deliberately disabled, so the files stay byte-identical to what Shopify serves

The rest of it

One of theseyou can run yourself.

The merchandising engine on this page is the real one — the decision file copied out of the production repository unchanged, running on synthetic rows, pinned by a parity test. The others are documented rather than demonstrated, because faking a warehouse would argue against the point of having one.

If something on this page is the kind of problem you have — hello@jdenandro.com.