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

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

Большая часть кода умирает не потому, что он плох, а потому что его контекст устаревает. Протокол меняется, бизнес-логика переписывается, 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)


  1. buratino
    24.08.2025 18:52

    чет мне подсказывает, что это хорошо спроектированная индейская национальная изба


  1. frostsumonner
    24.08.2025 18:52

    Есть ендпоинт, который выдает админские права пользователю. Если админ не менялся год, то ендпоинт может год не использоваться.


  1. slonopotamus
    24.08.2025 18:52

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

    Фантазию показали. Реальную практику не показали.


  1. Smokost
    24.08.2025 18:52

    # TODO: delete me after 01.01.2026


  1. subzey
    24.08.2025 18:52

    Главное, чтобы не получилось, что на CI всё хорошо, а через час в проде падения.