Код без мусора: как проектировать архитектуру, которая сама себя убирает

от автора

Мы привыкли бороться с «мусором» в коде — временными костылями, устаревшими методами, забытыми конфигами. Но что если можно проектировать систему так, чтобы она сама чистилась от ненужного, минимизируя технический долг прямо в процессе работы? В статье попробую показать, что это не миф, а вполне реальная практика, основанная на архитектурных паттернах, «самоочищающихся» механизмов и немного наглой инженерной фантазии.

Архитектура с встроенным сроком годности

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

Один из подходов, который мне довелось применять, — встроенный срок годности кода. Представьте, что каждый метод или endpoint в API живёт ограниченное время: например, 6 месяцев. Если он не используется реальными пользователями или не продлевается вручную через конфигурацию — он автоматически вычищается системой.

Это можно реализовать очень просто. Например, в Python с использованием декоратора:

import datetime import functools  def expires_on(expiry_date):     def decorator(func):         @functools.wraps(func)         def wrapper(*args, **kwargs):             if datetime.date.today() > expiry_date:                 raise RuntimeError(f"Функция {func.__name__} устарела и удалена.")             return func(*args, **kwargs)         return wrapper     return decorator   @expires_on(datetime.date(2025, 12, 31)) def legacy_payment_method():     return "Старый платежный метод работает... пока"

Таким образом, сам код становится «одноразовым». Он либо обновляется, либо умирает. А если настроить CI/CD так, чтобы устаревшие функции автоматически выпадали из сборки, то система буквально сама вычищает собственные залежи.

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

Мусор как сервис: зачем системе уметь выносить сама себя

Один из самых неприятных видов мусора — это временные зависимости: библиотеки, которые «прикрутили на время», старые миграции базы данных, скрипты для «однократного запуска». Обычно всё это превращается в кладбище внутри репозитория.

В какой-то момент я начал относиться к мусору как к сервису. То есть создавать в системе отдельный слой, задача которого — выносить ненужное. Например, сервис, который раз в неделю проверяет, какие миграции старше N месяцев, и переносит их в архив. Или скрипт-уборщик, который автоматически удаляет неиспользуемые зависимости, сверяясь с lock-файлом.

На Java это можно реализовать как «демон-чистильщик»:

import java.nio.file.*; import java.time.*; import java.util.stream.*;  public class CleanupService {     private static final Path MIGRATIONS_DIR = Paths.get("db/migrations");      public static void main(String[] args) throws Exception {         try (Stream<Path> files = Files.list(MIGRATIONS_DIR)) {             files.filter(Files::isRegularFile)                  .filter(p -> isOld(p, 180))                  .forEach(CleanupService::archive);         }     }      private static boolean isOld(Path file, int days) {         try {             FileTime lastModified = Files.getLastModifiedTime(file);             return lastModified.toInstant()                     .isBefore(Instant.now().minus(Duration.ofDays(days)));         } catch (Exception e) {             return false;         }     }      private static void archive(Path file) {         System.out.println("Архивирую: " + file);         // Переместить в архивную папку     } }

Здесь идея не в самом коде (он игрушечный), а в принципе: система сама ухаживает за собой, так же как операционная система подчищает временные файлы.

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

Самоочищающиеся контракты

Самая сложная часть мусора — это человеческие обещания, которые превращаются в костыли. Например, «мы оставим этот endpoint на всякий случай» или «пусть эта функция поживёт, вдруг пригодится».

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

Как это работает:

  • Endpoint считается живым только если у него есть активные запросы за последние 30 дней.

  • Библиотека остаётся в зависимостях только если её функции реально вызываются в runtime.

  • Конфигурация сохраняется только если значение используется хотя бы одним модулем.

Пример на Go: сервис, который регулярно проверяет доступность API-методов и убирает мёртвые:

package main  import ( "fmt" "time" )  type Endpoint struct { Name      string LastUsage time.Time }  func (e Endpoint) IsAlive(thresholdDays int) bool { return time.Since(e.LastUsage).Hours() < float64(thresholdDays*24) }  func main() { endpoints := []Endpoint{ {"legacyLogin", time.Now().AddDate(0, -2, 0)}, {"newOAuthLogin", time.Now()}, }  for _, e := range endpoints { if e.IsAlive(30) { fmt.Printf("Endpoint %s жив\n", e.Name) } else { fmt.Printf("Endpoint %s мёртв — удаляем\n", e.Name) } } }

Технически это несложно. Сложно — решиться доверить системе принимать такие решения. Но зато эффект колоссальный: мусор просто не успевает накапливаться.

Подытожив

Код, который сам себя убирает, — это не магия и не утопия. Это всего лишь принцип: каждая сущность в системе должна иметь встроенный механизм старения и удаления. Не важно, это функция, endpoint, библиотека или конфигурация.

Если система может сама себя чистить, разработчики начинают думать о будущем иначе. Вместо того чтобы бояться «удалить лишнее», они доверяют архитектуре — и тратят силы не на уборку, а на развитие.

И, да, впервые, когда CI уронит билд из-за «просроченной функции», вы будете злиться. А потом поймёте, что лучше один упавший тест, чем год жизни в мусорном коде.


ссылка на оригинал статьи https://habr.com/ru/articles/940218/