Внутренний скрипт стал бизнес-критичным: минимальный набор мер за один день
Полгода назад это был скрипт на двадцать строк, который вы написали за вечер, чтобы не собирать отчёт руками каждую пятницу. Сейчас от него зависит бухгалтерия, склад или рассылка клиентам, а лежит он на вашем ноутбуке, без единой строки логов и без документации, кроме той, что у вас в голове. Это не история про чью-то небрежность — так становится критичным почти любой внутренний инструмент. Ниже план на один день: пять мер, которые закрывают самые дешёвые и самые опасные риски, без превращения скрипта в полноценный продакшен-сервис за сутки.
Содержание
- Как скрипт «для себя» становится критичным незаметно для всех
- Мера 1: код должен жить не только на одном ноутбуке
- Мера 2: минимальное логирование — видно, что происходит при сбое
- Мера 3: два предложения о том, что скрипт делает и зачем
- Мера 4: расписание и подтверждение выполнения, если скрипт должен работать регулярно
- Мера 5: сбой не должен безвозвратно портить данные
- Что не нужно делать за один день
Как скрипт «для себя» становится критичным незаметно для всех
У этого сценария всегда одна и та же механика. Кто-то в команде — часто не разработчик, а аналитик, бухгалтер или менеджер с базовыми навыками Python или Bash — пишет скрипт, чтобы автоматизировать конкретную рутину: выгрузить отчёт, разложить файлы по папкам, отправить уведомление, синхронизировать пару таблиц. К нему не предъявляют требований надёжности, потому что в момент написания это был просто способ сэкономить полчаса лично себе. Никто не проектировал обработку ошибок, потому что не было цели «сделать сервис» — была цель «сделать один раз и забыть».
Дальше скрипт начинает использовать кто-то ещё. Потом на его вывод начинает полагаться другой процесс — например, файл, который он генерирует, забирает бухгалтерская система. Потом задачу ставят в cron, чтобы не запускать руками. И в какой-то момент оказывается, что если скрипт не отработает ночью, утром сорвётся отгрузка или не пройдёт платёж — а несёт эту ответственность код, который никто не пересматривал с момента написания, и который держится в голове одного человека.
Проблема в том, что переход от «удобной мелочи» к «критичной части процесса» почти никогда не сопровождается решением «а теперь надо повысить требования к надёжности». Он происходит постепенно, через накопление зависимостей, и осознаётся обычно уже после первого серьёзного сбоя. Если вы сейчас читаете эту статью, скорее всего, точка осознания уже пройдена — скрипт уже критичен, вопрос в том, что делать в первую очередь. Дальше — пять мер, которые можно закрыть буквально за один рабочий день, без выделенного спринта и без пересборки архитектуры.
Мера 1: код должен жить не только на одном ноутбуке
Это самый дешёвый и самый важный пункт списка, и с него стоит начинать в первую очередь. Если единственная копия скрипта — файл на личном компьютере одного человека (пусть даже с бэкапом в облачную папку), у бизнеса нет реальной сохранности кода: сгоревший диск, потерянный ноутбук или обычное «переустановил систему и забыл скопировать» обнуляют критичный процесс мгновенно и без предупреждения.
Минимум на один день — завести git-репозиторий и туда всё перенести:
# на машине, где сейчас лежит скрипт
cd /path/to/script
git init
git add .
git commit -m "Первый коммит: перенос критичного скрипта в репозиторий"
# на сервере с приватным Gitea/Gitlab, или в корпоративном GitHub/GitLab
git remote add origin git@your-git-host:team/internal-scripts.git
git push -u origin main
Если в компании ещё нет собственного git-сервера, самовольный Gitea или Forgejo поднимается на небольшом VPS за час — это отдельная задача, но для сегодняшнего дня подойдёт и бесплатный приватный репозиторий на GitHub или GitLab, если политика безопасности это допускает. Важно не «выбрать идеальное решение», а сделать так, чтобы код перестал существовать в единственном экземпляре.
Второй момент — доступ. Мало залить код в репозиторий, если единственный человек с правами на него всё тот же один сотрудник. Дайте доступ на чтение (как минимум) ещё одному-двум людям: техническому руководителю, второму разработчику, любому, кто в теории сможет найти и запустить скрипт, если автор окажется недоступен. Это прямое продолжение темы bus factor, равного единице — весь смысл переноса кода в общее хранилище теряется, если доступ к хранилищу тоже держит один человек.
Если скрипт использует какие-то секреты — токены API, пароли к базе, ключи доступа к облаку — не коммитьте их в репозиторий вместе с кодом, даже приватный. Вынесите в переменные окружения или отдельный .env-файл, добавленный в .gitignore, а сами значения храните в менеджере паролей команды или в секретах CI/CD. Это не займёт больше 15–20 минут, а исправлять утёкший в git-историю пароль потом — гораздо дороже по времени.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверМера 2: минимальное логирование — видно, что происходит при сбое
Второй по дешевизне и по отдаче пункт — логи. Не система мониторинга, не сбор метрик в Grafana, а самое базовое: запись в файл о том, что скрипт запустился, что он сделал и чем закончился. Разница между «скрипт без единой строки логов» и «скрипт, который пишет три строки в файл» — это разница между расследованием на несколько часов вслепую и пятиминутной проверкой tail.
Показательный пример того, к чему приводит полное отсутствие логов — разбор реального инцидента, когда скрипт бэкапа падал молча четыре месяца: ошибка глушилась внутри пайпа, файлы бэкапа создавались пустыми, и никто не замечал этого до момента, когда бэкап реально понадобился. Пары строк логирования кода возврата хватило бы, чтобы поймать проблему в первую же неделю.
Минимальный вариант на Python — не библиотека логирования, а просто дописывание в файл с меткой времени и кодом результата:
import datetime
import sys
import traceback
LOG_FILE = "/var/log/internal-scripts/report-export.log"
def log(message):
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(LOG_FILE, "a") as f:
f.write(f"[{ts}] {message}\n")
def main():
log("START")
try:
# основная логика скрипта
run_export()
log("OK: экспорт завершён успешно")
except Exception as e:
log(f"FAIL: {e}\n{traceback.format_exc()}")
sys.exit(1)
if __name__ == "__main__":
main()
Для bash-скриптов достаточно перенаправить вывод в файл с датой в имени и явно логировать код завершения:
#!/usr/bin/env bash
LOG="/var/log/internal-scripts/sync-$(date +%Y-%m-%d).log"
exec >> "$LOG" 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] START"
# основная логика
rsync -a /data/source/ /data/target/
STATUS=$?
if [ $STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] OK"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] FAIL: rsync exit code $STATUS"
fi
Обратите внимание на две вещи в примерах выше. Во-первых, ошибка не проглатывается — код возврата и текст исключения явно попадают в лог, а не теряются где-то в середине пайпа, как в истории с четырёхмесячным сбоем бэкапа. Во-вторых, не нужно городить ротацию логов или структурированный JSON — для одного дня достаточно, чтобы файл существовал и в нём было видно, что происходило при последнем запуске. Переезд на что-то вроде Graylog или Grafana Loki можно сделать позже — это не блокирует сегодняшний минимум.
Мера 3: два предложения о том, что скрипт делает и зачем
Следующий пункт занимает пятнадцать минут, а спасает часы чужого времени в будущем. Прямо в начале файла или в отдельном README.md рядом со скриптом опишите двумя-тремя предложениями: что скрипт делает, откуда берёт данные, куда их кладёт, как часто должен запускаться и кого спросить, если что-то непонятно.
# report-export.py
Раз в сутки в 03:00 (через cron) выгружает данные заказов за прошедший день
из базы `orders_db` в CSV и кладёт в `/data/exports/YYYY-MM-DD.csv`.
Файл забирает бухгалтерская 1С-интеграция каждое утро в 07:00.
Если экспорт не отработал — заказы за день не попадут в отчётность
до ручного перезапуска. Автор: Иван П., резервный контакт: Анна С.
Это не техническая документация в привычном смысле — не нужно описывать каждую функцию или переменную. Задача — чтобы человек, который не писал этот код, за одну минуту чтения понял три вещи: зачем скрипт вообще существует, что случится, если он не отработает, и к кому идти с вопросами. Такого минимума достаточно, чтобы скрипт не превратился в чёрный ящик, который боятся трогать.
Важный нюанс: сам код скрипта — не замена документации, даже если он написан читаемо. Код фиксирует, как именно что-то делается, но не отвечает на вопрос «зачем» и «что будет, если это сломается» — а именно эти два вопроса чаще всего задают в момент, когда автора нет рядом.
Мера 4: расписание и подтверждение выполнения, если скрипт должен работать регулярно
Если скрипт запускается регулярно, к моменту, когда вы читаете эту статью, он уже наверняка либо стоит в cron, либо (что хуже) полагается на то, что кто-то не забудет запустить его руками. Второй вариант для критичного процесса не работает: рано или поздно человек уйдёт в отпуск, заболеет, забудет — и процесс встанет без единого сигнала об этом.
Минимум на сегодня — если задача ещё не в cron, поставить её туда:
# crontab -e
# Экспорт отчёта каждый день в 03:00
0 3 * * * /usr/bin/python3 /opt/scripts/report-export.py >> /var/log/internal-scripts/cron.log 2>&1
Но одного cron недостаточно: он молча проглатывает несделанную работу так же охотно, как молча делает свою — если задача не запустилась (сервер был перезагружен, диск был полон, пользователь заблокирован), никакого уведомления об этом не будет. Второй шаг, который занимает буквально десять минут — подключить подтверждение выполнения через внешний heartbeat-сервис: скрипт должен «отчитаться», что он отработал, и если сигнала нет вовремя — придёт алерт.
#!/usr/bin/env bash
LOG="/var/log/internal-scripts/sync-$(date +%Y-%m-%d).log"
exec >> "$LOG" 2>&1
rsync -a /data/source/ /data/target/
STATUS=$?
if [ $STATUS -eq 0 ]; then
curl -fsS -m 10 --retry 3 https://hc-ping.com/ваш-uuid-задачи > /dev/null
fi
exit $STATUS
Как именно устроен такой пинг и что делать, если хочется self-hosted вариант без внешнего сервиса — в статье про Healthchecks.io и мониторинг cron-задач. Смысл в том, что вместо надежды «cron наверняка отработал» вы получаете конкретный сигнал: если пинг не пришёл в ожидаемое окно, кто-то узнаёт об этом в течение минут, а не в момент, когда отсутствие отчёта заметит бухгалтерия через два дня.
Мера 5: сбой не должен безвозвратно портить данные
Последняя мера из минимального набора — самая концептуальная, но и её можно закрыть на базовом уровне за оставшееся время дня. Вопрос простой: что произойдёт, если скрипт упадёт на середине выполнения — при обрыве сети, нехватке места на диске, случайном перезапуске сервера? Если ответ «часть данных потеряется без возможности восстановления» или «повторный запуск задвоит записи и всё сломает ещё сильнее» — это дыра, которую стоит закрыть в первую очередь.
Базовая идемпотентность не требует переписывать логику с нуля. Для большинства скриптов достаточно одного из трёх простых приёмов:
- Пишите во временный файл и переименовывайте в конце. Если скрипт формирует отчёт или экспорт, пусть он пишет в
report.csv.tmp, а переименовывает вreport.csvтолько после успешного завершения. Тогда сбой на середине оставляет старый рабочий файл нетронутым, а не половину нового. - Делайте операции записи в БД через upsert, а не insert. Если скрипт должен создавать или обновлять записи, используйте
INSERT ... ON CONFLICT DO UPDATE(PostgreSQL) или аналог для вашей СУБД вместо простогоINSERT. Тогда повторный запуск после сбоя не создаст дублей — он просто повторно применит те же данные. - Ставьте lock-файл на время выполнения. Если скрипт не должен запускаться параллельно с самим собой (частая причина порчи данных — предыдущий запуск ещё не закончился, а cron уже стартовал следующий), простая проверка на файл-флаг решает проблему:
LOCKFILE="/tmp/report-export.lock"
if [ -e "$LOCKFILE" ]; then
echo "Уже выполняется, выходим"
exit 0
fi
trap 'rm -f "$LOCKFILE"' EXIT
touch "$LOCKFILE"
# основная логика скрипта
Глубже про то, почему именно повторный запуск — а не единичный сбой — чаще всего оказывается причиной реального ущерба (например, задвоенного списания денег или дублирующейся отправки клиенту), можно прочитать в статье про идемпотентность на пальцах. Для целей одного дня достаточно honestно ответить на вопрос «что будет при повторном запуске после сбоя» и закрыть хотя бы самый очевидный сценарий порчи данных — полную защиту от всех классов сбоев за день не выстроить, и не нужно этого обещать себе или команде.
Что не нужно делать за один день
Соблазн, встретившись со списком выше, — расширить его до полноценного проекта: добавить тесты, обернуть в Docker, продумать retry-политику, настроить алерты по всем метрикам. Для критичного продакшен-сервиса это оправданно, но не за один день и не в качестве первой реакции на осознание критичности. У минимального набора мер другая цель — закрыть риски, которые дорого стоят при реализации и дёшево устраняются: потерю единственной копии кода, полную слепоту при сбое, знание в одной голове, надежду вместо расписания и порчу данных при повторном запуске.
Более глубокая переработка — перевод скрипта в полноценный сервис с супервизором вроде systemd, очередью задач или мониторингом метрик — отдельная задача с другим горизонтом, и решение о ней стоит принимать осознанно, оценив реальную стоимость простоя, а не в панике после первого сбоя. На конец августа 2026 года для подавляющего большинства внутренних скриптов пяти описанных мер достаточно, чтобы снять основной риск и выиграть время на спокойное решение о дальнейшей судьбе инструмента.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
У нас нет своего git-сервера и нельзя использовать публичный GitHub. Что делать?
Приватный репозиторий на GitHub или GitLab с ограниченным доступом обычно не считается «публичным» в смысле безопасности. Если политика компании запрещает и это, поднимите Gitea или Forgejo на отдельном VPS — это лёгкие приложения, разворачиваются за час.
Стоит ли сразу переписывать скрипт на более надёжном языке или фреймворке?
Нет, не в рамках этого дня. Смена языка или архитектуры — отдельная задача с собственными рисками, и она не решает ни одной из пяти проблем из списка выше. Сначала закройте сохранность, видимость и повторяемость, а переписывание решайте отдельно и без спешки.
Что делать, если скрипт запускает не cron, а другой сервис или внешняя система?
Логика та же: минимальное логирование и подтверждение выполнения всё равно нужны, просто вместо cron-обвязки вы добавляете вызов heartbeat-пинга в точке, где вызывающая система считает задачу завершённой. Зависимость от внешнего триггера тоже стоит упомянуть в документации из меры 3.
Мы уже используем скрипт полгода без единого сбоя — точно ли он «критичен»?
Отсутствие сбоёв ничего не говорит о критичности — оно говорит только о том, что сбоя ещё не было. Критичность определяется тем, что случится с бизнесом, если скрипт откажет завтра, а не тем, сколько раз он уже отработал успешно.
Можно ли сделать всё из списка за один день реально, а не формально?
Да, если не доводить каждый пункт до идеала. Перенос кода в репозиторий — час с учётом доступа. Логирование — час-два. Документация в два предложения — пятнадцать минут. Расписание с heartbeat-пингом — час. Базовая идемпотентность — час-два. В сумме укладывается в рабочий день с запасом.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →