Когда система «умеет только файл» — а так живёт половина банков, госорганов и легаси — интеграцию делают не через API, а батчем: сложили большой файл, по расписанию его забрали, обработали целиком. Транспорт — SFTP (файлы по SSH), а не голый FTP, потому что FTP гонит логин и данные открытым текстом. Форматы — CSV, фиксированная ширина, XML, в энтерпрайзе EDI. Ключевые требования аналитика к такому обмену: manifest-файл (сигнал «данные залиты целиком» + число строк и контрольная сумма), идемпотентность по файлу (тот же файл положили дважды — не задвоить), карантин битых строк вместо падения на первой ошибке и reconciliation — сверка итогов с источником. Честный trade-off: батч проще и дешевле для больших объёмов и старых систем, но это задержка вместо real-time, тяжёлая обработка ошибок и логика «всё или ничего» по файлу.
Однажды я потерял полдня на инцидент, который начался красиво: ночная выгрузка платежей «прошла», а утром бухгалтерия не сошлась на пару миллионов. Разбор показал банальность — наш загрузчик начал читать файл, который ещё дописывался по сети. Половину строк он увидел, вторую — нет, а признака «файл целиком тут» в схеме обмена не было вообще. Система не упала, не ругнулась, отчиталась «успех» — и тихо обработала недозалитый файл. С тех пор первое, что я спрашиваю про любой файловый обмен: «как принимающая сторона понимает, что файл загружен до конца?» Если внятного ответа нет — это не интеграция, это бомба замедленного действия.
Когда файл вместо real-time API
Кажется, что в 2026-м всё общается по REST. На практике огромный пласт интеграций по-прежнему стоит на файлах, и аналитику важно не морщиться, а понимать — почему здесь это правильный выбор, а не архаизм. Базовую развилку «ждать ответ сейчас или отправить и забыть» я разбирал в записи про синхронную и асинхронную интеграцию — батч это крайний, самый «отложенный» полюс асинхронности: не событие, а целая пачка данных раз в сутки.
Файл выбирают в четырёх типовых ситуациях. Легаси и «умеет только файл». Старая АБС банка, ГИС госоргана, бухгалтерская система — у многих просто нет API, зато есть папка на SFTP, куда они умеют класть и откуда забирать. Спорить с этим бесполезно, под это подстраиваются. Большие объёмы. Выгрузить разом 10 миллионов строк одним файлом дешевле и быстрее, чем дёргать API 10 миллионов раз — каждый вызов это накладные расходы, лимиты, ретраи. Госзаказ и регуляторика. Форматы обмена с госорганами часто прямо предписаны нормативкой — конкретный XML или CSV, конкретное окно, конкретный транспорт. Периодичность по своей природе. Реестр начислений, сверка за день, выгрузка зарплаты — это события «раз в период», им не нужен real-time.
Батч и API не враги, а слои
Часто в одной системе живёт и то, и другое: онлайн-операции идут через API в реальном времени, а ночью батчем уезжает свод за день — в банк, в БУ, регулятору. Не нужно выбирать «или-или». Вопрос аналитика не «API или файл», а «у этого конкретного потока данных какая природа — реакция на событие или периодический свод?».
Транспорт: почему SFTP, а не FTP
Файлы надо как-то доставить, и здесь до сих пор встречаются три буквы, которые легко перепутать. FTP — древний протокол передачи файлов, который гонит и логин-пароль, и сами данные открытым текстом. Любой между вами и сервером может это прочитать. Для платёжного файла это неприемлемо, и в нормальном контуре FTP запрещён политикой безопасности.
SFTP (SSH File Transfer Protocol) — передача файлов поверх SSH, того самого защищённого канала, по которому админы заходят на серверы. Весь трафик шифруется, аутентификация — по паролю или, что правильнее, по SSH-ключу (клиент держит приватный ключ, сервер знает публичный). Это де-факто стандарт файлового обмена в энтерпрайзе. Не путать с FTPS — это старый FTP, обёрнутый в TLS; тоже шифрует, но устроен сложнее (отдельные порты под данные, боль с firewall), поэтому на новых интеграциях почти всегда берут именно SFTP.
«Защищённый транспорт» не равно «защищённые данные»
SFTP шифрует канал в пути. Но файл на диске SFTP-сервера лежит как лежит, и доступ к папке — это доступ ко всем данным. Поэтому в требованиях помимо транспорта проставляют: кто владеет папкой, какие права, как долго файлы там живут до удаления, и шифруется ли сам файл (например, PGP) поверх SFTP — для особо чувствительных данных это отдельный слой.
Форматы: CSV, фиксированная ширина, XML, EDI
CSV — самый частый. Строки-записи, поля через разделитель (запятая, точка с запятой, таб). Прост и читается глазами, но дьявол в деталях, которые аналитик обязан зафиксировать в спеке: какой разделитель, какая кодировка (UTF-8 или старая windows-1251 — частая боль в РФ), как экранируются запятые внутри значения, есть ли строка-заголовок, как записана дата и десятичный разделитель (1500.00 или 1500,00). Не проставишь — разойдётесь с контрагентом на первом же файле.
Фиксированная ширина (fixed-width) — наследие мейнфреймов: разделителей нет вообще, каждое поле занимает строго отведённое число символов, недостающее добивается пробелами или нулями. Поле «сумма» — позиции с 20-й по 31-ю, и точка. Хрупко (сдвинулся на символ — поехало всё), но банки и госсистемы всё ещё этим живут.
XML — когда нужна структура, вложенность и валидация по схеме (XSD): можно формально проверить, что файл соответствует контракту, до обработки. Многословен и тяжёл, но в гособмене и банках распространён именно из-за строгости. EDI (Electronic Data Interchange) — отдельный мир стандартизированных деловых документов (заказ, накладная, счёт) между компаниями; форматы вроде ANSI X12 и EDIFACT. Аналитику на старте достаточно знать, что EDI существует и что это тоже, по сути, структурированные файлы по расписанию — глубоко в него лезут на профильных проектах ритейла и логистики.
Расписание и окно обработки
Батч ходит по часам. Запускает его планировщик — классически cron (строчка вида «каждый день в 02:00»), в современных стеках это Airflow или встроенный шедулер. Здесь рождается первое требование аналитика — окно обработки: в какой интервал файл должен быть положен, забран и обработан. «Банк забирает файлы с 03:00 до 05:00» — значит мы обязаны положить свой до 03:00, иначе он уедет завтра. Опоздал на пять минут — потерял сутки.
Сразу за окном идут вопросы, которые легко забыть и больно ловить в проде: что делать, если предыдущий запуск ещё не закончился, а уже пора следующему (не запускать параллельно — поставить блокировку)? Что, если файла в окне не оказалось вовсе — это норма (нет данных) или авария (источник упал)? Молчание — плохой ответ, отсутствие файла должно явно отслеживаться.
Manifest-файл и идемпотентность по файлу
Возвращаюсь к своему инциденту из начала. Корень был в том, что приёмник не мог отличить «файл залит целиком» от «файл ещё дописывается». Решает это manifest-файл (он же control-файл, расширение часто .ctl или .done): маленький файл, который кладут последним, уже после того как большой файл данных полностью записан. Его появление и есть сигнал «данные готовы, можно забирать». Внутри manifest обычно несёт метаданные для проверки целостности: число строк в файле данных, контрольную сумму (чтобы поймать побитый при передаче файл) и итоговую сумму по ключевому полю.
Отсюда железный порядок выкладки: сначала пишем файл данных, и только когда он закрыт — кладём manifest. Приёмник ждёт именно manifest, а не файл данных. Этим одним приёмом закрывается весь класс проблем «прочитали недозалитое».
Второе обязательное требование — идемпотентность обработки файла. По сети и по людям файл может прийти дважды: пересоздали по ошибке, повторно выложили после сбоя, контрагент перезалил «на всякий случай». Обработать тот же файл дважды — значит задвоить платежи. Защита та же по духу, что и в API: приёмник ведёт реестр уже обработанных файлов (по имени, по контрольной сумме или по дате-идентификатору в имени) и повторный распознаёт как дубль — не обрабатывает заново. Это ровно та же логика идемпотентного ключа, что я детально разбирал в записи про идемпотентные платежи, только единицей защиты тут выступает не запрос, а целый файл.
sequenceDiagram participant S as Отправитель participant F as SFTP-папка participant B as Банк (приёмник) S->>F: 1. кладёт payments_2026-05-24.csv S->>F: 2. кладёт payments_2026-05-24.ctl (manifest) Note over F,B: банк ждёт именно .ctl как сигнал готовности B->>F: 3. видит .ctl → забирает оба файла B->>B: 4. сверяет: строк и сумма = manifest? B-->>F: 5. кладёт ack_2026-05-24.csv (квитанция) S->>F: 6. забирает квитанцию, сверяет итоги
Схема показывает дисциплину выкладки на примере ежедневной выгрузки платежей. Отправитель кладёт сначала файл данных, затем manifest — и именно появление manifest говорит банку «готово». Банк забирает оба файла, сверяет фактическое число строк и сумму с тем, что заявлено в manifest, и в ответ кладёт файл-квитанцию (ack): принято столько-то, отклонено столько-то. Отправитель забирает квитанцию и сверяет итоги у себя. Ответный файл здесь — это и есть обратная связь батча: вместо HTTP-кода ответа мы получаем отдельный файл, и проектировать его контракт надо так же тщательно, как контракт ошибок в синхронном API — об этом в записи про ошибки как часть контракта.
Битые строки, карантин и реконсиляция
Главная ловушка батча — соблазн логики «всё или ничего». Файл на миллион строк, в одной строке кривая дата — и наивный загрузчик падает, не загрузив ничего. Так делать нельзя. Зрелая обработка не падает на первой битой строке: валидные строки принимаются, невалидные отправляются в карантин (отдельный файл/таблицу ошибок) с указанием — какая строка и что именно не так. По итогу формируется отчёт об ошибках, который и уезжает в ответной квитанции.
Дальше встаёт честный продуктовый вопрос, и ответ на него — требование, а не деталь реализации: при частично битом файле мы загружаем валидное и репортим остальное, или отклоняем файл целиком? Для платёжного реестра нередко выбирают «всё или ничего» — лучше не провести ничего, чем половину; для справочника товаров — наоборот, грузим хорошее, ругаемся на плохое. Аналитик обязан проставить это явно.
И финальный контроль — reconciliation, сверка итогов. Недостаточно, что строки «легли». Надо сверить агрегаты: число записей и итоговую сумму у отправителя, в manifest и фактически загруженное у приёмника — все три должны сойтись. Не сошлось — стоп, разбор. Это та же дисциплина, что и при заливке исторических данных: в записи про миграции и бэкфилл сверка «источник = приёмник» — обязательный финал, и в батч-обмене она ровно так же не пропускается.
Имена файлов и manifest на примере той самой выгрузки в банк:
payments_2026-05-24.csv # данные: реестр платежей за день
payments_2026-05-24.ctl # manifest, кладётся последним
# содержимое payments_2026-05-24.ctl
FILE=payments_2026-05-24.csv
ROWS=14302
TOTAL_AMOUNT=48761500.00
CHECKSUM_SHA256=9f2a...e1c4
CREATED=2026-05-24T02:14:00Z
# приёмник читает .ctl, забирает .csv, считает строки и сумму,
# сверяет с ROWS и TOTAL_AMOUNT — расходится → файл в карантин
Дата прямо в имени файла играет двойную роль: она делает имя уникальным (основа дедупликации) и человекочитаемо говорит, за какой период данные. Это маленькое, но важное проектное решение.
Откуда это взялось
Батч-обработка — это вообще исходный способ, которым компьютеры считали: на мейнфреймах 1960-х программы прогоняли пачками («batch») по расписанию, потому что машинное время было дефицитом и его делили окнами. Файловый обмен между организациями оформился в EDI: американский стандарт ANSI X12 появился в конце 1970-х, международный EDIFACT — в 1980-х; на них до сих пор стоит обмен в ритейле, логистике и здравоохранении. SFTP как часть протокола SSH пришёл позже — во второй половине 1990-х, придя на смену небезопасному FTP. Поэтому «обмен файлами по расписанию» — не пережиток, а технология старше большинства API, которая просто продолжает работать там, где надёжность и совместимость важнее моды.
Как это спрашивают на собесе
Файловые интеграции любят спрашивать на банковских, биллинговых и интеграционных позициях — там их много. Типовые формулировки: «Как принимающая сторона понимает, что файл загружен целиком?» (ответ — manifest/control-файл, кладётся последним), «Чем SFTP отличается от FTP и почему берут SFTP?» (шифрование канала поверх SSH против открытого текста), «Файл случайно положили дважды — как не задвоить?» (идемпотентность по файлу: реестр обработанных, дедупликация по имени/контрольной сумме), «Что делать с битыми строками?» (карантин + отчёт об ошибках, а не падение целиком), «Как убедиться, что данные доехали верно?» (reconciliation — сверка числа строк и суммы). Сильный кандидат сам назовёт trade-off: батч проще и дешевле для объёмов и легаси, но платишь задержкой и тяжёлой обработкой ошибок.
Частые вопросы
Зачем нужен отдельный manifest-файл, если можно просто проверить, что файл данных существует?
Потому что «файл существует» и «файл записан целиком» — разные вещи. Большой файл по сети заливается не мгновенно; если приёмник начнёт читать его в процессе записи, он увидит половину и обработает обрезанные данные, ничего не заподозрив. Manifest решает это порядком: его кладут последним, уже после полной записи файла данных, поэтому его появление — надёжный сигнал «готово, забирай». Бонусом он несёт число строк и контрольную сумму, по которым приёмник проверяет, что файл не побился в пути.
Чем батч-интеграция принципиально хуже или лучше real-time API?
Это не «хуже/лучше», а разный инструмент под разную природу данных. Батч выигрывает на больших объёмах (один файл на миллион записей дешевле миллиона вызовов), на легаси-системах, которые «умеют только файл», и там, где данные по своей сути периодические (свод за день). Цена — задержка (это не real-time, реакция приходит в следующее окно), более тяжёлая обработка ошибок (битые строки, частично обработанный файл) и риск логики «всё или ничего» по файлу. Если бизнесу нужна мгновенная реакция на событие — это к API и асинхронным механизмам, а не к батчу.
Что делать, если в файле часть строк битые — грузить остальное или отклонять весь файл?
Это продуктовое решение, которое аналитик проставляет в спеке явно, а не отдаёт на откуп разработчику. Два режима. «Частичная загрузка»: валидные строки принимаются, битые уходят в карантин с отчётом об ошибках — подходит для справочников и некритичных данных. «Всё или ничего»: один сбой — отклоняем весь файл — выбирают для платёжных реестров и финансовых сводов, где полузагруженное состояние опаснее, чем незагруженное. В любом случае обязательны две вещи: явный отчёт, что именно не прошло и почему, и финальная reconciliation — сверка итогов, чтобы расхождение не уехало в прод тихо.