Фраза сидит в вашей кодовой базе как бомба замедленного действия с перегоревшим светодиодным таймером. Никто не помнит, кто её установил, зачем она там и когда стала «принятым порядком вещей». Но она есть — устаревшие шаблоны, архитектурные решения и процессы, которые с течением времени и из-за инерции учреждения превратились в абсолютную истину. Самая опасная фраза в инженерии — это не синтаксическая ошибка или исключение нулевого указателя. Она гораздо тише и коварнее: «Мы всегда делали это так».
Я наблюдал, как эта фраза разрушала системы, срывала проекты и превращала блестящих инженеров в разочарованных археологов кода. Это предок технического долга, который никто не хочет признавать. И вот неудобная правда, которую никто не хочет озвучивать на стендапе: большинство из нас виновны в её распространении.
Почему эта фраза — тихий убийца
Позвольте мне описать сценарий, через который вы, вероятно, проходили. Вторник, утро. Вы просматриваете чей-то запрос на включение, и замечаете, что используется шаблон, который кажется, ну, неэффективным. Вы спрашиваете, почему, и ответ приходит быстрее, чем вы успеваете сказать «технический долг»: «Так мы всегда строили это».
На первый взгляд, это звучит обнадеживающе. Последовательность! Проверенные подходы! Но копните глубже, и вы обнаружите нечто гораздо более тревожное: никто на самом деле уже не знает, почему.
Опасность исходит не от самого шаблона — шаблоны являются нейтральными инструментами. Опасность исходит от нерассмотренных допущений. Когда мы перестаём подвергать сомнению унаследованные решения, мы перестаём заниматься проектированием и начинаем слепо копировать. Мы становимся программистами по поддержке собственной архитектуры, заменяя компоненты без понимания структурной целостности всей системы.
Скрытые издержки
Проблема проявляется тремя коварными способами:
Деградация производительности: тот шаблон запросов к базе данных, который имел смысл при 10 тысячах записей? Теперь он работает с 10 миллионами. Но мы продолжаем его использовать, потому что, ну, так мы делаем запросы. Я видел, как команды годами поддерживали неэффективные шаблоны доступа к базе данных просто потому, что первоначальное решение лежало в чьей-то голове и ушло вместе с ним.
Уязвимости безопасности: механизмы аутентификации, которые были разумными в 2015 году, становятся катастрофическими в 2026 году. Однако системы продолжают их использовать, потому что «мы всегда аутентифицировались таким образом». Я проводил аудит производственных систем, всё ещё использующих устаревшие криптографические библиотеки, поддерживаемые не из-за необходимости, а из-за институциональной амнезии.
Снижение скорости разработки: каждый новый инженер, который присоединяется к команде, изучает «как мы делаем вещи», что часто включает в себя недокументированные подводные камни, загадочные соглашения и шаблоны кода, которые существуют по причинам, которые никто не может сформулировать. Это не передача знаний — это посвящение в культ карго.
Анатомия стагнации
Позвольте мне показать вам, что происходит внутри организаций, которые позволяют этой фразе укорениться:
┌─────────────────────────────────────────────────────────────┐
│ Решение принято (2016) │
│ «Мы будем использовать Redis для хранения сессий» │
│ (В то время имело смысл, идеальные требования) │
└─────────────┬───────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Институционализация (2017–2019) │
│ Шаблон скопирован в 15 различных сервисов │
│ Никто больше не задаёт вопросов │
└─────────────┬───────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Потеря знаний (2020–2022) │
│ Первоначальный автор решения покидает компанию │
│ Логика уходит вместе с ним │
└─────────────┬───────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Накопление технического долга (2023–2026) │
│ Новые требования конфликтуют с шаблоном │
│ «Мы всегда делали это так» │
│ Инженеры тратят 6 недель на борьбу с системой │
└─────────────┬───────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Хрупкость системы │
│ Уловки строятся на уловках │
│ Архитектура становится карточным домиком │
└─────────────────────────────────────────────────────────────┘
Вот диаграмма механизма, показывающая, как это происходит:
Звуковое обоснование
Идеально для контекста"] --> B["Решение становится
Стандартом команды"] B --> C["Документация
Становится скудной"] C --> D["Первоначальное обоснование
Забывается"] D --> E["Предположения
Становятся невидимыми"] E --> F["Контекст меняется
Требования эволюционируют"] F --> G["Конфликт между
Шаблоном и потребностями"] G --> H{"Кто-нибудь
Задаёт вопросы?"} H -->|Нет| I["'Мы всегда
Делали это так'"] H -->|Да| J["Решение пересматривается
Современное решение"] I --> K["Технический долг
Нарастает"] J --> L["Система эволюционирует
Здорóво"] K --> M["Будущие проблемы"] L --> N["Устойчивый рост"]
Аргументы в пользу продуктивного скептицизма
Это не призыв к хаосу. Я не предлагаю вам выбрасывать каждый устоявшийся шаблон и перестраивать всё с нуля каждый квартал. Этот путь ведёт к худшему безумию — бесконечному циклу переписывания, который ничего не решает.
Вместо этого я выступаю за документированное принятие решений и плановый пересмотр. Революционные концепции, я знаю.
Вот практическая структура, которая, как я обнаружил, работает:
Шаг 1: Аудит существующих шаблонов
Прежде чем вы сможете что-либо подвергнуть сомнению, вам нужно знать, что вы на самом деле используете. Создайте инвентарь:
# patterns_audit.py
import ast
import os
from collections import defaultdict
class PatternDetector(ast.NodeVisitor):
def __init__(self):
self.patterns = defaultdict(list)
def visit_Call(self, node):
"""Обнаруживает шаблоны вызовов функций/методов"""
if isinstance(node.func, ast.Attribute):
pattern_name = ast.unparse(node.func)
self.patterns['function_calls'].append({
'pattern': pattern_name,
'line': node.lineno
})
self.generic_visit(node)
def audit_codebase(directory):
"""Сканирует кодовую базу на наличие установленных шаблонов"""
all_patterns = defaultdict(list)
for root, dirs, files in os.walk(directory):
# Пропускать каталоги поставщиков
dirs[:] = [d for d in dirs if d not in ['node_modules', 'venv', '.git']]
for file in files:
if not file.endswith('.py'):
continue
filepath = os.path.join(root, file)
try:
with open(filepath, 'r') as f:
tree = ast.parse(f.read())
detector = PatternDetector()
detector.visit(tree)
for pattern_type, instances in detector.patterns.items():
all_patterns[pattern_type].extend(instances)
except SyntaxError:
pass
return all_patterns
# Использование
patterns = audit_codebase('./src')
for pattern_type, instances in patterns.items():
print(f"\n{pattern_type}: {len(instances)} экземпляров")
Это даёт вам представление о том, какие шаблоны существуют. Знание — это необходимое условие для того, чтобы задавать вопросы.
Шаг 2: Документируйте «Почему»
Для каждого значительного шаблона создайте запись решения. Я не имею в виду написание романа — просто ответьте на три вопроса:
Почему: какую проблему это решило? Когда: каков был контекст (масштаб, ограничения, доступные опции)? До каких пор: при каких условиях это следует пересмотреть?
Вот шаблон:
# docs/architecture-decisions/session-storage.md
## Решение: использовать Redis для хранения сессий
**Дата принятия**: 2016-03-15
**Автор решения**: Сара Чен (руководитель отдела разработки, теперь в конкурирующей компании)
### Формулировка проблемы
Сессии хранились в процессе, что приводило к потере данных при развертывании
