Когда сайт стоит переносить, а когда дешевле собрать заново
Меня зовут Евгений Семёнов, я технический директор ITfresh — мы занимаемся IT-аутсорсингом для московских компаний до пятидесяти рабочих мест, держим собственные серверы в дата-центре МТС и уже больше пятнадцати лет разгребаем чужие сайты вместе с инфраструктурой. Это третья статья серии про Drupal: в первой был разбор самой CMS, во второй — регламент развёртывания в продакшен. Сегодня — самая дорогая и самая недооценённая часть любого проекта: переезд существующего сайта и его встраивание в контур компании.
Первый вопрос, который я задаю клиенту, принёсшему «переведите нас на Drupal», — не «с чего переезжаем», а «сколько у вас контента и насколько он структурирован». Потому что миграция — это не про технологию, а про объём. Наш рабочий критерий выглядит так:
- До 30–50 страниц (визитка, несколько услуг, контакты, десяток новостей). Переносим руками: редактор открывает старую страницу, копирует текст в новую, правит вёрстку. Один человек за день-два. Писать для такого объёма автоматизацию — выбрасывать деньги: отладка конвейера займёт больше, чем ручной перенос.
- От нескольких сотен однотипных записей — карточки врачей, практик юрфирмы, товаров, объектов недвижимости, статей базы знаний. Здесь руками уже нельзя: человек на третьей сотне начинает ошибаться, а любая правка структуры («давайте добавим поле „стаж“») означает повторный проход по всему массиву. Только Migrate API, только воспроизводимый конвейер.
- Между ними — серая зона, где решает структура. Сто страниц с произвольной вёрсткой дешевле перенести руками, чем сто карточек с десятью полями каждая — машиной.
Отдельный случай, который мы в 2026 году встречаем всё ещё регулярно, — старые сайты на Drupal 7. Семёрка официально закончилась 5 января 2025 года, обновлений безопасности для неё нет, и для таких сайтов вопрос «переносить или нет» не стоит: переносить обязательно. Хорошая новость в том, что это единственный сценарий, под который в ядре есть штатный путь: модули migrate_drupal и migrate_upgrade умеют прочитать базу семёрки напрямую и разложить её по сущностям Drupal 11. Плохая новость — штатный путь закрывает типовые узлы, таксономии и пользователей, а всё, что на старом сайте было накручено кастомными модулями и Views с хитрой логикой, всё равно придётся переписывать.
И последний критерий, про который стесняются говорить подрядчики: иногда переносить нечего. Если сайт клиента — это десять страниц маркетингового текста пятилетней давности, который всё равно будет переписан, то «миграция» сводится к редиректам со старых URL. Честнее назвать это новым сайтом, а не переездом, и не выставлять счёт за Migrate API.
Migrate API: конвейер source → process → destination
Главное, что нужно понять про перенос в Drupal: здесь нет кнопки «Импортировать CSV». Вместо неё — декларативная модель, в которой каждая миграция описывается YAML-файлом из трёх частей. Поначалу это раздражает («зачем писать конфиг, если в WordPress плагин импорта работал из коробки»), но ровно эта модель и позволяет переносить тысячи записей предсказуемо.

Разберём анатомию на типовой задаче — перенос каталога услуг из WordPress в тип содержимого «Услуга» на Drupal 11.
- Source (источник) — откуда берём данные. Это может быть прямое подключение к базе WordPress, экспорт WXR, CSV-выгрузка, JSON по HTTP, база Drupal 7. Плагин источника отвечает за одно: выдать поток строк с уникальным идентификатором каждой. Мы в большинстве WP-проектов выгружаем контент в CSV заранее подготовленным SQL-запросом — так источник «замораживается», и миграцию можно гонять на стенде, не трогая боевой WordPress.
- Process (обработка) — как каждое поле источника превращается в поле назначения. Это цепочки плагинов: подставить значение по умолчанию, заменить подстроку, найти связанную сущность из другой миграции, перевести статус «publish» в «1». Здесь же живёт вся грязная работа: вычищение шорткодов, перекладка картинок в сущности Media, нормализация телефонов.
- Destination (назначение) — куда кладём. Обычно это «узел такого-то типа», но может быть термин таксономии, пользователь, файл, сущность Media. Drupal сам ведёт таблицу соответствий «id в источнике → id в назначении», и именно она делает возможным откат и повторный запуск.
Так выглядит рабочий план миграции услуг, сокращённый до сути (реальный файл длиннее на десяток полей):
id: wp_services
label: 'Каталог услуг из WordPress'
migration_group: wp_site
source:
plugin: csv
path: 'private://migrate/services.csv'
ids: [wp_id]
track_changes: true
process:
title: post_title
'body/value':
-
plugin: callback
callable: '\Drupal\itf_migrate\Cleanup::stripShortcodes'
source: post_content
-
plugin: str_replace
search: 'https://old-site.ru/wp-content/uploads/'
replace: '/sites/default/files/legacy/'
'body/format':
plugin: default_value
default_value: basic_html
field_category:
plugin: migration_lookup
migration: wp_categories
source: category_id
field_image:
plugin: migration_lookup
migration: wp_media
source: thumbnail_id
field_price:
plugin: callback
callable: intval
source: acf_price
status:
plugin: static_map
source: post_status
map:
publish: 1
draft: 0
default_value: 0
uid:
plugin: default_value
default_value: 1
destination:
plugin: 'entity:node'
default_bundle: service
migration_dependencies:
required:
- wp_categories
- wp_media
Обратите внимание на зависимости: услуги ссылаются на рубрики и картинки, поэтому сначала должны отработать миграции wp_categories и wp_media. Плагин migration_lookup берёт идентификатор из WordPress и по таблице соответствий находит уже созданный термин или Media. Это и есть ответ на вопрос «как сохранить связи между сущностями» — они восстанавливаются автоматически, если миграции выстроены в правильном порядке. Набор плагинов из ядра закрывает процентов восемьдесят задач, остальное добирают migrate_plus (источник по HTTP, парсеры JSON и XML, дополнительные трансформации) и migrate_tools (команды drush и интерфейс в админке).
drush migrate:import и migrate:rollback — миграция как итеративный процесс
Вот ради чего стоило писать YAML. После того как план лежит в конфигурации, весь цикл выглядит так:
# Состояние всех миграций группы: сколько строк в источнике, сколько перенесено
drush migrate:status --group=wp_site
# Прогон одной миграции с учётом зависимостей
drush migrate:import wp_services --execute-dependencies
# Что-то не так? Откатываем — удаляются только созданные этой миграцией узлы
drush migrate:rollback wp_services
# Правим YAML, перечитываем конфигурацию, запускаем заново
drush cim -y && drush migrate:import wp_services
# Источник изменился, нужно подтянуть только новое и изменённое
drush migrate:import wp_services --update
На практике первый прогон никогда не бывает удачным. В третьей сотне карточек найдётся описание с незакрытым тегом, в пятой — цена «от 5 000 р.» вместо числа, в седьмой — картинка, которой нет на диске. Без отката каждую такую находку пришлось бы чистить руками в базе, а потом бояться повторного запуска из-за дублей. С откатом цикл «увидели проблему → поправили плагин → откатили → перезапустили» занимает минуты, и мы делаем его десятки раз, пока migrate:status не покажет ноль ошибок. Именно поэтому я называю Migrate API главным аргументом за Drupal в проектах со структурированным контентом: он превращает миграцию из разового героического подвига в повторяемую процедуру.
migrate:import, и окно простоя измеряется минутами, а не сутками.Перенос с WordPress: что ломается всегда и в каком порядке мы работаем
WordPress — самый частый донор в наших проектах миграции, и за годы у нас накопился список вещей, которые ломаются в каждом втором переезде. Дело не в том, что WordPress плохой: у двух систем разная модель данных, и наивное «перенести как есть» не работает по определению.
| Что в WordPress | Что ломается при переносе | Что делаем |
|---|---|---|
| Рубрики и метки | В WP рубрики иерархические, метки — плоские, и обе навешаны на один тип «запись». В Drupal таксономия привязана к полям типа содержимого, и «рубрика услуги» и «рубрика новости» — разные словари | Строим словарь соответствий: какая рубрика WP в какой словарь Drupal попадает. Лишние метки (у типового сайта их сотни, половина с одной записью) сливаем или отбрасываем |
Шорткоды [gallery], [button], [contact-form-7] | Без плагина-обработчика превращаются в квадратные скобки посреди текста | Инвентаризируем все шорткоды запросом по базе. Галереи и кнопки разбираем плагином callback в HTML или в ссылки на Media, формы заменяем на Webform, остальное вырезаем |
| Поля ACF / Pods / кастомные мета-поля | Лежат в wp_postmeta в виде «ключ-значение», часто сериализованные массивы PHP. Автоматически не маппятся ни во что | Явный маппинг каждого поля в поле Drupal. Сериализованные значения десериализуем своим плагином обработки. Это самая трудоёмкая часть инвентаризации |
| Блоки Gutenberg и билдеры (Elementor, WPBakery) | Контент перемешан с разметкой билдера: комментарии-маркеры, вложенные div с inline-стилями, JSON в атрибутах | Честно предупреждаем клиента: из билдера переносится текст и картинки, а не оформление. Страницы-лендинги собираем заново в Drupal Canvas или Layout Builder |
Картинки в wp-content/uploads | Вставлены абсолютными URL в тело текста, плюс десять размеров-копий на каждую | Переносим только оригиналы в сущности Media отдельной миграцией, в теле текста заменяем пути, размеры генерирует Drupal через image styles |
| Комментарии | Чаще всего на корпоративном сайте там спам | Не переносим, если клиент не настаивает. Экономим день работы |
| Меню и виджеты | Живут в настройках темы, а не в контенте | Собираем руками — их мало, и они всё равно меняются при редизайне |
Порядок работ у нас за годы устоялся, и нарушать его я не советую:
- Инвентаризация контента в таблицу. Не на глаз, а запросами по базе: сколько записей каждого типа, какие мета-поля реально заполнены (а не просто объявлены в ACF), какие шорткоды встречаются и сколько раз, сколько картинок и какого объёма. Результат — таблица, которую подписывает клиент. Это защита обеих сторон: потом не будет разговора «а где наши отзывы», если отзывы в таблице не значились.
- Словарь соответствий. Для каждого типа записи WP — тип содержимого Drupal, для каждого поля — поле, для каждого словаря — словарь. Здесь же фиксируем, что выбрасываем. Этот документ напрямую превращается в секции
processв YAML. - Пробная миграция на стенде. Поднимаем Drupal 11 на стейджинге, заливаем выгрузку, гоняем конвейер до нуля ошибок, отдаём клиенту смотреть. Правки на этой стадии — дёшево, после запуска — дорого.
- Заморозка контента на старом сайте и финальный прогон. За день-два до запуска просим клиента не править старый сайт, делаем финальную выгрузку и переносим дельту.
Пользователи и пароли: переносим учётки, а не хеши
Технически хеши паролей WordPress можно перенести: есть contrib-модуль, который учит Drupal проверять пароль по старому алгоритму и при первом удачном входе перехешировать его в свой формат. Мы этим путём почти не пользуемся, и вот почему. На корпоративном сайте пользователей обычно мало — редакторы и администраторы, — и у половины из них пароли трёхлетней давности, которые давно пора менять. Миграция — хороший повод это сделать. Поэтому наш стандарт: переносим учётки с ролями и e-mail, пароли не переносим, а в день запуска рассылаем письмо «сайт переехал, задайте новый пароль по ссылке». Исключение — сайты с личными кабинетами на тысячи клиентов: там принудительный сброс вызовет волну обращений в поддержку, и хеши мы переносим, предварительно прогнав аудит на слабые пароли.
SEO при миграции: карта редиректов — половина проекта
Если бы меня попросили назвать одну причину, по которой бизнес жалеет о переезде сайта, я бы не задумываясь сказал: потерянный поисковый трафик. И в девяти случаях из десяти теряют его не потому, что Drupal «хуже для SEO», а потому, что редиректы решили «сделать потом».
URL-структура при миграции меняется почти всегда. WordPress жил на /uslugi/lechenie-kariesa/, новый сайт — на /services/lechenie-kariesa, без завершающего слеша и с другим разделом. Для поисковика это два разных адреса: старый начинает отдавать 404, накопленные за годы ссылки и позиции по нему обнуляются, новый стартует с нуля. Единственный способ передать вес — постоянный редирект 301 со старого адреса на новый, и для каждой страницы он должен быть готов в момент запуска, а не через неделю.
Как мы это делаем:
- Выгружаем полный список URL старого сайта — из карты сайта, из базы, из логов веб-сервера за последние месяцы (там всплывают адреса, о которых никто не помнил, но по которым ходят люди) и из отчётов Яндекс Вебмастера и Search Console.
- Для каждого старого URL определяем новый. Для контента, который идёт через Migrate, это делается в самом конвейере: старый путь переносится в поле, а после генерации нового алиаса модулем
pathautoпара «старый → новый» записывается в модульredirectотдельной миграцией. Для ручного контента — таблица, которую заполняет редактор. - Страницы, которым нет аналога, сводим на ближайший раздел, а не на главную: редирект всего подряд на главную поисковики справедливо считают мягким 404.
- Перед запуском прогоняем скриптом весь старый список по стенду и проверяем, что каждый адрес отдаёт ровно один 301 на существующую страницу. Цепочки редиректов и петли — отдельный пункт проверки.
Обязательный SEO-комплект Drupal, без которого мы сайт не сдаём: pathauto (человекочитаемые адреса по шаблонам), metatag (title, description, Open Graph, canonical), simple_sitemap (карта сайта с автоматической регенерацией), redirect (редиректы и, в его составе, журнал 404). Все четыре — contrib с покрытием Security Team, ставятся через Composer.
composer require drupal/pathauto drupal/metatag drupal/simple_sitemap drupal/redirect
drush en pathauto metatag simple_sitemap redirect -y
drush cr
После запуска начинается контроль. Первые две недели я смотрю журнал 404 модуля Redirect ежедневно: туда попадают адреса, которые мы пропустили, и их можно тут же превратить в редирект прямо из админки. Параллельно — Search Console и Вебмастер: отдаём новую карту сайта, следим за количеством проиндексированных страниц и ошибками сканирования. Просадка позиций на две-четыре недели при смене URL-структуры нормальна; если через полтора месяца трафик не вернулся к прежнему уровню — где-то дыра в карте редиректов.
Интеграция с 1С: каталог и цены без ручной синхронизации
Типовой запрос нашей аудитории звучит одинаково в клинике, в юрфирме и у оптовика: «Прайс на сайте должен совпадать с прайсом в 1С, и чтобы никто ничего руками не перебивал». Запрос разумный, и Drupal его закрывает, но я обязан сказать прямо: готового коннектора уровня «поставил модуль, ввёл адрес базы, всё заработало», как это устроено в экосистеме Битрикса, для Drupal нет. Интеграция с 1С — это всегда небольшая разработка, и её нужно закладывать в бюджет отдельной строкой.

Хорошая новость — разработка небольшая, потому что у нас уже есть Migrate API. Два рабочих варианта, которые мы применяем:
Вариант 1: OData из 1С по расписанию → Migrate с track_changes
Современные конфигурации 1С умеют отдавать справочники и регистры по стандартному REST-интерфейсу OData — его включают на стороне публикации базы. Со стороны Drupal конвейер выглядит как обычная миграция, только источник — не CSV, а HTTP-запрос плагином url из migrate_plus с JSON-парсером. Ключевой флаг — track_changes: true: Drupal хранит хеш каждой строки источника и при повторном запуске обновляет только те узлы, у которых данные в 1С реально изменились. Без этого флага ночная синхронизация десяти тысяч позиций переписывала бы всё подряд и сбрасывала дату изменения у каждой карточки.
id: onec_services
label: 'Услуги и цены из 1С (OData)'
source:
plugin: url
data_fetcher_plugin: http
data_parser_plugin: json
urls:
- 'https://1c.example.ru/base/odata/standard.odata/Catalog_Номенклатура?$format=json'
authentication:
plugin: basic
username: odata_reader
password: '***'
item_selector: value
track_changes: true
ids:
Ref_Key:
type: string
fields:
- name: Ref_Key
selector: Ref_Key
- name: Description
selector: Description
- name: Price
selector: Цена
process:
title: Description
field_price: Price
field_1c_guid: Ref_Key
destination:
plugin: 'entity:node'
default_bundle: service
Запуск — по cron: drush migrate:import onec_services --update раз в час или раз в ночь, в зависимости от того, как часто меняются цены. Полное описание изменений остаётся в таблице миграции, откат работает так же, как при переезде. Этот вариант мы выбираем для каталогов от сотен до нескольких тысяч позиций, когда 1С опубликована в интернете или доступна по VPN с сервера сайта.
Вариант 2: 1С сама отправляет JSON на REST-эндпоинт Drupal
Если 1С стоит в закрытой сети и наружу её публиковать нельзя (частая ситуация у клиентов, где базу обслуживает сторонний франчайзи), направление разворачиваем: в 1С пишется небольшая обработка, которая по регламентному заданию собирает изменившиеся позиции и POST-запросом отправляет JSON на наш эндпоинт. На стороне Drupal это кастомный модуль с одним маршрутом, проверкой токена в заголовке и простой логикой «найти узел по GUID из 1С → обновить или создать». Код — порядка сотни строк, плюс тесты. Вариант дороже первого на разработку в 1С, но снимает вопросы безопасности: сайт не имеет доступа в учётную систему, только учётная система к сайту.
Что выбирать по объёму. При сотне позиций оба варианта избыточны — честно скажу, что дешевле обновлять прайс раз в месяц вручную через импорт CSV в админке, если цены меняются редко. От нескольких сотен до нескольких тысяч — вариант с OData и расписанием. На десятках тысяч позиций с остатками по складам, которые меняются каждые несколько минут, ночной полный прогон уже не годится: нужен второй вариант с отправкой только дельты, очередью на стороне Drupal и обработкой пакетами, чтобы не класть сайт в момент синхронизации. Это уже полноценный проект, и его масштаб нужно понимать до подписания договора.
Формы и CRM: заявки не должны жить в почте
Вторая типовая интеграция — заявки с сайта. Картина «у нас форма отправляет письмо на info@, а менеджер по утрам разбирает почту» знакома каждому, и она плоха не эстетически, а экономически: письма теряются в спаме, заявки дублируются, никто не знает, сколько обращений пришло за месяц и сколько из них довели до сделки.
В Drupal формы — это модуль webform, и я не преувеличу, сказав, что он закрывает процентов девяносто пять задач без строчки кода: многошаговые формы, загрузка файлов, условная логика (показать поле «номер договора», если выбрано «действующий клиент»), черновики, ограничение по количеству отправок, экспорт в CSV. Все отправки хранятся в базе сайта — это уже само по себе даёт журнал обращений, даже если CRM пока нет.
Отправка заявки во внешние системы делается обработчиками (handlers) формы. У одной формы их может быть несколько, и они отрабатывают параллельно: один шлёт письмо менеджеру, второй — подтверждение клиенту, третий делает HTTP-запрос в CRM. Штатный обработчик «Remote post» умеет отправить данные формы POST-запросом на произвольный URL с маппингом полей, и для простых CRM этого достаточно.
Наша типовая связка выглядит так: Webform → вебхук → CRM клиента. В практике это чаще всего Planfix, Битрикс24 или amoCRM, у всех трёх есть REST-интерфейс для создания лида. Три вещи, которые мы делаем всегда:
- Телефон как ключ дедупликации. Перед созданием лида ищем в CRM контакт с таким же номером, нормализованным до одного формата. Нашли — добавляем обращение к существующему контакту, не нашли — создаём. Без этого через полгода в CRM будет пять карточек одного клиента, который звонил с разных форм.
- Промежуточный слой, а не прямой вызов. Для Planfix и Битрикс24 штатного «Remote post» мало: нужен токен, нужна правильная структура полей, нужна обработка ошибок. Поэтому у нас небольшой свой обработчик Webform, который умеет повторить запрос, если CRM ответила ошибкой, и записать неудачу в журнал. Заявка не должна пропасть из-за пятиминутного простоя CRM.
- Почта остаётся как резерв. Письмо менеджеру отправляется всегда, даже если заявка ушла в CRM: это страховка и привычный для людей канал. Для отправки используем внешний SMTP-сервер, а не
mail()с веб-сервера — иначе письма улетают в спам.
Антиспам без капчи: Antibot + Honeypot
Открытая форма на сайте начинает собирать спам через неделю после запуска. Классический ответ — капча — убивает конверсию: каждый лишний клик на форме заявки стоит процентов обращений, а для пожилой аудитории клиники «выберите все светофоры» превращается в непреодолимый барьер. Мы ставим два модуля, которые работают незаметно для человека: honeypot добавляет в форму скрытое поле, которое заполняют только боты, и проверяет минимальное время заполнения (человек не отправляет форму через секунду после загрузки); antibot требует, чтобы форма была отправлена через JavaScript, что отсекает примитивные скрипты. Вдвоём они убирают процентов девяносто пять мусора. Для оставшихся пяти — ограничение по количеству отправок с одного IP в Webform и, если совсем невмоготу, капча только на той форме, которую атакуют, а не на всём сайте.
Headless и JSON:API: зачем это малому бизнесу — и почему обычно незачем
Раз в несколько месяцев ко мне приходит запрос «хотим headless на Drupal с фронтом на React, как у больших». Давайте разберёмся, что это даёт и сколько стоит, потому что тема модная, а ответ для компании до пятидесяти рабочих мест чаще всего отрицательный.
Техническая сторона простая и честно хорошая. В ядре Drupal есть модуль jsonapi: включили — и весь контент сайта доступен по REST в стандартизированном формате. Каждый тип содержимого, каждая таксономия, каждая сущность Media получает свой адрес вроде /jsonapi/node/service, с фильтрацией, сортировкой, постраничной выдачей и вложенными связями. Никакого кода: права доступа управляются теми же ролями, что и для сайта. Для разработчика мобильного приложения это готовый бэкенд контента.
drush en jsonapi -y
# Опубликованные услуги с ценой, отсортированные по названию, первые 20
curl -s 'https://site.ru/jsonapi/node/service?filter[status]=1&sort=title&page[limit]=20&fields[node--service]=title,field_price'
Где это действительно полезно нашим клиентам:
- Мобильное приложение клиники: список врачей, услуг и акций берётся с сайта, а не дублируется в отдельной админке приложения. Контент-менеджер правит в одном месте.
- Инфоэкраны в офисе или на ресепшене: простая страница на телевизоре, которая раз в минуту забирает через JSON:API список новостей или расписание и показывает его. Пара дней работы.
- Обмен с партнёрскими площадками: агрегатор или маркетплейс забирает каталог без ручных выгрузок.
А теперь про full-headless, когда Drupal только хранит контент, а весь сайт рендерит отдельное приложение на React, Next.js или аналоге. Что теряется: вся встроенная работа Drupal с отображением — темы, Layout Builder, Drupal Canvas с живым превью, штатные метатеги, карта сайта, предпросмотр перед публикацией, формы Webform, кэширование страниц. Всё это нужно либо написать заново на фронте, либо прикрутить через дополнительные слои. Что приобретается: скорость интерфейса на уровне приложения и свобода фронтенд-разработчика. По нашему опыту, бюджет такого проекта выходит в два-три раза выше, чем у классического сайта на той же CMS, а сопровождение требует двух разных специалистов вместо одного. Для корпоративного сайта, который посещают несколько тысяч человек в месяц и главная функция которого — рассказать об услугах и принять заявку, это переплата за модность.
Разумная середина, которую мы рекомендуем, — гибрид: Drupal рендерит сайт сам, как обычная CMS, со всеми своими удобствами, а jsonapi включён параллельно для приложения, экранов или партнёров. Один бэкенд, одна команда, один бюджет на сопровождение — и при этом дверь в мобильные сценарии открыта, когда они понадобятся.
Смета и сроки: из чего складывается миграция
Закончу тем, о чём клиенты спрашивают в первую очередь, а я намеренно отложил до конца, потому что без понимания предыдущих разделов цифры смысла не имеют. Типовой проект переноса корпоративного сайта с интеграциями у нас раскладывается по этапам в довольно стабильной пропорции. Возьмём для наглядности условный проект в двести часов: сайт на WordPress с тремя сотнями карточек услуг, десятком лендингов, прайсом из 1С и заявками в CRM.
| Этап | Доля | Часы (условно) | Что внутри | Результат этапа |
|---|---|---|---|---|
| Инвентаризация и проектирование | 10% | 20 | Запросы по базе старого сайта, таблица контента, словарь соответствий, структура типов содержимого, список URL | Подписанная таблица «что переносим, что выбрасываем» |
| Стенд и миграции | 40% | 80 | Развёртывание Drupal 11, типы содержимого и поля, YAML-планы, плагины обработки, итерации до нуля ошибок, тема и лендинги | Сайт на стейджинге с полным контентом |
| Редиректы и SEO | 15% | 30 | Карта старых и новых URL, миграция редиректов, Pathauto, Metatag, карта сайта, проверка скриптом, настройка Вебмастера и Search Console | Ноль битых старых адресов на стенде |
| Интеграции | 25% | 50 | Конвейер OData из 1С с track_changes или REST-эндпоинт, Webform с обработчиком в CRM, дедупликация, SMTP, антиспам | Цены синхронизируются, заявки в CRM, письма доходят |
| Приёмка и запуск | 10% | 20 | Проверка клиентом, правки, заморозка контента, финальный прогон, переключение DNS, дежурство первой недели, контроль 404 | Сайт на боевом домене, журнал 404 пустеет |
Пропорции меняются от проекта к проекту — у сайта без 1С доля интеграций падает до десяти процентов, у сайта на Drupal 7 с кастомными модулями доля стенда и миграций может перевалить за половину. Но если в смете подрядчика какого-то из этих этапов нет вовсе, это повод задать вопросы.
Маркеры того, что миграцию считают «на глазок», по которым я сам оцениваю чужие коммерческие предложения:
- В смете нет этапа инвентаризации, а цена названа до того, как кто-то заглянул в базу старого сайта.
- Редиректы не выделены отдельной строкой или упомянуты как «настроим при запуске».
- Интеграция с 1С оценена в один-два дня «через готовый модуль». Модуля нет; либо подрядчик не делал этого раньше, либо закладывает доработку в скрытую маржу.
- Срок назван без привязки к объёму контента: «любой сайт за три недели».
- Нет упоминания стенда: значит, миграцию планируют делать сразу на боевом домене.
- Отсутствует этап приёмки и дежурства после запуска — первые две недели дают больше правок, чем весь стенд.
И чек-лист вопросов, которые стоит задать подрядчику до подписания договора. Ответы на них отличают команду, которая делала миграции, от команды, которая прочитала документацию:
- Как вы будете переносить контент — руками, Migrate API или своими скриптами? Покажете пример YAML-плана с прошлого проекта?
- Что произойдёт, если после первого прогона мы найдём ошибку в трёхстах карточках? Сколько стоит повторный прогон?
- Кто составляет таблицу инвентаризации контента и когда мы её подписываем?
- Как вы соберёте список всех URL старого сайта и как проверите, что каждый отдаёт 301 перед запуском?
- Что именно из 1С будет синхронизироваться, в какую сторону, как часто и что будет ключом соответствия?
- Куда попадает заявка с формы, если CRM недоступна в момент отправки?
- На какой ветке Drupal вы разворачиваете сайт и что будет с ним в декабре, когда выйдет Drupal 12? Кто и за чей счёт обновляет?
- Где будет жить стенд, и получим ли мы к нему доступ до запуска?
- Как будет организовано дежурство в первые две недели после переключения и кто смотрит журнал 404?
- Что мы получим на руки в конце: репозиторий с конфигурацией (
drush cex), планами миграций и документацией — или только работающий сайт на вашем хостинге?
Если на большинство вопросов звучат внятные ответы — перед вами люди, которые уже наступали на грабли из этой статьи. Если нет — вы оплатите их обучение.
Мы в ITfresh берём миграции на Drupal вместе с интеграциями в контур компании — 1С, CRM, почта, мобильные сценарии — и сопровождаем сайт дальше на собственных серверах в дата-центре МТС. Обсудить ваш переезд можно в Telegram @ITfresh_Boss или по телефону +7 903 729-62-41: разберём старый сайт, прикинем объём и честно скажем, переносить его или собирать заново.
Оставить комментарий