Субота, обід. Ви сідаєте з кавою, маючи намір швидко реалізувати нову функцію або нарешті дошліфувати той самий побічний проєкт. Через десять хвилин усе зупиняється. Не тому, що логіка занадто складна. Не тому, що ви не розумієте фреймворк. Прогрес завмирає, бо один тег залишився незакритим.
Саме це сталося під час виклику цих вихідних. Помилка синтаксису Liquid. Тег не було закрито належним чином. Парсер пройшов крізь файл, дійшов до моменту, де він очікував закриваючу послідовність, і нічого не знайшов. Ось так просто збірка провалилася. Це той тип багів, що принижують досвідчених розробників і можуть загнати новачків у спіраль невпевненості в собі, хоча виправлення займає лічені секунди, щойно ви його помітите.
Що пішло не так «під капотом»
Liquid — це мова шаблонів, створена Shopify, яка керує всім: від інтернет-магазинів до блогів на базі Jekyll на GitHub Pages. Вона покладається на два основні синтаксичні патерни. Подвійні фігурні дужки використовуються для виводу, як у {{ page.title }}. Фігурні дужки з відсотками використовуються для логіки та керування потоком, як-от {% if user %} або {% for item in list %}.
Кожен відкриваючий тег очікує на пару. {% if %} вимагає {% endif %}. Цикл {% for %} вимагає {% endfor %}. Блок capture потребує {% endcapture %}. Це не просто рекомендації. Рушій Liquid читає ваш шаблон послідовно. Коли він зустрічає відкриваючу конструкцію, він додає фрейм у свій внутрішній стек і чекає. Якщо файл закінчується або якщо інший великий блок закривається до появи очікуваного тега, рушій видає помилку. Повідомлення часто буває різким: тег не було закрито належним чином. Система очікувала закриваючу послідовність. Іноді ви отримуєте номер рядка. Іноді цей номер вказує не на те місце, тому що парсер усвідомлює відсутність пари лише після того, як опрацює все, що знаходиться нижче.
Розглянемо конкретний приклад. Ви можете написати щось подібне:
{% for product in collections.all.products %}
<div class="card">
<h2>{{ product.title }}</h2>
{% if product.available %}
<span>In stock</span>
{% endif %}
</div>
{% endfor %}
Усі три теги закриті. Тепер уявіть, що ви швидко ітеруєте, копіюєте та вставляєте фрагменти з документації, і випадково пропускаєте останню r:
{% for product in collections.all.products %}
<div class="card">
<h2>{{ product.title }}</h2>
{% if product.available %}
<span>In stock</span>
</div>
{% endfo %}
Або, можливо, ви просто зовсім забули про {% endfor %}, бо він знаходиться під стіною HTML-коду. Рушій бачить {% for %, реєструє цикл і так і не знаходить йому пару. У контексті Shopify це означає, що вся тема не може скомпілюватися. У Jekyll GitHub Pages надішле вам електронний лист про помилку збірки. Локальна розробка може видати загадковий stack trace. Один забутий тег зупиняє весь конвеєр.
Тиранія дрібних помилок
Ці помилки дратують саме тому, що вони не пропорційні розміру помилки. Ви не спроектували базу даних неправильно. Ви не обрали невірний алгоритм. Ви забули один-єдиний символ. Маленькі помилки спричиняють великі баги. Той відсутній {% endif %} не просто ввічливо ламає один рядок. Він викликає каскадний ефект. Парсер, заплутавшись у тому, де закінчується умова, може інтерпретувати кожен наступний рядок як неправильно сформований. Те, що виглядало як шаблон на двадцять рядків, раптом генерує шістдесят рядків виводу помилок, більшість із яких вводять в оману.
Ви стикаєтеся з цими помилками, коли забуваєте один символ, і ваш мозок майже ніколи не готовий до такої реальності. Люди читають код через розпізнавання патернів. Ми бачимо намір. Ми бачимо if і відповідну логіку, і ми робимо висновок про межі. Комп'ютер не робить висновків. Він читає символ за символом, зверху вниз, з нульовою толерантністю до неоднозначності. Коли він доходить до кінця файлу, все ще чекаючи на тег-пару, він здається. Ваше завдання — стати таким розробником, який здатний думати як парсер рівно на стільки часу, щоб помітити прогалину.
Це не притаманно лише Liquid. Незакрита дужка в Python, відсутня зворотна лапка в Markdown, забута фігурна дужка в JavaScript, незакрита кутова дужка в HTML. Виклик цих вихідних використав Liquid як навчальний інструмент, але основний урок стосується кожної мови, з якою ви коли-небудь працюватимете. Синтаксис — це граматика, а граматика не прощає помилок.
Як їх вистежити
Коли ви впираєтеся в таку стіну, першим інстинктом є панічне перечитування всього файлу. Спротивтеся цьому. Панічне читання змушує вас проглядати саме той символ, який ви пропустили, тому що ваш мозок автоматично його «виправляє». Замість цього працюйте систематично.
Явно зіставляйте свої теги. Пройдіть по всьому файлу та проговоріть або запишіть кожен відкриваючий тег. for потребує endfor. if потребує endif. unless потребує endunless. capture потребує endcapture. Якщо ви вкладаєте блоки один в одного, ведіть у голові лічильник. Коли я відкриваю if всередині for, це два зобов'язання, які я маю виконати до кінця файлу.
Використовуйте свій редактор. Якщо ви регулярно працюєте з Liquid, встановіть підсвітку синтаксису, яка розпізнає його граматику. У Visual Studio Code є розширення, які приглушують або розфарбовують теги Liquid. Коли закриваючий тег сформований неправильно, колірна схема змінюється. Деякі лінтери можуть виявити незакриті блоки ще до того, як ви натиснете «компілювати». У Vim або Neovim розгляньте можливість використання плагіна на кшталт vim-liquid або налаштуйте Tree-sitter для підсвічування відповідних тегів. Ці інструменти не усувають необхідність думати, але роблять невідповідність помітною.
Використовуйте бінарний пошук у своєму шаблоні. Якщо повідомлення про помилку вказує на рядок 200, але там усе виглядає нормально, справжній винуватець, ймовірно, знаходиться вище. Закоментуйте нижню половину шаблону. Чи збирається він? Якщо так, то помилка в закоментованій частині. Розкоментуйте половину цієї частини. Повторюйте, поки не ізолюєте зламаний блок. Це здається повільним, але це швидше, ніж шість разів перечитувати ті самі двісті рядків, поки ваше роздратування зростає.
Перевіряйте свої include. Liquid підтримує модульні фрагменти через {% include %} або {% render %}. Незакритий тег може взагалі не бути в основному файлі. Він може бути всередині фрагмента (snippet), який підтягує батьківський шаблон. Саме тут контроль версій рятує ваш душевний спокій. Запустіть diff. Подивіться, що змінилося з моменту останньої успішної збірки. Часто відповідь одразу кидається в очі завдяки червоному та зеленому кольорам.
Відступи — це документація. Якщо ваш {% if %} починається з нульової колонки, а відповідний йому {% endif %} зміщений кудись усередину вкладеної структури, візуальне вирівнювання допоможе вам помітити невідповідність. Якщо ваші HTML та Liquid теги використовують однакову схему відступів, ваш погляд одразу помітить «напарника», який опинився на неправильній глибині.
Справжня навчальна програма
Виклики вихідного дня мають значення, тому що вони відтворюють саме ті умови, в яких ви насправді працюєте. Жоден менеджер не спостерігає. Жодних дедлайнів, що тиснуть. Ви пишете код заради навичок або для задоволення, і раптом мікроскопічна помилка змушує вас завмерти. Цей момент і є уроком. Ви не вчитеся налагоджувати (debug), читаючи про налагодження. Ви вчитеся, дивлячись на зламану збірку в той час, коли вам хотілося б бути на вулиці, змушуючи себе сприймати повідомлення про помилку як дані, а не як критику.
Вчіться виправляти ці помилки, тому що вони ніколи не зникають повністю. Навіть через десять років кар'єри ви все одно забудете закриваючий тег під час розгортання (deploy) у п'ятницю ввечері. Різниця між junior та senior розробником полягає не у відсутності помилок. Вона у швидкості відновлення. Senior бачить синтаксичну помилку, розпізнає патерн, перевіряє очевидних «підозрюваних» і рухається далі. Junior гадає, чи не зламався весь інструментарій. Повторення формує цей рефлекс.
Спільнота прискорює цей процес. Коли кілька людей працюють над одним і тим самим зламаним шаблоном протягом вихідних, з'являються закономірності, які жоден розробник не помітить наодинці. Хтось помічає, що помилка виникає лише всередині вкладених циклів for. Хтось інший ділиться shell-скриптом, який шукає (grep) поширені невідповідності тегів Liquid. Знання примножуються, коли ними діляться, а не коли їх накопичують. Ви можете прочитати всі деталі конкретного виклику та побачити, як до нього підійшли інші, у дописі на Dev.to. Якщо ви хочете обмінятися досвідом з людьми, які вирішують такі ж проблеми, існує необов'язкова навчальна спільнота в Telegram, де ці обговорення зазвичай тривають і після вихідних.
Головний висновок
Не сприймайте синтаксичні помилки як перешкоди для вашої справжньої роботи. Це і є фундаментальна робота. Тег Liquid, який зламав збірку цими вихідними, ніколи не стосувався безпосередньо рушія шаблонів. Йшлося про те, щоб навчити себе читати з точністю, коли ваш мозок хоче просто вгадати. Відкрийте файл, який ви написали минулого тижня. Проскануйте відкриті теги. Переконайтеся, що на кожен із них є відповідь. Закрийте свої цикли. Завершіть свої умовні оператори. А потім повертайтеся до розробки — по одному правильному символу за раз.
