LUCENTCOMMERCEGET A FREE STORE AUDITFREE AUDIT

THEME · TECHNICAL · PERFORMANCE · 24 AUGUST 2026 · 8 MIN READ

Theme code review: what to look for

A review that produces forty comments produced nothing. Automate the nitpicks, then spend human attention on blast radius, Liquid cost and who owns the setting.

A Liquid template open beside the section it renders

Run Theme Check first so no human spends attention on something a linter finds, then review four things in order: blast radius — which templates and snippets this change can reach; Liquid cost — loops over collections, repeated lookups inside iterations, anything unpaginated; the section schema — whether a merchandiser can now break the page, and whether they can fix it; and the front end — how much JavaScript ships, whether it is deferred, and whether the thing still works when it fails. Accessibility basics and image handling are non-negotiable checks, not preferences. Everything else is a note rather than a blocker, because a review with forty comments gets skimmed and merged anyway.

IN SHORT

  • Theme Check is a linter for Liquid and JSON that Shopify describes as detecting errors and enforcing best practices; run it in CI so reviews never spend human attention on syntax, unused snippets or deprecated tags.
  • Read the blast radius before the diff: a three-line change to a snippet used on every template is a bigger review than a new section nobody renders yet.
  • Liquid performance is almost always a loop — nested iteration over collection products, or a metafield or product lookup repeated once per item.
  • JSON templates can render up to 25 sections, each with up to 50 blocks, and a theme can contain up to 1,000 JSON templates — worth knowing before designing a page-building pattern.
  • The `paginate` tag supports up to 50 items per page; an unpaginated loop over a collection is a review comment every time.
  • Ask whether a merchandiser can do this without a developer, and whether they can undo it — a setting somebody can use wrongly is still better than a change that needs a deploy.
  • Block on correctness, security, accessibility and performance. Everything else is a note, or the review stops being read.

Two different reviews with the same name

"Theme code review" means two quite different jobs and it helps to say which one you are doing.

Reviewing a change — a pull request, a feature, a fix. The question is whether this should merge. It is bounded, it has an author who can answer questions, and most of the value is in the first fifteen minutes.

Reviewing an inherited theme — you have been handed a codebase four agencies have touched and asked whether it is salvageable. The question is what it will cost to work in. That is an audit, it produces a document rather than comments, and it is answered by reading the shape of the theme rather than any particular file.

Most of what follows applies to both, but the ordering is written for the first. If you are auditing, start at the schema and settings section — how a theme is configured tells you more about its maintainability than any individual template.

Run the machine before you read anything

Theme Check is Shopify’s linter for Liquid and JSON in themes and theme app extensions. The documentation describes it as detecting errors and enforcing Liquid best practices, covering syntax errors, missing templates, unused variables and snippets, unknown and deprecated tags, performance issues and style errors. It runs on demand, in CI, or through a language server in the editor.

If it is not running in CI, that is the first review comment and it is worth more than anything else you could write that day. Every check it performs is a check a person would otherwise do badly and resentfully. Human review time is expensive and finite, and spending it on an unused snippet is a waste of the only thing in this process that can actually think.

The secondary benefit is cultural. A review where the machine has already said the boring things is a review where the human comments are all substantive, which is the difference between a process people value and one they route around.

First human question: what can this reach?

Before reading the diff line by line, work out its blast radius. The size of a change in lines has almost no relationship to its risk in a theme.

A new section file that no template currently renders is close to zero risk: it cannot affect anything until someone adds it. A three-line edit to a snippet rendered inside the header is on every page of the store, including checkout-adjacent flows, including the templates nobody has looked at since 2022. The second one deserves ten times the attention and usually gets less, because it is shorter.

The practical version: for each changed file, grep the theme for where it is rendered. A snippet included in eleven places needs the reviewer to ask what happens in the other ten contexts, and specifically whether the object it now expects is always present. product.featured_image inside a snippet that also gets rendered from a blog template is the classic failure, and it will not show up in local testing unless someone thinks to look.

Ask the same question of schema changes. Removing or renaming a setting in a section schema affects every page where a merchandiser has already configured that section, and the effect lands at publish time rather than in review.

Liquid cost: it is nearly always a loop

Liquid renders server-side, so its cost lands directly in time-to-first-byte, which means it is invisible in most front-end profiling and very visible to a customer. In our experience essentially all of it comes from three patterns.

Unpaginated iteration. A for loop over a collection’s products without pagination is a review comment every time. The paginate tag supports up to 50 items per page, and that ceiling exists for a reason. The version that hurts most is the one that works fine on the collection used in development and meets the 900-product collection in production.

Nested loops. Iterating products and then iterating each product’s variants, metafields or media multiplies quickly, and the multiplication is invisible in the diff because each loop looks reasonable on its own. When you see a second for inside the first, work out the worst-case product of the two bounds and ask whether that number is acceptable.

Lookups repeated inside iteration. Anything that resolves an object — a product by handle, a collection, a metafield on a related object — costs more inside a loop than the surrounding markup does, and it is usually hoistable. If the value does not change between iterations, assign it once before the loop.

The reviewer’s question is not "is this fast" but "what is the largest input this will ever see, and has anyone tried it". A collection with five products in the dev store proves nothing.

The schema is the part merchandisers live with

Reviewers who came from application development tend to skim section schemas as configuration boilerplate. It is the most consequential part of a theme change, because it decides whether the next request needs a developer at all.

Three questions per schema block. Is anything hardcoded that should be a setting? A heading, a link, an image, a piece of copy baked into Liquid becomes a ticket every time marketing wants it changed. Is anything a setting that should not be? Exposing structural choices — grid column counts, arbitrary padding values, colour pickers on every element — hands merchandisers enough rope to make the site inconsistent, and the resulting pages become the reason for the next redesign. Can a bad value break the page? A setting that renders into markup without a fallback will meet an empty string eventually.

The platform limits are worth knowing before designing a pattern: Shopify documents that JSON templates can render up to 25 sections, that each section can have up to 50 blocks, and that a theme can contain up to 1,000 JSON templates. Most stores never approach these, but a page-building approach that treats every element as a block can find the 50 quite quickly, and a per-product template strategy needs the 1,000 in mind.

The useful framing for the author: name the person who will use this setting, and describe what they will do with it on a Tuesday. If the answer is vague, the setting is speculative and probably should not exist.

The front end: what ships, when, and what happens when it fails

Four checks, in descending order of how often they find something.

How much JavaScript does this add, and is it deferred? Anything blocking parse needs a reason. A feature that ships a library to do something CSS can do is worth a conversation, and that conversation goes better in review than after launch.

Does it survive the section being re-rendered? The theme editor re-renders sections without a full page load. Code that initialises on DOMContentLoaded only will appear broken to the merchandiser configuring it, which generates a bug report that is really a missing event listener.

Does the core function work without JavaScript, or degrade honestly? Not everything can, but navigation, add-to-cart and form submission generally should, and a review is the right moment to ask. The failure mode to hunt is the one that renders a control which silently does nothing when its script fails to load.

Images. Are dimensions requested explicitly rather than served at full size, is there a width and height attribute so the layout does not shift, and does the above-the-fold image load eagerly while everything below it is lazy? Image handling is the highest-leverage performance work in almost every theme and the easiest thing to get wrong in a hurry.

The things that are not preferences

A short list where "that is a matter of taste" is not an acceptable response in review.

  • Interactive elements must be reachable and operable by keyboard, with a visible focus state. A div with a click handler is a bug, not a style choice.
  • Form inputs need real labels. Placeholder text is not a label and disappears exactly when the user needs it.
  • One h1 per page and headings that descend in order, because this is both an accessibility requirement and the structure every extraction system reads.
  • Any user-supplied or merchant-supplied value rendered into markup needs the escaping considered. Liquid escapes by default; the review question is wherever someone has turned that off.
  • Alt text that is either meaningful or explicitly empty for decorative images — never the filename.
  • No secret, token or private API key in theme code. It is public by definition, and this is worth stating in a review checklist precisely because it keeps happening.

What not to comment on

The failure mode of theme review is not missing a bug. It is producing so many comments that the author triages them, fixes the easy ones, and merges. Forty comments and three of them mattered is a worse outcome than three comments, because nobody knows which three.

So: do not comment on formatting a linter could enforce — configure the linter instead. Do not rewrite working code in your own idiom. Do not ask for an abstraction because a pattern appears twice; ask on the third. Do not relitigate a decision made in a previous review unless something has changed.

And separate blocking from non-blocking explicitly. We mark comments as one or the other, and hold ourselves to a small number of blockers: correctness, security, accessibility, performance with a plausible production input, and anything that cannot be changed later without a migration. Everything else is a note the author may take or leave. A review whose blockers are few and unambiguous gets acted on; one that treats every observation equally gets skimmed.

The questions worth asking the author

Most of the value in a review comes from five questions rather than from reading every line. They surface the things a diff cannot show.

  • What is the largest realistic input this handles, and have you run it against that?
  • Which other templates render this file, and what did you check in each?
  • What does a merchandiser do with this, and what happens if they configure it wrongly?
  • What breaks if the JavaScript does not load or the API this depends on is slow?
  • How would we undo this after it has been live for a month?

The honest position on review at all

Review is the cheapest quality mechanism available and the easiest to perform theatrically. A team that reviews everything and blocks on nothing has a ritual rather than a process, and a team that blocks on everything gets routed around the first time something is urgent.

The version that lasts is narrow and enforced: a linter in CI, a short list of things that genuinely block, and a reviewer who reads for blast radius before they read for style. That is also the difference between a theme that is still workable in three years and one that becomes the reason for a rebuild — which is why we treat review standards as part of the deliverable when we do [theme customisation work](/services/build/shopify-theme-customization), not as something a client team is left to invent afterwards.

If you have inherited a theme with no review process and no linter, add the linter first, agree the blocking list second, and do not attempt to retrofit standards onto existing code. Fix what you touch. Retroactive cleanup projects are rarely finished and never funded twice.

Questions this raises

What should a Shopify theme code review cover?

In order: whatever a linter has already flagged (so no human time is spent there), the blast radius of the change across templates and snippets, Liquid cost in loops and repeated lookups, the section schema and whether merchandisers can use and undo it, and the front end — how much JavaScript ships, whether it survives a section re-render, and what happens when it fails. Accessibility basics and secret-handling are blockers rather than notes.

What is Theme Check and should it run in CI?

Theme Check is Shopify’s linter for Liquid and JSON in themes and theme app extensions. Shopify documents it as detecting errors and enforcing Liquid best practices, covering syntax errors, missing templates, unused variables and snippets, unknown and deprecated tags, performance issues and style errors, and it can run on demand, in CI, or through a language server. It should run in CI — every check it performs is one a human would do worse and slower.

What are the most common performance problems in theme code?

Loops, in three forms: iterating a collection without pagination, nesting a second loop inside the first so the cost multiplies, and repeating a lookup inside an iteration when the value could be assigned once before it. Liquid renders server-side, so this cost lands in time-to-first-byte and is invisible in browser profiling. The `paginate` tag supports up to 50 items per page.

How many sections can a Shopify template have?

Shopify documents that JSON templates can render up to 25 sections, that each section can have up to 50 blocks, and that a theme can contain up to 1,000 JSON templates. Most stores never reach these, but a design pattern that treats every small element as its own block can meet the 50-block limit sooner than expected.

Should a reviewer block a merge or leave notes?

Both, marked explicitly and with very few blockers. Block on correctness, security, accessibility, performance against a realistic production input, and anything that cannot be undone later without a migration. Everything else is a note. A review with forty equally weighted comments gets skimmed and merged, which is worse than three comments the author actually acts on.

How do you review a theme you have inherited?

Differently — that is an audit, not a code review, and it produces a document rather than comments. Start with the section schemas and settings, because how a theme is configured says more about the cost of working in it than any individual template does. Then add a linter, agree what blocks in future, and fix code as you touch it rather than launching a cleanup project nobody will fund twice.

NEXT STEP

Free store audit

A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.