Import Linter: архитектурные правила импортов в CI

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

Есть ещё один простой инструмент для этой задачи — import-linter.

Он делает ровно то, чего часто не хватает в Python-проектах: проверяет, что импорты не ломают архитектурные границы.

Например:

  • handlers могут ходить в flows;
  • flows не должны знать про внешние интеграции;
  • models не должны тянуть за собой инфраструктуру;
  • соседние интеграции не должны импортировать друг друга напрямую.

Это не замена ruff или mypy. Они отвечают на вопросы «код отформатирован?» и «типы сходятся?». import-linter отвечает на другой вопрос: «мы всё ещё соблюдаем архитектурные договорённости?»

Минимальная идея выглядит так: [importlinter] root_package = app

[importlinter:contract:layers] name = Layered architecture type = layers

layers = handlers flows repositories models core После этого проверку можно запускать вместе с остальными линтерами: uv run --group linter lint-imports И если кто-то случайно импортирует репозиторий из модели — сборка падает. И это прекрасно, потому что ошибка ловится сразу, а не через полгода, когда вы решаете сделать рефакторинг и обнаруживаете, что всё переплетено.

В качестве примера можно посмотреть на конфигурацию django-modern-rest

Как внедрять без боли: 1. Описать текущую архитектуру, а не идеальную. 2. Легаси-нарушения временно занести в ignore_imports. 3. Запретить новые нарушения в CI. 4. Постепенно убирать исключения во время рефакторинга.

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