## Передача значений между dataLayer и переменными GTM: где чаще всего ломается логика
Восемь из десяти разборов чужих контейнеров, которые попадают мне в руки, валятся на одном и том же месте — разработчик путает **источник правды** для значений. dataLayer пушитят событие, в триггере проверяют встроенную переменную, в теге снова читают dataLayer, а в отчёте — пустота или, что хуже, мусорные дубли.
Разложу, как мы в команде договариваемся о трёх уровнях и зачем это нужно.
**Уровень 1 — dataLayer.** Это контракт между сайтом и GTM. Сюда попадает только то, что сайт *готов отдать* и что не меняется после загрузки. Идентификатор заказа, тип пользователя, источник визита, список товаров в корзине. Содержимое dataLayer фиксируется в момент пуша — дальше оно лежит как снимок.
**Уровень 2 — встроенные и пользовательские переменные.** Это *производные* от dataLayer. Переменная не должна жить своей жизнью, у неё одна задача: достать значение по понятному пути и положить в удобную обёртку. Если вы ловите {{DLV — ecommerce.transaction_id}} в dataLayer, а в теге используете {{Transaction ID}}, которую определили вручную — у вас два места правды, и они рано или поздно разъедутся.
**Уровень 3 — теги.** Тег *потребляет* значения, но ничего не хранит. Шаблон тега должен ссылаться на переменные, а не на dataLayer напрямую. Это кажется педантизмом, пока не приходит задача «поменяй название параметра в трёх местах» и не выясняется, что в одном месте стоит dataLayer.push, в другом — переменная, в третьем — хардкод в шаблоне.
Практическое правило, которое экономит часы дебага: **один смысл — один источник чтения**. Если в dataLayer уже есть идентификатор заказа, не плодите custom JS, который его же вытаскивает из DOM. Если у вас есть переменная {{User Type}}, не дёргайте в теге window.dataLayer[0]['user_type'] напрямую.
Отдельная боль — порядок пушей. dataLayer.push({...}) *до* установки GTM не виден контейнеру. dataLayer.push({...}) *после* установки — виден, но если между ними успело сработать событие типа Pageview, ваше значение опоздает к нужному триггеру. Решение старое: либо пушить всё критичное в dataLayer до фрагмента GTM, либо использовать event name в dataLayer и подписываться на него триггером Custom Event — это самый предсказуемый паттерн в 2026 году.
И последнее. Переменная GTM — это не место для бизнес-логики. Решение «если user_type равен X, подставить Y» должно жить либо на сайте (до пуша), либо в lookup-таблице переменной, либо в условии триггера. Когда логика рассыпана по десяти местам, любая правка превращается в археологическую экспедицию.
Сильный контейнер читается как код: одна правда, явные зависимости, минимум магии. Слабый — как чёрный ящик, в который все пушитят, и никто не знает, что оттуда улетает в рекламу.
Хорошим правилом самопроверки считаю вопрос: «Если завтра в dataLayer переименуют поле, в скольких местах я должен буду поправить?» Если больше двух — пора пересобирать структуру.
— @GTMrecipesRuPro
GTM рецепты — теги и триггеры
@GTMrecipesRuPro
## Передача значений между dataLayer и переменными GTM: где чаще всего ломается логика
Этот пост опубликован в Telegram-канале GTM рецепты — теги и триггеры. Подписаться можно по ссылке: @GTMrecipesRuPro.