Франкенштейн в программировании — это код, собранный из несовместимых частей разных технологий, стилей и архитектур. По данным исследования ThoughtWorks Technology Radar (2024), 28% крупных проектов содержат признаки синдрома Франкенштейна — архитектурной эклектики, возникающей при отсутствии единого технического видения. По аналогии с романом Мэри Шелли, такой код работает, но его поддержка превращается в кошмар.
Главное
Франкенштейн (Frankenstein code, Frankenstein pattern) — это антипаттерн, при котором программная система собирается из частей, не предназначенных для совместной работы. Как монстр Франкенштейна, такой код может функционировать, но он уродлив, непредсказуем и опасен при малейших изменениях.
Термин пришёл из литературы: в романе Мэри Шелли «Франкенштейн, или Современный Прометей» (1818) учёный создал живое существо из фрагментов тел разных умерших людей. В программировании аналогия точная — разработчики берут куски разных фреймворков, библиотек, языков и склеивают их «на живую нитку», получая работающий, но чудовищный результат.
Отличие Франкенштейна от макаронного кода в масштабе и природе проблемы. Спагетти-код — это запутанная структура внутри одного технологического стека. Франкенштейн — это эклектика на уровне архитектуры: разные технологии, несовместимые парадигмы, конфликтующие подходы внутри одной системы.
Микросервисная архитектура допускает использование разных технологий для разных сервисов, но при условии чётких границ и стандартизированных протоколов взаимодействия. Франкенштейн — это хаотичное смешение без границ: REST и GraphQL в одном контроллере, две ORM в одном модуле, SQL и NoSQL для одной сущности.
Отсутствие технического лидера или архитектора — первопричина. Когда в проекте нет человека, отвечающего за целостность архитектуры, каждый разработчик выбирает инструменты «под себя». Один любит Spring, второй — Guice, третий — самописный DI. Результат — архитектурный винегрет.
Слияние проектов — вторая распространённая причина. Две команды разрабатывали свои модули независимо, используя разные стеки. Когда модули нужно объединить в одно приложение, их просто «склеивают» адаптерами и прослойками. Получается Франкенштейн.
Корпоративные поглощения — третий сценарий. Компания А купила компанию Б и хочет интегрировать её продукт в свой. Вместо переписывания — склейка через API, общие базы данных и костыли. Через год система превращается в монстра, которого никто не понимает.
| Причина | Описание | Типичный результат |
|---|---|---|
| Нет архитектора | Каждый разработчик выбирает свой стек | 3 разных HTTP-клиента в одном модуле |
| Слияние проектов | Два продукта склеиваются в один | Две ORM, два способа логирования |
| M&A | Поглощение компании с её продуктом | Гибрид из разных архитектур и стилей |
| Эксперименты | Внедрение новых технологий без стратегии | Java 8 + Java 21 фичи в одном файле |
| Политические решения | Навязывание технологии сверху без учёта контекста | Enterprise-фреймворк для простого скрипта |
Опытные разработчики, которые хотят попробовать новые технологии в продакшне, часто становятся источником Франкенштейна. Вместо того чтобы ограничить эксперименты изолированным модулем, они внедряют экспериментальный код в критическую часть системы.
Классический пример — использование нескольких ORM в одном приложении. Часть модулей использует Hibernate, часть — MyBatis, а часть — прямые JDBC-запросы. Транзакции становятся неуправляемыми, кэш — несогласованным, а новый разработчик не знает, какой подход выбрать для новой фичи.
Второй пример — смешение архитектурных стилей. В контроллере REST API встречаются вызовы SOAP-сервисов, прямые SQL-запросы, обращение к файловой системе и генерация HTML. Такое приложение невозможно тестировать, расширять или документировать.
Третий пример — технологический стек, где Python используется для бэкенда, Node.js — для микросервиса, C# — для десктопного клиента, а Java — для Android-приложения, при этом вся бизнес-логика размазана между ними без чёткого разделения ответственности.
// frankenstein — mixed styles and technologies
// callbacks, Promises, and async/await combined
// callbacks
db.query("SELECT * FROM users", function(err, rows) {
if (err) handleError(err);
// Promise inside callback
fetch("/api/data").then(function(data) {
// async/await inside then
(async () => {
const result = await processData(data);
sendResponse(result);
})();
});
});
// clean code — unified async/await style
async function getUserData(userId) {
const user = await db.query("SELECT * FROM users WHERE id = ?", [userId]);
const data = await fetch("/api/data/" + userId);
return await processData(user, data);
}
Одна база данных используется одновременно как SQL-реляционная (с нормализацией) и как NoSQL-документо-ориентированная (с JSON-колонками). Часть запросов идёт через ORM, часть — через хранимые процедуры, часть — через прямой SQL из кода. Схема БД не документирована, миграции конфликтуют.
Сложность онбординга — первое последствие. Новый разработчик должен знать 5 языков, 3 фреймворка, 2 архитектурных стиля, чтобы понимать, как работает система. Онбординг затягивается с недель до месяцев. По данным LinkedIn (2023), проекты с технологической эклектикой теряют новых сотрудников в 2 раза чаще.
Непредсказуемость поведения — второе последствие. Изменение в Python-микросервисе может неожиданно сломать Java-модуль, потому что они используют общую БД без чётких контрактов. Отладка таких проблем требует одновременного знания всех технологий в стеке.
Безопасность — третье последствие. Каждая технология в стеке требует своей конфигурации безопасности, своих патчей, своего мониторинга. Поддерживать безопасность на приемлемом уровне для 5–6 разнородных технологий практически невозможно. Одна из них неизбежно окажется уязвимой.
SonarQube может измерить технический долг, но не может измерить «архитектурный долг» — несовместимость компонентов. Этот долг проявляется не в предупреждениях линтера, а в невозможности добавить новую функцию без изменения трёх разных модулей, написанных на разных технологиях.
Первый и главный шаг — назначить архитектора или tech lead, ответственного за целостность технологического стека. Этот человек имеет вето на внедрение новых технологий без архитектурной рецензии. Не демократия, а ответственное единоличное решение по ключевым технологиям.
Второй — внедрить процесс Architecture Decision Record (ADR). Любое значимое архитектурное решение (выбор БД, фреймворка, протокола) документируется в виде короткого текста: контекст, рассмотренные альтернативы, принятое решение, последствия. ADR хранятся в репозитории и доступны всей команде.
Третий — установить принцип «одна задача — один инструмент». Для HTTP-запросов — один клиент. Для ORM — одна библиотека. Для логирования — один фреймворк. Исключения допускаются только через ADR с обоснованием. Если в проекте уже есть Axios — не добавляйте fetch, если есть SLF4J — не пишите через System.out.
Эксперименты допустимы, но в изолированной среде. Выделите модуль или сервис, который можно переписать на новой технологии без влияния на остальную систему. Если эксперимент удался — стандартизируйте его через ADR. Если нет — удалите без последствий.
Инвентаризация — первый шаг. Составьте полную карту технологического стека: какие фреймворки, библиотеки, языки, протоколы используются, в каких модулях и для каких задач. Вы увидите масштаб проблемы: дублирование инструментов, конфликтующие технологии, неиспользуемые зависимости.
Стандартизация — второй шаг. Выберите один инструмент для каждой задачи. Например: только Hibernate для ORM, только SLF4J + Logback для логирования, только REST для API. Задокументируйте стандарт в ADR. Начните замену с модулей, где эклектика приносит больше всего проблем.
Стратегия Parallel Run — третий шаг. Старый и новый инструменты работают параллельно, пока новый не докажет свою надёжность. Например, старый HTTP-клиент и новый работают одновременно, но новый — только для части запросов. После периода стабилизации старый удаляется.
// frankenstein — three HTTP approaches in one project
// Module A: OkHttp
OkHttpClient client = new OkHttpClient().newCall(request);
// Module B: RestTemplate (Spring)
restTemplate.getForObject(url, String.class);
// Module C: java.net.HttpURLConnection
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
// unified approach: RestTemplate for sync, WebClient for reactive
@Autowired
private RestTemplate restTemplate;
public String callApi(String url) {
return restTemplate.getForObject(url, String.class);
}
Технический лидер — главный инструмент борьбы с Франкенштейном. Не менеджер, не архитектор в башне из слоновой кости, а практикующий разработчик, который пишет код, ревьюит PR и принимает архитектурные решения. Без такого человека проект неизбежно скатывается в технологическую эклектику.
RFC (Request for Comments) — процесс, заимствованный из Open Source-сообществ. Перед внедрением любой значимой технологии автор пишет RFC: проблема, предлагаемое решение, альтернативы, план внедрения. Команда обсуждает, голосует, принимает или отклоняет. RFC создаёт прозрачность и предотвращает «тихие» архитектурные решения.
Технологический радар (Technology Radar от ThoughtWorks) — инструмент категоризации технологий: Adopt, Trial, Assess, Hold. Команда регулярно пересматривает радар и обновляет статусы. Это помогает отличать «модное» от «полезного» и избегать внедрения непроверенных технологий в критический код.
Самое важное качество архитектуры — согласованность (consistency). Даже не самый лучший инструмент, используемый во всём проекте, лучше, чем лучший инструмент, используемый только в одном модуле. Согласованность снижает когнитивную нагрузку, упрощает онбординг и делает код предсказуемым.
Часто задаваемые вопросы
Polyglot persistence — осознанное использование разных БД для разных задач (PostgreSQL для транзакций, Redis для кэша, Elasticsearch для поиска). Франкенштейн — хаотичное смешение без стратегии. Разница в наличии архитектурного решения: polyglot — это план, Франкенштейн — его отсутствие.
Да, и это частая проблема. Когда каждый микросервис использует свой язык, свою БД, свой протокол и свой подход к развёртыванию без централизованных стандартов — получается распределённый Франкенштейн. Для микросервисов важны общие стандарты: единый протокол (REST/gRPC), общий формат логов, централизованное observability.
Не запрещайте — направляйте. Предложите автору RFC: опишите, почему существующее решение не подходит, какие альтернативы рассмотрели, как будете мигрировать. Часто в процессе написания RFC разработчик сам понимает, что новая технология не нужна. Если RFC убедителен — внедряйте, но с планом и ограничениями.
Сначала инвентаризация, потом стандартизация. Не пытайтесь переписать всё сразу. Выделите один слой (например, HTTP-клиенты или логирование), выберите единый инструмент, напишите ADR и мигрируйте постепенно. Метод Strangler Fig — заменяйте старые компоненты новыми по одному, без остановки работы приложения.
Чем меньше, тем лучше. Идеально — один язык, один фреймворк, одна БД, один способ логирования. Реалистично — 2–3 языка (при чётком разделении), 1–2 БД, 1–2 фреймворка. Каждая дополнительная технология увеличивает когнитивную нагрузку команды и стоимость поддержки.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также