Project portfolio Browse selected work

Shopify Plus Upgrade Monthly Fee Reduction + Up to $4800 Development Fee Credit - Exclusive WesWoo Offer

Guide

Shopify Theme Development: Architecture, Liquid, QA, and Reversible Releases

Published: Editorial review: 2026-08-30

Shopify theme development produces more than files that happen to render in a browser. It produces a storefront interface that can be explained, checked, and restored. The lifecycle starts with requirement classification, then moves through directory contracts, Liquid data boundaries, JSON templates, sections and blocks, app extensions, development themes, code review, release acceptance, and a traceable recovery path. Every boundary below is written as a check that design, development, content, merchant, and on-call roles can execute together. The budgets are team acceptance thresholds, not promises about platform capability or business outcomes.

1. Classify the requirement before choosing a file

1.1 Describe the visible user result

Begin with what a visitor sees on a specific page, then select the implementation layer. A product-card price presentation, a collection filter entry point, or a cart empty state usually belongs to theme presentation. A cross-page business rule, sensitive data access, asynchronous synchronization, or checkout restriction needs an app, function, or service boundary. Putting every problem into Liquid makes templates carry untestable responsibilities and lets a visual change create an unexplained business change.

A useful requirement record includes the page type, data source, interaction, no-script result, language and market conditions, acceptance evidence, and recovery action. Do not write “make this component more flexible.” State which fields a merchant can configure in the theme editor, what a visitor sees when JavaScript is unavailable, what happens when a field is empty, and which data change the theme must not own.

Requirement signalPreferred layerVisible theme resultKeep outside the theme
Change layout, copy, or display orderTheme section, block, or snippetConfigurable area, stable empty state, responsive HTMLOrder writes and cross-system sync
Read a Shopify product or collection objectLiquid and template contextObject-driven output with explicit defaultsHard-coded stock or prices
Let a merchant compose modulesJSON template, schema, and sectionAddable, removable, sortable modulesTreating editor settings as permissions
Inject an app-owned interfaceTheme App ExtensionAddressable app block and settingsCopying private app files into the theme
Change checkout rules or calculate business resultsApp, Function, or service layerTheme presents a confirmed resultFaking price, discount, or eligibility in Liquid
Retry, audit, or access sensitive cross-system dataBackend integration with separate credentialsTheme presents safe result or errorExposing tokens, queues, or private data in the browser

1.2 Write one boundary decision for each request

The boundary record needs an owner and a counterexample. A “member label” can be presentation when it reads an authorized value already in the page context; it needs a different boundary when it requires a live query against private customer information. Each request should have one primary implementation layer. Other layers should be documented as inputs or outputs, rather than giving both a theme and an app ownership of the same fact.

During review, answer four questions line by line: Who owns this field? When does it change? What is displayed for an empty or failed value? Who can correct a mismatch? If the answer depends on an undefined global variable, the request is not ready for implementation. Keep the decision and its acceptance examples beside the code version so a later upgrade can distinguish a code change from a changed business assumption.

2. Establish a readable repository contract

2.1 Give each directory one responsibility

Directory names are collaboration interfaces. layout owns the page shell, global head output, and global resource entry. templates owns page-type composition. sections owns modules that a template or editor can place. snippets owns reusable output fragments that are not standalone layouts. assets owns CSS, JavaScript, and media references. config owns theme settings and defaults. With this separation, a reviewer can infer impact from a path; with a blurred structure, every change requires runtime guesswork.

Do not make one giant section represent the entire home page, and do not put every rule into one snippet. Organize files around one user task or one display contract. A section can compose several blocks, but each block should have one settings shape and an explicit empty state. Templates should compose rather than quietly mutate an object or perform an app synchronization job.

Directory or objectAppropriate responsibilityReview evidenceCommon boundary failure
layoutHTML shell, global head, global resource entrySource output and resource listPut a page-only query in the global shell
templatesPage type and section orderJSON structure and route examplesCopy component logic into every template
sectionsConfigurable modules and block containersSchema, empty state, mobile sampleDepend on invisible global state
snippetsSmall reusable output fragmentsInputs, output HTML, call sitesHide side effects inside a fragment
assetsStyles and scripts required by a pageLoad condition, failure fallback, size recordLoad every script on every page
configSetting definitions and defaultsTypes, defaults, migration noteStore secrets or order facts in settings
App extension directoryApp-owned extension resourcesExtension config, install, removal testEdit extension resources as theme files

2.2 Make names searchable interfaces

Names should describe a page role or action, such as product-card, cart-line, or media-gallery, rather than new-final-v2. A variable name should tell the reviewer whether it is an object, a setting, a derived display value, or a state. Keep loop variables local to the loop and document inputs when passing values across snippets.

The repository also needs a short README describing the local preview entry point, the source of theme settings, the versions of the checking tools, app-owned directories, and changes that need design or content review. A README does not replace code comments; it lets a new contributor reproduce a page without access to real customer data.

3. Liquid: presentation, filtering, and data boundaries

3.1 Confirm the object before reading fields

Liquid composes Shopify objects that are available in the page context. First confirm the object type, whether a field can be absent, and whether a template or market changes its availability. Do not use a product title as a stable ID, build a URL from a title, or hard-code a country or language in a branch. When displaying a price, preserve the currency context supplied by the platform rather than simulating conversion with string replacement.

A resilient fragment treats “no object,” “field unavailable,” and “empty field” as separate states and gives each a readable fallback. A product card with no image can still show its title, price, and action link. A recommendation module with no result can collapse without leaving an empty container. This also makes no-script acceptance possible because navigation and primary information do not depend on one asynchronous request.

Data conditionSafe Liquid actionVisitor fallbackEvidence to retain
Root object is missingCheck existence firstHide the dependent module and keep page structureRoute, template, fixture
Field is unavailableUse explicit default copy or omit the fieldExplain that information is unavailableField list and screenshot
List is emptyRender an empty state or omit the containerOffer a useful next navigationEmpty-list fixture
Market or language differsRead context or configured resultPreserve local format and directionMarket, language, direction set
Business value needs calculationDisplay only a confirmed input resultExplain that confirmation is pendingCalculation owner and source

3.2 Bound loops, filters, and side effects

List the small field set required by the page before writing a loop. Unbounded nested loops, repeated filters, and duplicated calculations across snippets make rendering cost hard to explain. Let an upstream object or service own sorting, pagination, and stable identity when it supports those operations; keep the theme transformation light and visible.

Liquid output should not perform invisible writes or own retries. When JavaScript adds interaction, let HTML provide an initial usable state and let the script enhance it. If a script fails, is blocked, or arrives late, a visitor should still browse, select, and receive an error explanation. This boundary also limits the blast radius when an app script conflicts with theme behavior.

4. JSON templates, sections, blocks, and schema

4.1 Treat editor settings as a typed contract

A schema is a settings table that a merchant can understand, not a random list of variables. Each setting needs a label, default, allowed range or options, empty behavior, and a representative page sample. A block setting should describe that block’s content and display behavior; a section setting should describe the container. Once a setting name, default, or type is used, maintain it as a compatibility interface.

JSON templates declare which sections form a page and how their settings are composed. Validate a minimal template first, then combine more complex layouts. When several sections depend on the same resource, check for duplicate loading. When a section is removed, confirm that navigation, the main heading, and forms still have a usable path.

Design itemAcceptable contractAcceptance questionFailure fallback
Setting typeMatches the input purpose and renders its defaultIs empty, long, or special-character input safe?Hide optional decoration and retain text
Block countHas a reasonable limit or clear reorder behaviorDo first, last, single, and many blocks behave?Use a simplified layout
Section orderDeclared explicitly by the JSON templateDoes moving one section break heading order?Return to the minimal template
Optional mediaHas dimensions, alt text, and missing stateDoes failure still leave useful information?Text and reserved structure
Dynamic fieldHas a traceable source and defaultCan a missing field be mistaken for a fact?Omit the field and keep the action
Settings migrationRename or removal has a noteDoes an old configuration open without an error?Use a compatible default

4.2 Keep section and block composition independent

A section is a layout container; a block is a movable content unit. A block should not assume a neighboring section exists or use DOM queries to guess a sibling’s order. When information must be shared, use a stable setting, a documented context object, or an explicit service result, and test the missing case separately.

Test the editor preview against the public theme preview. The editor may show selection outlines, temporary sample content, or unsaved settings, while the public page should show only saved configuration. Record whether each acceptance result came from the editor, a preview, or a public theme so temporary editor state is not mistaken for a visitor result.

5. App extensions and theme-file boundaries

5.1 Use an app block for app-owned interface

When an interface belongs to an app, prefer a Theme App Extension app block or app embed block, with extension configuration, so the app owns its resources and lifecycle. The theme should provide a place to render, pass only necessary settings, and keep surrounding layout usable. After an app is disabled, removed, or returns no data, the theme should hide an empty block without copying app code into the theme.

Extension settings must state who can change them, which fields are stored, and what appears when permission is missing. Do not put app tokens, private endpoints, or retry queues in theme settings. When an app needs server data, the theme should consume an authorized rendering result and handle timeout, empty result, and version mismatch with visible copy.

SituationTheme responsibilityApp extension responsibilityAcceptance and exit evidence
Place a recommendation moduleProvide an app-block layout slotFetch and render the extension resultEmpty result does not push core content
Load extension resourcesKeep an entry only where neededOwn extension assets and initializationResource list and removal page
Store merchant settingsProvide understandable container settingsMaintain its own configuration contractOld and empty setting samples
Request failsPreserve title, navigation, and primary actionAlerting, diagnosis, and retry policyFailure screen and recovery steps
App is removedHide the empty block without copied filesRemove its extension stateNo residual script or style

5.2 Diagnose app and theme conflicts

Start with a minimal reproduction: the same product, market, theme copy, and page, changing only one app or one block. Record the route, template, resource order, console errors, DOM changes, and time. If disabling an app makes the problem disappear, that does not prove the theme is innocent; check whether the theme depended on an injected global variable or selector.

The order is isolate, collect evidence, degrade, repair, and regress. Disable a disputed block or resource first and confirm that core pages remain usable; do not upgrade the theme, app, and browser script together. For style conflicts, narrow selectors and loading scope. For script conflicts, prevent duplicate initialization and unchecked globals. For data conflicts, return to one source of truth and pause uncertain output.

6. CLI and development-theme workflow

6.1 Work on a development theme first

Start each task on a separate development theme. Keep page samples, settings snapshots, test products, markets, and languages there; it is temporary and hidden rather than a durable unpublished theme, and it is not a second long-lived business system. When a version needs durable review, push it to an unpublished theme with a documented access boundary. At the beginning record the theme name, baseline version, owner, and access scope. At the end save the preview address, checking output, and difference summary.

Use Shopify CLI according to the current official documentation and the installed team version. Use the documented theme development, checking, push, and release actions and do not embed unverified flags in scripts. Confirm the target theme before any action that can change shared state, and save the terminal result afterwards. A shared-theme action requires a review record and recovery preparation.

StageCLI or platform actionEvidence to retainStop condition
Create isolationConnect and select a development themeTheme identifier, operator, start timeTarget or permission is unclear
Local iterationStart a theme preview and change a small scopePreview address, route, differencePreview is not reproducible
Static checkRun Theme CheckOutput, tool version, locationsUnexplained error or warning
Preview acceptanceTest representative devices and languagesScreenshots, keyboard path, consoleCore action or no-script state fails
Shared reviewPush to a dedicated review spaceDifference summary and reviewNo recovery version or owner
Release actionRelease the approved versionVersion name, time, resultUnaudited difference appears

6.2 Make preview data repeatable

Fixtures should not depend on one contributor’s private draft. Prepare at least one product with media and variants, an empty collection, a multilingual page, an app-block failure, and a path with JavaScript unavailable. Fixtures reproduce interfaces and must not contain real customer information, access tokens, or private order details.

Walk the preview by page type rather than clicking files one at a time. Record routes for home, collection, product, search, cart, content, and error states. When a failure appears, retain the minimum reproduction steps and change one variable at a time. Reviewers can then tell whether a fix changed the original failure model.

7. Preview and acceptance matrix

7.1 Cover pages, states, and direction

Acceptance is not “the home page looks fine.” The matrix should cover page type, data state, language direction, input device, and app switch. Put fragile combinations first: a product without media, an empty collection, a long title, a right-to-left language, a narrow keyboard path, blocked scripts, and an app returning no result. Each row needs an expectation, actual result, evidence, and recovery action.

DimensionRepresentative valuesObservePassing evidence
PageHome, collection, product, search, cart, contentTemplate, heading order, primary actionRoute and screenshot
DataNormal, empty, missing, long textDefaults, truncation, wrappingFixture and result
LanguageChinese, English, right-to-left languageCopy, direction, number and dateLanguage setting and screenshot
DeviceNarrow, desktop, touch, keyboardOverflow, focus, touch targetViewport record
ScriptNormal, delayed, failed, disabledCore content and action availabilityNo-script image and log
AppEnabled, empty, timeout, removedBlock space, conflict, residueBefore and after switch

7.2 Use a reproducible acceptance order

Confirm the URL and template first, then the page heading and navigation, then content, forms, media, and interaction, and finally resources, console output, and error logs. Change one variable at a time. If editor settings, app configuration, and test product change together, the outcome cannot be attributed.

Use three results: pass, conditional pass, and blocking. A conditional result must name the affected page, owner, due time, and recovery action; “optimise later” is not evidence. Blocking signals include broken links, a form that cannot submit, an incomplete keyboard path, mixed-language copy, exposed sensitive information, failed theme checks, or an unexplained resource spike.

8. Theme Check and continuous checks

8.1 Turn static warnings into actionable work

Theme Check is valuable because it locates risks in Liquid, schema, templates, and theme conventions down to a file and line. Save its version and complete output rather than a green summary. For each warning state whether it is a real defect, an accepted exception, or a structure to rewrite; an exception needs an owner and a review condition.

Static checks do not replace browser and representative-data acceptance. A template can be syntactically clean and still fail on an empty object, a right-to-left language, long copy, or app removal. CI should keep static checks, fixture rendering, link checks, and the human matrix as adjacent but separate gates so a failure identifies the layer that needs recovery.

Check gateTriggerWhat it checksFailure action
Format and syntaxEvery changeLiquid, JSON, and schema structureRepair and rerun
Theme CheckEvery review versionRules, deprecated-risk signals, locationsExplain or pause review
Fixture renderingWhen a theme version is formedPage states, empty values, error statesKeep the last usable version
Browser regressionBefore shared previewConsole, keyboard, responsive behaviorIsolate the failed page
Links and metadataBefore release reviewURL, title, language, structureBlock the release action
Post-release observationAfter releaseKey pages and error signalsApply the recovery decision

8.2 Avoid a false green CI result

CI fixtures should use fixed inputs and an explicit version, not a personal theme, external network, or temporary app response. Compare readable differences in HTML structure, links, resources, and key text instead of only one total hash. When a difference appears, the reviewer should see whether it is an expected heading change or an accidental removal of a form, focus path, or alt attribute.

When the checking tool changes, run old and new rules in an isolated change, list new warnings, and schedule code changes separately. Do not put a tool upgrade and a theme refactor into one inseparable batch. If the result fails, you can then restore the rule version or the theme difference without guessing.

9. Git, review, and version evidence

9.1 Make each commit explain one change

A commit message should state the page scope, user result, data dependency, and recovery path. Separate spacing, Liquid data correction, app-block integration, and asset splitting when possible so a reviewer can read by risk. Do not format the entire repository as a side effect, and do not mix generated large differences with hand-authored code.

The review description should list changed files, unchanged boundaries, test routes, language and device combinations, known limits, and the action that restores the last usable version. A screenshot proves one view, not a state matrix. A command output proves one run, not a failure recovery. A theme version name or tag should map to the review record one to one.

Review questionEvidence requiredPassing standardRejection signal
Which pages are affected?File-to-template mappingScope is enumerable“The whole store should be fine”
What data is read?Object and field listSource, default, owner clearHard-coded business fact
Which apps are involved?App block and resource listCan disable, remove, and degradeCopied app code
Is it accessible?Keyboard, focus, label evidenceCore path can completeMouse-only demonstration
Can it be restored?Version, difference, and stepsAction is executableNo known-good version
Which facts are uncertain?Limit and review noteUncertainty is explicitAssumption written as guarantee

9.2 Protect theme settings and content

Code version and editor settings can change independently. Record the code version, JSON templates, settings snapshot, and representative pages together; a Git difference alone is incomplete. If a merchant changes a heading, media item, or section order in the editor, verify that recovery code will not overwrite it. If the settings shape changes, provide a compatibility path for old settings.

Permissions are also version evidence. Separate theme developer, reviewer, merchant, and app-owner responsibilities; grant only the scope required for the task. Remove shared-theme access when a contributor leaves, record the change, and never store personal credentials or temporary passwords in the repository.

10. Performance budgets and sustainable rendering

10.1 Use page budgets to constrain resources

A performance budget finds resource creep; it is not a fixed guarantee across networks, devices, or visitors. Record budgets by page and resource type, keep measurement conditions stable, and inspect LCP resource priority, server-rendered essential content, long tasks, media dimensions, and third-party scripts together. One over-budget result needs an explanation, scope, and action rather than an unexplained score.

Page resourceTeam acceptance budgetMeasurementAction when exceeded
Critical HTMLNo more than 170 KB compressedRecord response and routeRemove duplicate fragments and retest
Critical CSSNo more than 24 KBRecord critical style entrySplit non-critical styles
Initial theme JavaScriptNo more than 120 KBRecord the page load setDefer non-critical interaction
Main above-fold mediaNo more than 220 KBRecord source, size, and formatChange size or defer loading
Third-party scriptsNo more than 3 entry points by defaultRecord owner and execution timeDisable, defer, or consolidate
Font requestsNo more than 2 initiallyRecord use and fallbackUse system font or fewer variants
Long taskNo single task over 50 msRecord browser performanceSplit initialization and listeners

10.2 Make every budget change explainable

For each resource answer which page needs it, who owns it, and how it degrades when disabled. Essential content should be server-rendered instead of waiting for a script to assemble the title, price, navigation, or form. Do not lazy-load the genuine LCP resource by default; when it is truly the above-fold resource, use an appropriate fetchpriority hint only after measuring the page. Do not load an app script across the store because one app requests it; let an app block create an entry only on pages where it is placed, and check that the element exists before initialization. Avoid one large bundle for every page and do not make unused polyfills default dependencies.

Media needs dimensions, alt text, and a loading decision. Decorative backgrounds must not block primary content. A product hero image should reserve layout space so a late response does not push an action away. Caching and a CDN can assist transfer, but they do not replace source dimensions, format, or scope review. When a page is slow, identify the resource and page instead of hiding a long-tail problem in one aggregate score.

11. Accessibility and internationalisation acceptance

11.1 Walk the core task with keyboard and screen reader

Core tasks include opening navigation, reaching main content, selecting a variant, adding to cart, changing quantity, closing a dialog, and reading an error. Start each task with a keyboard. Focus should follow the visual sequence, remain visible, and not be covered by a sticky element. Buttons, links, inputs, and dialogs need correct semantics and names; do not attach a click handler to a decorative element with no interaction semantics.

With a screen reader, verify the page title, landmark names, form labels, error association, and status changes. Dynamic updates need an appropriate announcement without interrupting every decorative change. Hidden content must be truly unreachable; when a panel opens, focus should go somewhere predictable, and when it closes, focus should return to the trigger. Alt text should describe an image’s purpose rather than its filename.

Test itemChinese page sampleEnglish or right-to-left samplePassing evidence
Page title and languageTitle, lang, spoken ChineseEnglish title, language, directionDOM and reading record
Main navigationKeyboard expansion and skip linkSame path with translated labelsFocus path
Product selectionVariant name, state, errorVariant names and state changesForm recording and DOM
Cart actionQuantity, remove, empty stateQuantity, remove, empty stateCompleted keyboard path
DialogOpen, focus boundary, returnSame behavior with longer labelsBefore and after focus
Image and iconPurposeful alt, decorative hidingLocalized purpose textAttribute check
Dynamic errorField association and announcementError text in the localeScreen-reader output

11.2 Treat translation and direction as layout inputs

Translation is not a token-for-token replacement. English may be longer, right-to-left languages change arrows and spacing, and numbers, dates, currencies, and product names have their own formatting. Put buttons, labels, errors, empty states, and alt text in the language inventory; do not hide Chinese-only strings in JavaScript or images.

Use long copy, a missing translation, mixed numerals, and a right-to-left direction during acceptance. Check navigation overflow, currency placement, icon direction, and focus order. When a translation is missing, use an explicit fallback language and record it; never expose a translation key. If an app block does not support the current language, retain the layout and explanation instead of exposing a script error.

12. SEO, structured information, and semantic HTML

12.1 Let the template own page semantics

Each page type should define its title, description, canonical, language association, and one primary heading. The theme should emit one semantic structure; layout, section, and app should not each emit a copy of the same head information. Product structured information may use only visible, traceable fields. If a value is missing, omit or degrade it rather than inventing a price, stock state, review, or eligibility.

Semantic HTML improves accessibility and maintenance. Use navigation elements for navigation, a clear main region, buttons for actions, links for location changes, and labeled form controls. Do not use heading tags only for larger type or skip levels for visual effect. Perform SEO acceptance with page states, languages, and app switches because duplicate head output, a wrong language, and residual resources often arise from composition.

Page typeCheckCommon errorFallback
HomeMain heading, description, navigation regionsSections repeat the headingKeep one page heading
CollectionCollection name, pagination, filter semanticsEmpty collection emits a bad linkRender useful empty navigation
ProductProduct fields, media, variant stateHidden value enters structured dataOmit the missing field
SearchQuery state, result count, empty stateQuery becomes a generic page titleUse safe query semantics
ContentHeading order, author region, linksApp duplicates the canonicalLet the page template own it

12.2 Check multilingual URLs and navigation

Language paths and link labels should match the page language. Do not add an English service entry to a Chinese page merely to fill a link count, and do not disguise a language switcher as an ordinary content link. Check URL, canonical, and language associations for every locale path; language versions should reference one another instead of sending every locale to a default page.

For related reading, see Shopify theme architecture and operations, Shopify Liquid theme customisation, Theme App Extension governance, Shopify performance engineering, and Shopify multilingual localisation. These pages cover operations, Liquid structure, extension boundaries, performance diagnosis, and localisation inputs; they do not replace this theme lifecycle guide.

13. Release checklist and responsibility matrix

13.1 Complete the release checklist item by item

Execute the checklist in order and attach evidence to every row. Confirm the version and target theme first, then inspect static output, templates, and schema, then run the page matrix, keyboard and screen-reader checks, performance budgets, language checks, and app-conflict tests. Finally save approval, the version name, and the last restorable version. A checklist is not complete when its evidence is missing.

OrderCheckEvidenceBlocking condition
1Target theme and versionName, version, operatorTarget or scope unclear
2Difference and directory boundaryFile list and affected pagesUnaudited file appears
3Liquid and schemaTheme Check outputUnexplained error
4Templates and empty statesPage matrix and fixturesEmpty object breaks core path
5App extensionsEnable, disable, removal recordsResidual or global script
6AccessibilityKeyboard, reading, error stateCore task cannot complete
7Performance budgetResource and long-task recordOver budget without action
8Language and semanticsLocale path, headings, labelsMixed copy or wrong direction
9Recovery preparationLast usable version and stepsCannot restore safely
10Approval and observationReview, time, ownerNo accountable role

13.2 Make ownership traceable with RACI

RACI should not make everyone “jointly accountable.” Give each activity one A. R roles execute, C roles provide input, and I roles need the result. For an app extension, the app owner is responsible for its configuration and failure path; the theme owner is responsible for layout degradation, resource scope, and semantic HTML. The merchant approves the business result and content, not the internal diagnosis.

ActivityTheme developerTechnical reviewerMerchant or content ownerApp ownerOn-call lead
Requirement boundary and page samplesRCACI
Liquid, schema, and templatesRACII
App-block configurationCCARI
Accessibility and language matrixRACCI
Performance budget and resource listRAICI
Version approval and releaseRCACI
Incident isolation and recoveryCCIRA
Follow-up record and repairRAICC

14. Version control, upgrades, and compatibility

14.1 Build a compatibility table before an upgrade

List theme code, JSON templates, setting keys, app blocks, script entries, language resources, and manual editor changes before upgrading. Mark each item as safe to carry unchanged, requiring conversion, requiring new acceptance, or ready for removal. Do not overwrite the old theme with new files and do not treat “the editor opens” as the only compatibility test.

AssetBefore-upgrade checkAfter-upgrade validationFailure action
JSON templateSection names and orderPage type retains primary pathRestore old template and convert
Schema settingKey, type, defaultEditor opens and savesUse a compatible default
Liquid snippetInput object and empty stateNormal and missing-field fixturesIsolate the fragment and recover
App blockExtension configuration and assetsEnable, disable, and removeDisable the block, retain layout
CSS and scriptEntry, selector, initialisationConsole and budget checksSplit or defer the resource
Language resourceKey and fallback languageLong copy and directionRestore the old translation path
Merchant contentSettings snapshot and mediaEditor and public previewRestore the settings snapshot

14.2 Record facts that code cannot represent

Theme code does not represent every store state. Record app versions, editor settings, representative products and markets, permission changes, and known exceptions so reviewers know what recovery restores and what it does not. Do not claim code recovery can undo later merchant edits, and do not use a timestamp as proof that one version is trustworthy.

Run a small page test after an upgrade, then expand to the full matrix. If only one page fails, keep the last usable version and the minimal failure fixture. If navigation, cart, form, language, or sensitive information fails, stop expanding the scope and return to the last usable version first.

15. Failure case: an app script breaks theme navigation

15.1 Reproduce and preserve evidence

A cross-border store added a recommendation app block. On a narrow viewport the menu close control lost focus, so a keyboard user could not return to the trigger. The app script also initialised on every page even though the block existed only on the product page. The home page looked normal, while the product page logged a duplicate-initialisation warning. “One browser is incompatible” is not a sufficient diagnosis; preserve the same theme copy, language, product, and app switch as a comparison.

Reproduce it by opening navigation in a development theme without the block, then adding the block only to the product page. Use a keyboard to open the menu, trigger the recommendation, close the menu, and open it again. Repeat with long English copy and a right-to-left test language, then disable the app entry. Record the focused element, resource order, DOM attributes, console output, and screenshot for each step. This separates theme focus handling, app initialisation, and language layout.

15.2 Recover and repair in a bounded order

Disable the app block first and confirm that navigation, cart, and product selection remain usable. Keep the last usable theme version as the safety reference. Repair focus within the menu so it returns to the trigger, and let app initialisation check for the block before running. Scope the resource entry to pages that need it. The app owner handles recommendation timeout and empty results; the theme owner handles empty-block layout and keyboard degradation; the on-call lead decides when the block can return.

16. Failure timeline and recovery boundary

16.1 Separate observation, action, and decision

A timeline should separate what was observed, what was done, and what was decided. Do not write a later hypothesis as if it was known at the time. Record one event per row with the theme version and app switch. After recovery, retain the failed difference as read-only regression evidence rather than making it the default version again.

TimeEventSignal observedDecision and evidence
09:00Create development themePage matrix passesSave version and fixtures
09:25Add product-page app blockProduct resource set growsRecord difference and owner
09:40Begin keyboard regressionFocus disappears after closeMark blocking and capture screen
09:55Disable app blockNavigation returnsIsolation is proven, root cause is not
10:10Inspect initialisation entryEvery page has an entryPrepare page-scoped repair
10:35Repair focus and resource conditionCore path passesSave Theme Check and matrix
11:00Retest language and empty resultNo residual resource, stable focusApp owner reviews
11:25Review restorable versionLast usable version remainsOn-call lead chooses scope

16.2 Use the recovery decision table

Recovery restores usable pages; it does not erase evidence. First judge the failure scope and data boundary, then choose to disable a block, restore a resource, restore a theme version, or pause an app. Be conservative when navigation, cart, forms, privacy, or language paths are affected; isolate an optional decoration when the core path is safe.

SignalScopeImmediate actionCondition before repair continues
Decorative style is misalignedOne optional sectionDisable the section or restore styleCore path and focus pass
App block is emptyOne product moduleDisable the block and retain product factsEmpty state is explained
Menu or cart keyboard path failsSeveral core pagesRestore the last usable theme versionKeyboard steps pass again
Language path is mixedOne or more marketsPause language change and restore linksCanonical and language matrix pass
Sensitive field is visibleAny public pageRemove output and isolate the versionPermission and source review pass
Resource delays primary contentAffected page setDefer or disable the third-party entryBudget and error log are clear
Theme check cannot be explainedChanged versionDo not widen the scopeRule, version, and difference reproduce

17. Maintenance rhythm and an executable runbook

17.1 Maintain in small, observable steps

Choose one page family and one observable result for each maintenance task. Save a baseline, change the smallest scope, and run the same matrix. Changing templates, apps, translation, and assets together makes attribution impossible. Before removing a snippet, search call sites and setting keys and write a migration note. Before removing an app block, confirm that removal leaves no script, style, or empty container.

The runbook should state the first diagnostic step, isolation switch, last usable version, evidence location, notification roles, and post-recovery checks. It is for on-call readers and should not require deep Liquid expertise. After an incident, update the runbook and fixture so a similar problem appears earlier in the next check.

17.2 Reconfirm changing facts on a regular cadence

Shopify platform behavior, theme tools, app-extension configuration, and browser behavior change. Regularly recheck the official documentation links, installed CLI version, Theme Check rules, extension resources, language inventory, and performance budgets. When a fact depends on version or region, record the review date and owner. Never treat an old command, field, or screenshot as a permanent contract.

Keep maintenance results in three states: continue using, review required, and isolate. Preserve evidence for each state. This guide helps a team build checks, but the actual theme version still needs validation in its own development theme, representative data, and permitted scope. Related performance, Liquid, and extension boundaries are covered by companion topics on theme operations, Liquid architecture, and Theme App Extension governance.

Frequently asked questions

FAQ 1: Should Shopify theme development start with Liquid or a JSON template?

Start with the user task, page type, and data boundary, then choose the order of Liquid and JSON work. If page composition and editor capability are undefined, writing Liquid first hardens the wrong responsibility. If objects, empty states, and output semantics are clear, build a minimal section first, then compose it with a JSON template and acceptance fixtures.

FAQ 2: Can an app block be copied directly into theme files?

No. An app extension owns its configuration and lifecycle. The theme should provide a placement, layout degradation, and necessary settings, not copy private app code. Test enable, empty result, timeout, disable, and removal; residual scripts or styles need the app owner to correct the extension boundary.

FAQ 3: Must the theme be fully usable without JavaScript?

Core reading, navigation, form semantics, and primary actions should have an understandable base state, with an error explanation or alternative path when enhancement fails. A dynamic recommendation may not be reproducible without a script, but product facts, primary links, and focus access must not disappear. Keep the no-script page as its own matrix fixture.

FAQ 4: Is a performance budget a guarantee of Shopify page speed?

No. A budget is a team threshold measured under fixed conditions to control resources and long tasks. It cannot guarantee behavior on every network, device, app response, or visitor context. Record page, data, device, resource, and measurement time; when a budget is exceeded, identify the resource, choose split, defer, replace, or isolate, and retest under the same conditions.

FAQ 5: When should the last usable theme version be restored?

Restore it when core navigation, cart, form, keyboard path, language URL, sensitive information, or primary content across many pages is affected and a safe isolated module cannot be proven quickly. Save the failed difference and logs first. After recovery, retest representative pages, app switches, languages, and settings to confirm the fault is gone without creating a new empty state.

First-party source register

Use these sources to verify theme architecture, CLI, Theme Check, performance, app extensions, accessibility, and version control. Documentation behavior, fields, rules, and tool output can change with versions; verify them in the development theme and scope used for the work.