StoreConnect debugging and performance
Measuring and fixing StoreConnect Liquid at runtime: what to instrument, what the web Console
reports, which fix matches which measured cost, and how to key a cached fragment so it stays
correct.
Three symptoms look similar and have different causes: a template that renders blank, one
that renders the wrong data, and one that is slow. Identify which you have before you
change anything, because the diagnostic path differs for each.
Rules that apply before you touch anything
Never print these anywhere, in any tag, comment, attribute, or log: request parameters,
headers, session state, cart contents, customer or contact records, credentials, tokens,
payment values, or authorization data. Print sizes, counts, booleans, and ids you already
display. HTML comments and data- attributes are visible to anyone who views source.
Never pass a visitor-supplied string as a tag option. String option values are re-rendered
as Liquid, so a value containing {{ … }} or {% … %} is evaluated. This applies to
{% debug %}, {% cache %}, {% render %}, {% require %}, {% component %}, and every
other tag option. Pass a count or a boolean derived from the value instead.
Never build a page-visible debug mode. No ?debug=1 branch, no page dump, no hidden
diagnostic block, no "print everything" fallback. If Console access is unavailable, reproduce
on a non-production store or a theme preview with test records.
Never publish a {% timer %}. With no active Console session the tag renders nothing and
the body it wraps is silently dropped. A timer left around a price or an add-to-cart
control removes it for every visitor while looking correct to whoever still has the Console
open. Remove every {% debug %} and {% timer %} before publishing, then reload the page with
the Console closed and confirm nothing has disappeared.
Never expose introspection output. record_fields, record_relationships, and
record_name are for your own session; their output describes the object's shape.
Every axis that changes a cached fragment's output must appear in items:. Otherwise the
first visitor's markup is served to everyone whose key matches. Cross-customer disclosure
through an incomplete cache key is the most damaging mistake in this skill's scope.
Cache only stable, public, read-only markup. Never cache a {% form %}, a {% component %}
container, checkout or payment content, totals or balances, customer-specific output, or anything
derived from the visitor or request. A violation can reject an interaction, expose one visitor's
output to another, or leave a component unable to update. Do not infer permission from an existing
cache block. Move unsafe content outside it and follow references/caching.md.
Never cache before measuring. Caching a page that is slow because of repeated work hides
the cost for one visitor and leaves it for the next miss, and a wrong key turns a performance
problem into a correctness problem.
Write boundary. This skill diagnoses and edits theme templates. It creates and updates no
store, catalog, or Salesforce records. A template change still ships through the normal review,
publish, and supported cache-refresh path — see storeconnect-sync-deploy.
Triage: symptom, first move, reference
| Symptom |
First move |
Then read |
| Template renders blank, or one value is missing |
Open Error entries in the Console. A missing template and a misspelled attribute both render empty and log an error there. |
references/diagnosis.md — the blank-output path |
| A loop over a collection renders nothing |
Check whether the collection is paginated. Paginated collections render nothing outside {% paginate %}, and .any? on one is always false. |
references/performance.md — collections |
| Template shows plausible but wrong data |
Print the id of the record you believe you are rendering, and confirm store and customer scope. |
references/diagnosis.md — the wrong-data path |
| Right for one visitor, wrong or stale for another |
Suspect an unsafe cache boundary or incomplete public variation inputs. Test the storefront URL directly in separate clean sessions. |
references/caching.md — Review every variation axis |
| Output did not change after a theme deploy |
Do not re-deploy. The supported deploy refresh does not guarantee immediate expiry of an explicit {% cache %} fragment. |
references/caching.md — what actually invalidates a fragment |
| A form submit is rejected, or a component stopped refreshing, on a page you cache |
Check whether the form or component is inside the {% cache %} block. Forms and component containers must not be cached because their correctness can vary by visitor or request. |
references/caching.md — Safety boundary |
| Page or region is slow |
Switch the Console to Slowest entries to find the template with the highest self duration. Do not add a cache yet. |
references/diagnosis.md for the measurement order, then references/performance.md for the fix |
| You know the template but not the line |
Read the per-filter timings on that entry, then bisect with {% timer %}. |
references/diagnosis.md — timer tag |
| Deciding whether to cache a region, or which items to key it on |
Confirm the output is shareable at all, then design the key. |
references/caching.md |
| A discount, promotion, or controller behaves as if the Liquid never ran |
These surfaces swallow errors; a dynamic discount rescues to 0. |
references/diagnosis.md — Liquid that runs outside a page render |
| Server time is fine, the page still feels slow |
Look at asset placement and per-row client-side work. |
references/performance.md — asset weight, client-side cost |
| You need a Map, List, or index built in Liquid |
Use a Map as a lookup index rather than a nested scan. |
references/performance.md — Maps and Lists |
The measurement loop
Always in this order. Skipping step 1 or step 6 is how a "fix" ships that changes nothing.
- Reproduce on the target store, at the real URL, with realistic data volume and the
relevant visitor state (anonymous, signed in, cart populated). A page that is fast on three
products proves nothing.
- Classify the symptom: blank, wrong, or slow. Each has its own path.
- Attribute the cost or the failure to one template using the Console entry tree, before
forming a theory. Self duration names the template doing the work; total duration only names
its parent.
- Instrument narrowly. One
{% timer %} around the widest suspect region, then halve.
{% debug %} for a count or a branch marker. Never scatter tags across a theme.
- Apply the one fix that matches the measured cost class, from
references/performance.md.
- Re-measure the same URL in the same state and record the before and after numbers. If
you cannot state the saving, you have not finished.
- Remove all instrumentation and verify the page again with the Console closed.
Cost order, highest first
When you have no measurement yet, this is the order in which StoreConnect templates usually
lose time. It guides where to look, not what to conclude.
- A
{% query %} inside a loop, or any repeated per-row database access.
- A whole-collection load:
| depaginate, .size on a query result, a missing
{% paginate %}.
- An unscoped or unnarrowed
{% query %} against a high-volume object.
- Liquid on a per-item hot path, such as a dynamic discount field evaluated once per cart item.
- A slow region that blocks the first paint but could be a deferred component.
- Repeated rendering of a fragment that is identical for many visitors and is not cached.
- Assets loaded on pages that do not use them.
- Per-row client-side lookups that could be batched.
Before you publish
- No
{% debug %}, no {% timer %}, no force: true anywhere in the change.
- Reload every affected page with the Console closed. Nothing disappeared.
- Every
{% cache %} block you added or changed: no form, no component, no money inside; and
the output follows each axis in its key when you change that axis.
- States tested: anonymous, signed in, empty collection, empty cart, and a second page of any
paginated collection you touched.
- Deploy through the reviewed publish and supported cache-refresh path, then verify on the live URL.
Related skills
storeconnect-liquid — references/runtime-gotchas.md is the symptom-to-cause catalog for
Liquid failures generally; references/query-tag.md covers query cost, scope, and the
distance struct; references/drops.md is the authoritative attribute list.
storeconnect-components — references/component-reload.md for defer: and lazy:, the
alternative to caching a region that cannot be cached.
storeconnect-theme-development — references/assets-and-build.md for asset pipelines and
page-specific packs.
storeconnect-controllers — for logic that runs before the page renders.
storeconnect-sync-deploy — for the reviewed publish and supported
cache-refresh path.
storeconnect-pos-customization — for the supported POS client-side bridge.
1---2name: storeconnect-debug-performance-23description: Instrument and speed up StoreConnect Liquid — the web Console, the debug and timer tags, drop and record introspection, cache key design and invalidation, collection and pagination cost, per-item hot paths, asset weight, and client-side batching. Use when you need to instrument a template that renders blank or wrong, cache a fragment, or cut queries on a slow page, when cached output is stale or reaches the wrong visitor, and before adding any debug, timer, or cache tag. For an audit that changes nothing use storeconnect-theme-review.4---56# StoreConnect debugging and performance78Measuring and fixing StoreConnect Liquid at runtime: what to instrument, what the web Console9reports, which fix matches which measured cost, and how to key a cached fragment so it stays10correct.1112Three symptoms look similar and have different causes: a template that renders **blank**, one13that renders the **wrong data**, and one that is **slow**. Identify which you have before you14change anything, because the diagnostic path differs for each.1516## Rules that apply before you touch anything1718**Never print these anywhere, in any tag, comment, attribute, or log:** request parameters,19headers, session state, cart contents, customer or contact records, credentials, tokens,20payment values, or authorization data. Print sizes, counts, booleans, and ids you already21display. HTML comments and `data-` attributes are visible to anyone who views source.2223**Never pass a visitor-supplied string as a tag option.** String option values are re-rendered24as Liquid, so a value containing `{{ … }}` or `{% … %}` is evaluated. This applies to25`{% debug %}`, `{% cache %}`, `{% render %}`, `{% require %}`, `{% component %}`, and every26other tag option. Pass a count or a boolean derived from the value instead.2728**Never build a page-visible debug mode.** No `?debug=1` branch, no page dump, no hidden29diagnostic block, no "print everything" fallback. If Console access is unavailable, reproduce30on a non-production store or a theme preview with test records.3132**Never publish a `{% timer %}`.** With no active Console session the tag renders nothing and33**the body it wraps is silently dropped**. A timer left around a price or an add-to-cart34control removes it for every visitor while looking correct to whoever still has the Console35open. Remove every `{% debug %}` and `{% timer %}` before publishing, then reload the page with36the Console closed and confirm nothing has disappeared.3738**Never expose introspection output.** `record_fields`, `record_relationships`, and39`record_name` are for your own session; their output describes the object's shape.4041**Every axis that changes a cached fragment's output must appear in `items:`.** Otherwise the42first visitor's markup is served to everyone whose key matches. Cross-customer disclosure43through an incomplete cache key is the most damaging mistake in this skill's scope.4445**Cache only stable, public, read-only markup.** Never cache a `{% form %}`, a `{% component %}`46container, checkout or payment content, totals or balances, customer-specific output, or anything47derived from the visitor or request. A violation can reject an interaction, expose one visitor's48output to another, or leave a component unable to update. Do not infer permission from an existing49cache block. Move unsafe content outside it and follow [references/caching.md](references/caching.md).5051**Never cache before measuring.** Caching a page that is slow because of repeated work hides52the cost for one visitor and leaves it for the next miss, and a wrong key turns a performance53problem into a correctness problem.5455**Write boundary.** This skill diagnoses and edits theme templates. It creates and updates no56store, catalog, or Salesforce records. A template change still ships through the normal review,57publish, and supported cache-refresh path — see `storeconnect-sync-deploy`.5859## Triage: symptom, first move, reference6061| Symptom | First move | Then read |62|---|---|---|63| Template renders blank, or one value is missing | Open **Error entries** in the Console. A missing template and a misspelled attribute both render empty and log an error there. | [references/diagnosis.md](references/diagnosis.md) — the blank-output path |64| A loop over a collection renders nothing | Check whether the collection is paginated. Paginated collections render nothing outside `{% paginate %}`, and `.any?` on one is always false. | [references/performance.md](references/performance.md) — collections |65| Template shows plausible but wrong data | Print the id of the record you believe you are rendering, and confirm store and customer scope. | [references/diagnosis.md](references/diagnosis.md) — the wrong-data path |66| Right for one visitor, wrong or stale for another | Suspect an unsafe cache boundary or incomplete public variation inputs. Test the storefront URL directly in separate clean sessions. | [references/caching.md](references/caching.md) — Review every variation axis |67| Output did not change after a theme deploy | Do not re-deploy. The supported deploy refresh does not guarantee immediate expiry of an explicit `{% cache %}` fragment. | [references/caching.md](references/caching.md) — what actually invalidates a fragment |68| A form submit is rejected, or a component stopped refreshing, on a page you cache | Check whether the form or component is inside the `{% cache %}` block. Forms and component containers must not be cached because their correctness can vary by visitor or request. | [references/caching.md](references/caching.md) — Safety boundary |69| Page or region is slow | Switch the Console to **Slowest entries** to find the template with the highest self duration. Do not add a cache yet. | [references/diagnosis.md](references/diagnosis.md) for the measurement order, then [references/performance.md](references/performance.md) for the fix |70| You know the template but not the line | Read the per-filter timings on that entry, then bisect with `{% timer %}`. | [references/diagnosis.md](references/diagnosis.md) — timer tag |71| Deciding whether to cache a region, or which items to key it on | Confirm the output is shareable at all, then design the key. | [references/caching.md](references/caching.md) |72| A discount, promotion, or controller behaves as if the Liquid never ran | These surfaces swallow errors; a dynamic discount rescues to `0`. | [references/diagnosis.md](references/diagnosis.md) — Liquid that runs outside a page render |73| Server time is fine, the page still feels slow | Look at asset placement and per-row client-side work. | [references/performance.md](references/performance.md) — asset weight, client-side cost |74| You need a Map, List, or index built in Liquid | Use a Map as a lookup index rather than a nested scan. | [references/performance.md](references/performance.md) — Maps and Lists |7576## The measurement loop7778Always in this order. Skipping step 1 or step 6 is how a "fix" ships that changes nothing.79801. **Reproduce** on the target store, at the real URL, with realistic data volume and the81 relevant visitor state (anonymous, signed in, cart populated). A page that is fast on three82 products proves nothing.832. **Classify** the symptom: blank, wrong, or slow. Each has its own path.843. **Attribute** the cost or the failure to one template using the Console entry tree, before85 forming a theory. Self duration names the template doing the work; total duration only names86 its parent.874. **Instrument narrowly.** One `{% timer %}` around the widest suspect region, then halve.88 `{% debug %}` for a count or a branch marker. Never scatter tags across a theme.895. **Apply the one fix that matches the measured cost class**, from90 [references/performance.md](references/performance.md).916. **Re-measure the same URL in the same state** and record the before and after numbers. If92 you cannot state the saving, you have not finished.937. **Remove all instrumentation** and verify the page again with the Console closed.9495## Cost order, highest first9697When you have no measurement yet, this is the order in which StoreConnect templates usually98lose time. It guides where to look, not what to conclude.991001. A `{% query %}` inside a loop, or any repeated per-row database access.1012. A whole-collection load: `| depaginate`, `.size` on a query result, a missing102 `{% paginate %}`.1033. An unscoped or unnarrowed `{% query %}` against a high-volume object.1044. Liquid on a per-item hot path, such as a dynamic discount field evaluated once per cart item.1055. A slow region that blocks the first paint but could be a deferred component.1066. Repeated rendering of a fragment that is identical for many visitors and is not cached.1077. Assets loaded on pages that do not use them.1088. Per-row client-side lookups that could be batched.109110## Before you publish1111121. No `{% debug %}`, no `{% timer %}`, no `force: true` anywhere in the change.1132. Reload every affected page with the Console closed. Nothing disappeared.1143. Every `{% cache %}` block you added or changed: no form, no component, no money inside; and115 the output follows each axis in its key when you change that axis.1164. States tested: anonymous, signed in, empty collection, empty cart, and a second page of any117 paginated collection you touched.1185. Deploy through the reviewed publish and supported cache-refresh path, then verify on the live URL.119120## Related skills121122- `storeconnect-liquid` — `references/runtime-gotchas.md` is the symptom-to-cause catalog for123 Liquid failures generally; `references/query-tag.md` covers query cost, scope, and the124 distance struct; `references/drops.md` is the authoritative attribute list.125- `storeconnect-components` — `references/component-reload.md` for `defer:` and `lazy:`, the126 alternative to caching a region that cannot be cached.127- `storeconnect-theme-development` — `references/assets-and-build.md` for asset pipelines and128 page-specific packs.129- `storeconnect-controllers` — for logic that runs before the page renders.130- `storeconnect-sync-deploy` — for the reviewed publish and supported131 cache-refresh path.132- `storeconnect-pos-customization` — for the supported POS client-side bridge.