16 мая 2025 г.·7 мин чтения

Библиотеки workflow Go для приложений с частыми согласованиями и понятными правилами

Сравните библиотеки workflow Go для приложений с частыми согласованиями. Посмотрите, как пакеты обрабатывают шаги, повторы, таймауты и согласования с участием людей без путаницы из if-цепочек.

Библиотеки workflow Go для приложений с частыми согласованиями и понятными правилами

Почему логика согласований превращается в цепочки if

Согласования редко начинаютcя беспорядочно. Команда стартует с одного простого правила: менеджер утверждает заявку, а потом финансы проверяют её, если сумма достаточно высокая. Такое кажется легко написать несколькими условиями в обработчике.

Проблемы начинаются, когда одно правило зависит сразу от трёх или четырёх факторов. Одна и та же закупка может идти по разному пути в зависимости от роли заявителя, общей суммы, срока или того, новый ли это поставщик. Срочная заявка, которую нужно закрыть сегодня, часто идёт иначе, чем та же заявка, если она нужна только на следующей неделе.

Потом приходят изменения в политике. Финансы хотят второе согласование после нового порога. Юристы хотят проверку для определённых условий контракта. Руководитель отдела может утвердить заявку для одной команды, но не для другой. Большинство команд не останавливаются и не проектируют весь процесс заново. Они добавляют ещё один if, потому что так быстрее на этой неделе.

Через несколько месяцев правила уже живут не в одном месте. Повторы для неудачных уведомлений лежат в одной задаче. Письма-напоминания отправляет другой воркер. Проверки таймаута находятся в планировщике. API-обработчик всё ещё решает, может ли человек утвердить, отклонить, отправить заявку заново или вернуть её на доработку. Когда заявка застревает, никто не видит весь путь, пока не откроет несколько файлов.

Люди только усугубляют ситуацию. Кто-то уходит в отпуск. Кто-то передаёт право согласования запасному сотруднику. Заявитель меняет сумму после отказа, поэтому заявку нужно вернуть лишь на часть процесса, но не на весь. Каждый отдельный случай кажется мелким. Вместе они превращаются в кучу условий, которым уже никто не доверяет полностью.

Команды ещё и теряют из виду, кто вообще может сдвинуть заявку дальше. Один сервис проверяет роли, другой — владельца, третий — статус. Поддержка спрашивает, почему заявка всё ещё ждёт, а инженерам приходится разбирать правило вручную.

Вот обычно в этот момент библиотеки workflow для Go и библиотеки конечных автоматов Go начинают выглядеть не как лишняя сложность, а как обычная поддержка порядка. Они дают правилам одно место вместо того, чтобы прятать их в разбросанных if-ах.

Что должна уметь хорошая библиотека

Приложения для согласований ломаются, когда инструмент умеет только хранить поле статуса. В реальной работе есть паузы, передачи, дедлайны и люди, которые передумывают. Полезная библиотека должна аккуратно моделировать весь этот хаос.

Начните со состояний, которые совпадают с тем, как процесс реально работает. Заявка может перейти из черновика на проверку у менеджера, потом ждать финансы, а затем закончиться как одобренная, отклонённая или отменённая. Ожидание важно не меньше действия. Если библиотека не может представить заявку, которая просто ждёт человека, таймер или документ, правила снова окажутся в разбросанных if-ах.

Для согласования заявок в Go действия должны содержать больше, чем просто «да» или «нет». Люди оставляют комментарии, просят изменить что-то, передают задачу другому или утверждают с условиями. Библиотека должна хранить эти события как часть процесса, а не как случайные заметки сбоку. Так проще разбираться в человеческих согласованиях, когда кто-то спрашивает: «Почему эта заявка зависла на три дня?»

Время — тоже часть модели, а не мелкое дополнение. Хорошие библиотеки workflow для Go должны позволять задавать дедлайны, повторять неудачные шаги и запускать эскалацию, если никто не действует. Простой пример — заявка на закупку, которая ждёт менеджера 48 часов, через 24 отправляет напоминание, а потом уходит директору, если таймер истёк. Если повторы и таймауты живут вне workflow, команды обычно забывают крайние случаи.

Ещё нужна понятная история того, кто что сделал и когда. Имена, отметки времени, комментарии и смены состояний должны легко читаться в одной ленте событий. Такой журнал аудита помогает с поддержкой, внутренними спорами и просто повышает доверие к системе.

Не менее важны и guard-правила. Проверяющий не должен одобрять заявку, которая уже отклонена. Финансы не должны действовать до того, как подпишет менеджер. Библиотека должна заранее блокировать неверные переходы, причём правила должны читаться без десяти вспомогательных функций.

Если пакет хорошо работает со состояниями, действиями людей, таймерами, историей и guard-правилами, он даёт workflow, который можно объяснить вслух. Обычно это хороший знак.

Какие пакеты стоит посмотреть

Среди библиотек workflow для Go и библиотек конечных автоматов Go эти четыре закрывают большинство сценариев с частыми согласованиями. Они решают разные задачи, поэтому сначала важно понять одно: вам нужны надёжные workflow, которые могут ждать часами или днями, или достаточно аккуратных переходов состояний внутри одного сервиса?

  • Temporal подходит для долгих workflow с человеческими согласованиями. Если менеджер одобрит только на следующей неделе, workflow всё равно сможет продолжиться с правильного шага после деплоя, перезапуска или падения воркера. Он также даёт повторы, таймеры и шаблоны компенсации без кучи собственного кода. Цена — сложность. Нужно принять более крупную модель выполнения.
  • go-workflows имеет смысл, когда вам нужна оркестрация на обычном Go-коде и не хочется сразу переходить на более крупную платформу. Он ближе к обычному коду приложения, а многим командам так легче читать и тестировать. Для команд, которые хотят, чтобы логика workflow оставалась в Go, это разумный компромисс.
  • looplab/fsm хорошо подходит, когда один сервис владеет простым процессом согласования и переходы прямые. Заявка может перейти из черновика в отправленную, затем в одобренную или отклонённую, а после — в оплаченную. Для многих внутренних инструментов этого достаточно. Правила живут в одном месте, а не размазываются по обработчикам, задачам и вспомогательным функциям.
  • qmuntal/stateless — лучший выбор, когда важнее всего триггеры и условия проверки. Если заявка может двигаться только когда есть бюджет, отдел совпадает с политикой и правильный человек уже одобрил, guard-условия делают эти правила явными. Потом менять их намного проще.

Обычная очередь задач тоже нужна. Используйте её для фоновой работы вроде отправки напоминаний или повтора неудачного webhook. Но не считайте её движком workflow, если вам действительно не нужны правила состояний, история аудита или надёжные таймауты после перезапуска. Многие команды начинают с очереди, потому что она кажется маленькой, а потом вручную заново строят повторы и таймауты workflow.

Коротко это можно разделить так: Temporal — на стороне надёжных, долгих процессов, looplab/fsm и stateless — на стороне моделирования состояний, а go-workflows — посередине. Сначала решите, где находится ваше приложение, и только потом сравнивайте API.

Как сравнивать пакет шаг за шагом

Начните с одного процесса, который уже точно кажется запутанным. Хорошо подойдёт заявка на закупку: сотрудник её отправляет, менеджер утверждает, финансы проверяют бюджет, а если два дня никто не действует, заявка истекает. Если пакет не может описать это аккуратно, пропускайте его.

Многие команды сравнивают библиотеки workflow для Go, просто читая списки возможностей. Это слабая проверка. Гораздо лучше сначала нарисовать на бумаге один реальный процесс со всеми состояниями и передачами. Сделайте его небольшим, но достаточно настоящим, чтобы там были задержки, отклонённые заявки и человек, который передумал.

Используйте один и тот же чеклист для каждого пакета:

  • Опишите процесс через состояния и события, а не через код. Включите отправку, одобрение, отказ, отмену и истечение срока.
  • Отметьте каждое место, где система ждёт. Ожидание менеджера, таймаут через 48 часов и повтор после неудачного письма — это разные вещи.
  • Решите, где действуют люди. Одни пакеты хорошо справляются с человеческими согласованиями, а другие в основном предполагают шаги между системами.
  • Сразу проверьте хранилище. Нужно понимать, где живёт текущее состояние, как пакет записывает прошлые действия и можно ли потом увидеть, кто что одобрил.
  • Сломайте сценарий специально. Смоделируйте таймаут, повторный клик по

Поток заявки на закупку простыми словами

Автоматизируйте согласования без хаоса
Получите практическую помощь, чтобы перевести ручные процессы в понятное и надёжное ПО для команды.

Представьте, что сотрудник хочет купить новый ноутбук дороже, чем команда может утвердить сама. Приложению не нужна паутина проверок. Ему нужен небольшой набор состояний: черновик, отправлено, проверка менеджером, проверка финансов, одобрено, отклонено, возвращено и истекло по таймауту.

Когда сотрудник нажимает отправку, система записывает сумму, деловую причину и первого проверяющего. Если в заявке не хватает цены, поставщика или цели, приложение должно остановить её ещё до начала проверки. Это сильно сокращает переписку туда-обратно.

Менеджер принимает первое человеческое решение. Он может одобрить заявку, отклонить её или вернуть с примечанием вроде «Нужна вторая цена» или «Объясните, почему текущего устройства недостаточно». Возврат не должен стирать ничего. Сотрудник редактирует заявку, комментарии сохраняются, и он отправляет её снова.

Если менеджер одобряет, финансы проверяют бюджет. Это отдельная задача. Финансы могут согласиться, что ноутбук нужен, и всё равно остановить заявку, потому что бюджет уже израсходован, выбран не тот центр затрат или у команды уже открыт похожий запрос.

Молчание создаёт собственные проблемы. Если никто не действует в течение одного дня, система должна напомнить текущему проверяющему. Если проходит ещё больше времени, приложение может эскалировать задачу другому человеку, назначить её заново или закрыть как просроченную. Таймаут — это не ошибка. Это один из допустимых финалов.

Повторы тоже важны, но в основном для системных задач. Если приложению не удаётся отправить письмо, повторите отправку. Если не проходит вызов к API бюджета, повторите этот вызов. Не повторяйте человеческое решение так, будто это сетевой запрос. Людям нужны напоминания, сроки и правила эскалации.

В workflow для Go такой процесс остаётся читаемым, когда у каждого состояния есть понятный смысл, небольшой набор разрешённых действий и видимый следующий шаг. Тогда любой сможет открыть заявку и ответить на два простых вопроса: кто владеет ею сейчас и что должно произойти, прежде чем она сдвинется дальше?

Где таймауты и повторы становятся сложными

Команды часто считают таймауты и повторы одной функцией. Это не так.

Повтор помогает, когда ломается системный шаг. Письмо не дошло. Webhook не успел ответить. Очередь сообщений потеряла доставку. В этих случаях приложение может попробовать ещё раз, потому что человек ещё ничего не решил.

Человеческое промедление — другое дело. Если менеджер не утвердил закупку за два дня, повторять один и тот же шаг согласования каждый час не поможет. Это только создаст шум и сделает процесс менее надёжным.

Небольшая заявка на закупку хорошо показывает разницу. Если приложение не смогло отправить запрос на согласование в Slack, повторите доставку. Если менеджер его игнорирует, запустите правило таймаута. Это два разных пути, и хорошая библиотека должна позволять моделировать оба без костылей.

Сделайте каждый таймаут явным

Таймауту нужен назначенный владелец и понятный следующий шаг. Если у таймаута нет владельца, заявка зависнет в неопределённости.

Держите эти правила видимыми в состоянии процесса:

  • кто владеет текущим согласованием
  • когда истекает таймаут
  • что произойдёт дальше
  • можно ли эскалировать процесс или его нужно остановить
  • сколько попыток доставки уже было

Последний пункт важнее, чем многие команды ожидают. Если счётчики повторов живут только внутри воркера или потребителя очереди, workflow не может их прочитать. Тогда бизнес-правило превращается в разбросанный код вместо одного понятного процесса.

Напоминания создают ещё одну ловушку. Напоминание должно указывать на тот же открытый элемент согласования, а не создавать новый. Иначе один человек может одобрить заявку A, а другой через пять минут отклонит дубликат. У вас получится две записи и одна путаница.

У правил эскалации тоже должен быть жёсткий стоп. Как только кто-то дал окончательный ответ — одобрено, отклонено или отменено — все ожидающие напоминания, повторы и таймеры эскалации должны завершиться. Поздние задачи должны сначала проверить текущее состояние и тихо выйти.

Если библиотека упрощает идемпотентные напоминания, видимые счётчики и условия остановки, потом она сэкономит много уборки.

Чем отличаются конечные автоматы и движки workflow

Подключите поддержку CTO
Используйте опыт старшего технического специалиста для выбора workflow, инфраструктуры и плана запуска.

Конечный автомат отвечает на один узкий вопрос: «Что может случиться дальше?» Если заявка находится в состоянии «черновик», она может перейти в «отправлено» или «отменено». Если она уже «одобрена», она не должна прыгать обратно в «черновик», если только вы не разрешили этот ход специально. Это делает конечный автомат хорошим для защиты правил и блокировки плохих переходов.

Внутри одного сервиса Go этого часто достаточно. Код остаётся легче. Вы можете держать разрешённые состояния в одном месте вместо того, чтобы прятать их в обработчиках, cron-задачах и вспомогательных функциях. Для небольшого workflow в Go это уже убирает много грязных if-ов.

Движок workflow решает другую задачу. Он отслеживает работу, которая длится минуты, часы или дни. Он может поставить процесс на паузу, пока менеджер проверяет заявку, ждать таймаут, повторить неудачный шаг и продолжить после деплоя или перезапуска процесса. Такая память важна, когда человеческие согласования зависят от людей, сообщений и внешних систем.

Разница обычно простая:

  • Конечный автомат управляет допустимыми переходами между состояниями.
  • Движок workflow управляет долгими ожиданиями, повторами, таймерами и логикой возобновления.
  • Конечный автомат лучше подходит, когда всем процессом владеет один сервис.
  • Движок workflow лучше, когда согласования проходят через несколько сервисов или команд.

Подумайте о заявке на закупку. У самой заявки могут быть понятные состояния вроде черновика, отправлено, одобрено, отклонено и оплачено. Конечный автомат может защитить эти переходы. Но если финансы должны ответить в течение 48 часов, а система должна напомнить им, потом эскалировать директору и ещё повторить сбойную синхронизацию с бухгалтерией, с этим лучше справится движок workflow.

Некоторые команды используют оба подхода. Они держат простой конечный автомат в основном сервисе, чтобы бизнес-правила оставались понятными. А для медленных частей запускают движок workflow: ждут людей, повторяют внешние вызовы и продолжают работу после сбоев. Такая гибридная схема часто встречается у команд, которые сравнивают библиотеки workflow для Go и библиотеки конечных автоматов Go, потому что эти инструменты решают разные проблемы.

Ошибки, которые потом причиняют боль

Приложения для согласований обычно ломаются в самых скучных местах. Команда выпускает счастливый путь, потом реальная заявка перераспределяется, снова открывается или отменяется на полпути, и правила начинают расползаться по кодовой базе.

Одна ошибка проявляется рано: проверки ролей жёстко зашиты в обработчики. Строка вроде «if user is manager» кажется быстрой в написании, но со временем она плохо живёт. Через несколько месяцев финансы хотят отдельное согласование, запасной согласующий подменяет основного, или руководитель отдела может отменить отказ. Теперь каждый endpoint содержит свою версию одного и того же правила. Держите роли и переходы в логике workflow, а не в обработчиках запросов.

Другая проблема — смешивать изменение состояния с побочными эффектами. Если одна функция обновляет заявку, отправляет письмо, постит сообщение в чат и запускает выплату, повторы становятся рискованными. Можно отправить два сообщения на одно одобрение или списать деньги дважды после таймаута. Сначала переведите заявку в новое состояние, зафиксируйте изменение, а потом запускайте последующие действия отдельными безопасными для повторов задачами.

Ручные действия тоже требуют понятного следа. Когда кто-то утверждает, отклоняет, передаёт задачу или добавляет заметку, сохраняйте, кто это сделал, когда и почему. Без этой истории поддержка гадает, менеджеры спорят, а аудит превращается в расследование. Простой журнал событий часто решает больше проблем, чем команды ожидают.

Повторы тоже используют не по назначению. Они помогают, когда не доставилось письмо или сервис на короткое время упал. Они не лечат плохой процесс. Если заявка лежит три дня только потому, что никто не владеет следующим шагом, дополнительные повторы лишь скрывают реальную проблему.

Сценарии, которые команды забывают тестировать, обычно и причиняют больше всего боли:

  • повторное открытие после отказа
  • отмена, пока напоминание ещё в очереди
  • переназначение согласования, когда первый согласующий вне офиса
  • одобрение после того, как предупреждение о таймауте уже сработало

Вот где помогают библиотеки workflow для Go и библиотеки конечных автоматов Go. Они заставляют команды называть переходы, таймеры и человеческие действия. Если пакет делает пути повторного открытия или отмены неудобными, эта боль потом попадёт в продакшн.

Что проверить перед внедрением

Проектируйте под долгие ожидания
Спланируйте согласования, которые переживут перезапуски, задержки решений и сбои внешних систем.

Многие библиотеки workflow для Go выглядят одинаково, пока вы не проверите их на реальной работе с согласованиями. Красивого API недостаточно, если менеджер одобряет заявку через три дня, кто-то уходит в отпуск или один сбойный шаг оставляет команду в догадках о том, что произошло.

Начните с отложенных человеческих действий. Ваш workflow должен сохранять весь контекст, пока задача ждёт: кто её отправил, зачем нужно согласование, что изменилось и какое следующее действие. Если пакет заставляет собирать этот контекст заново из таблиц базы и старых сообщений, быстро начнётся хаос.

Потом проверьте видимость. Коллега должен уметь ответить на вопрос «Где эта заявка застряла?» без чтения логов и поиска по цепочке вызовов. Хорошие инструменты делают текущее состояние очевидным и показывают последнее событие, возможное следующее действие и владельца.

Короткий пробный запуск обычно сразу показывает слабые места:

  • Поставьте заявку на паузу на несколько дней, потом возобновите её и посмотрите, легко ли читать полную историю.
  • Специально сломайте один шаг и проверьте, показывает ли пакет ошибку, число повторов и текущий статус в одном месте.
  • Измените одно правило согласования, например увеличьте лимит расходов для подписи директора, и посчитайте, сколько файлов нужно править.
  • Покажите список состояний или схему процесса продукт-менеджеру или руководителю операций и попросите его пересказать вам, как он это понял.
  • Попросите нового инженера найти все пути, которые ведут к отказу или таймауту.

Третий тест важнее, чем многие думают. Если одно изменение политики отправляет вас в обработчики, cron-задачи и разбросанные условия, пакет на самом деле не моделирует процесс. Он просто прячет if-ы под новыми названиями.

Для согласования workflow в Go я бы скорее выбрал более простой инструмент, который чётко показывает состояние, чем умный, который выглядит магически на демо. Через шесть месяцев ясность почти всегда важнее хитрости.

Что делать дальше

Начните с одного процесса, который уже каждую неделю создаёт мелкие, но надоедливые проблемы. Подойдёт согласование расходов или запрос доступа. Выберите что-то с двумя или тремя шагами согласования, одним дедлайном и одним местом, где человек может отказать или вернуть заявку.

Сначала соберите этот процесс в небольшом сервисе на Go. Держите пилот узким. Вам нужно понять, где правила выглядят понятно, а где они всё ещё просачиваются в случайные обработчики, cron-задачи или админские скрипты.

Простой первый тест выглядит так:

  • один человек отправляет заявку
  • один менеджер её одобряет или отклоняет
  • финансы или операционный отдел дают финальное согласование
  • система отправляет одно напоминание после пропущенного дедлайна
  • заявка закрывается с понятным финальным состоянием

Внимательно наблюдайте за скучными сбоями. Они скажут вам больше, чем когда-либо скажет чистый демо-сценарий. Отслеживайте пропущенные напоминания, двойные клики, повторы, которые запускаются дважды, и заявки, которые зависают в состоянии без владельца. Если команда не может за несколько секунд ответить на вопрос «что случилось с этой заявкой?», модель ещё требует доработки.

Запишите, где простой конечный автомат перестаёт быть достаточным. Эта граница важна. Небольшой FSM часто работает, когда процесс короткий, состояний мало, а люди в основном действуют по порядку. Движок workflow начинает иметь смысл, когда нужны таймеры, повторы, журнал аудита, шаги компенсации или долгие задачи в нескольких сервисах.

Не переносите решение на всю компанию после одного чистого теста. Прогоните пилот пару недель, устраните шероховатости и только потом переносите следующий процесс на ту же схему.

Если вы выбираете между библиотеками workflow для Go и более простым конечным автоматом, внешний взгляд может сэкономить время. Oleg Sotnikov помогает командам сопоставить компромиссы, заранее увидеть риски внедрения и выбрать подход, который подходит продукту, размеру команды и бюджету, а не гнаться за инструментом крупнее, чем нужно.