Мы привыкли бороться с «мусором» в коде — временными костылями, устаревшими методами, забытыми конфигами. Но что если можно проектировать систему так, чтобы она сама чистилась от ненужного, минимизируя технический долг прямо в процессе работы? В статье попробую показать, что это не миф, а вполне реальная практика, основанная на архитектурных паттернах, «самоочищающихся» механизмов и немного наглой инженерной фантазии.
Архитектура с встроенным сроком годности
Большая часть кода умирает не потому, что он плох, а потому что его контекст устаревает. Протокол меняется, бизнес-логика переписывается, 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 уронит билд из-за «просроченной функции», вы будете злиться. А потом поймёте, что лучше один упавший тест, чем год жизни в мусорном коде.
Комментарии (18)
frostsumonner
24.08.2025 18:52Есть ендпоинт, который выдает админские права пользователю. Если админ не менялся год, то ендпоинт может год не использоваться.
pda0
24.08.2025 18:52Ну, не обязательно буквально удалять. Можно начать кидать варнинги, а там пусть разбираются - нужно оно ещё и надо дату передвинуть или ну его на фиг.
slonopotamus
24.08.2025 18:52В статье попробую показать, что это не миф, а вполне реальная практика, основанная на архитектурных паттернах, «самоочищающихся» механизмов и немного наглой инженерной фантазии.
Фантазию показали. Реальную практику не показали.
subzey
24.08.2025 18:52Главное, чтобы не получилось, что на CI всё хорошо, а через час в проде падения.
vityo
24.08.2025 18:52Выглядит как нечто такое, что можно начинать делать после того как есть реальные пользователи системы и их трекинг конкретный. Ну программисты не пользователи, а тесты покрывшие все тоже старые могут быть.
Но там и чистка поумнее должна быть, после результатов трекинга. Она должна часть выкинуть, а часть переписать-скомбинировать с ещё нужным. Так как юзеры скорее в целом покажут, что есть старое на целую область, а не на какую-то функцию. Либо наоборот это не мусор и надо подымать в топ, если это фича. Типа старый чайник нашли, он хороший, просто забыли, начинаем пользоваться, а не выкидываем. Или это новое, просто ещё никто не в курсах, вот и не пользуется. Короче чистка настоящего мусора это куда-то в анализ результатов трекинга реальных пользователей системы.
Goodzonchik
24.08.2025 18:52Мне кажется, что идея интересная, но на практике применить ее будет сложнее. Так как разные технологические стеки требуют разных скриптов для выпиливания. Более практический смысл можно было бы описать именно реализацию, как изолировать сервис/функцию так, чтобы можно было ее руками удалить за пару минут и не сломать код. Как вести мониторинг того, что API не используется и т.д.
Ну и любая автоматизация кода требует огромного количества тестов разного уровня, которые тоже надо как-то выпиливать, но так, чтобы они проверили работоспособность.
CTDNikiru
24.08.2025 18:52Удалять код - радикально, но вот отмечать что он старый и выводить это в логах можно. Правда не понятно как именно реализовать. Пока что из идей это просматривать stackTrace или логи вызовов
cmyser
24.08.2025 18:52В $mol такая система давно используется, в бандл попадает только используемый код
Aarrtteemm
24.08.2025 18:52Уже вижу как expires_on бесконечно продлевают каждые 90 дней лишь бы не трогать
buratino
чет мне подсказывает, что это хорошо спроектированная индейская национальная изба