Мы привыкли бороться с «мусором» в коде — временными костылями, устаревшими методами, забытыми конфигами. Но что если можно проектировать систему так, чтобы она сама чистилась от ненужного, минимизируя технический долг прямо в процессе работы? В статье попробую показать, что это не миф, а вполне реальная практика, основанная на архитектурных паттернах, «самоочищающихся» механизмов и немного наглой инженерной фантазии.
Архитектура с встроенным сроком годности
Большая часть кода умирает не потому, что он плох, а потому что его контекст устаревает. Протокол меняется, бизнес-логика переписывается, 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 уронит билд из-за «просроченной функции», вы будете злиться. А потом поймёте, что лучше один упавший тест, чем год жизни в мусорном коде.
Комментарии (5)
frostsumonner
24.08.2025 18:52Есть ендпоинт, который выдает админские права пользователю. Если админ не менялся год, то ендпоинт может год не использоваться.
slonopotamus
24.08.2025 18:52В статье попробую показать, что это не миф, а вполне реальная практика, основанная на архитектурных паттернах, «самоочищающихся» механизмов и немного наглой инженерной фантазии.
Фантазию показали. Реальную практику не показали.
subzey
24.08.2025 18:52Главное, чтобы не получилось, что на CI всё хорошо, а через час в проде падения.
buratino
чет мне подсказывает, что это хорошо спроектированная индейская национальная изба