06Wooden 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.
- Jul 2023 – present
- One, continuously live
- 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.
- 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.
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.
The store is configured, not yet programmed.
Shopify Admin
Storefront content and theme configuration on a live commercial store. First production Liquid, HTML, CSS and JavaScript.
Lead Graphic Designer
Owned the e-commerce visual system across storefront, campaign and product imagery.
Lead Web Developer
Theme and storefront development, integrations, and release QA — plus the internal tools built around the store.
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.
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
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
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
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
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.
| TYPED | ALSO RETURNED | CARRIES THE WORD? |
|---|---|---|
| lobster | OH CRAB!, KEY WEST | No |
| pumpkin | MAUI V, CROP BOYFRIEND | No |
Public product titles from the public storefront. Neither word appears in the title, tags, description or variants of any of them.
snippets/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”.
snippets/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.
the 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.