Антипаттерны (по автору)

Сборник контрарианских позиций автора а о том, что в JS-сообществе считается «хорошей практикой», но он считает антипаттерном.

1. Middleware (Express/Koa-стиль)

См. Middleware Pattern.

Кратко: цепочка обработчиков, мутирующих req/res. Reference pollution, неявное зацепление, race conditions. Решение: Chain of Responsibility + Context Pattern с приватными полями.

2. ORM

«ORM не нужен» — повторяющийся тезис автора.

  • ORM скрывает SQL → разработчик не понимает производительность
  • Сложные join'ы и денормализованные view плохо ложатся на ORM
  • Query Builder — золотая середина

3. EventEmitter как замена promise/callback

«Event-emitter подобен go-to»

  • Эмит из одного модуля передаёт управление в неизвестное место
  • Зацепление через события неявное, хуже явного
  • Тестировать невозможно изолированно — две абстракции вместе
  • Решение: использовать await и явные интерфейсы

4. Singleton

«На JavaScript это почти бессмысленная вещь»

  • Часто относят к антипаттернам — shared state
  • Усложняет тестирование, скрывает зависимости
  • В JS заменяется ES-модулями (export default new X)
  • Решение: dependency injection вместо синглтона

5. Active Record

«Нарушает SOLID и GRASP, separation of concerns»

  • Один класс отвечает за данные и persistence
  • Перемешивает domain logic и CRUD
  • Решение: DAO или Repository

6. Внешняя MQ для маленькой задачи

«Хватило бы структуры данных в коде»

  • Простую очередь между модулями решают подключением Kafka/RabbitMQ
  • Лишняя машина, настройка, deploy, мониторинг
  • Задача не выходила за рамки одного инстанса приложения
  • Решение: in-process queue, паттерны GoF в коде

7. Зависимость от порядка подписчиков на события

«Если нам нужно знать про последовательность подписчиков, у нас уже проблема с архитектурой»

  • Завязка на порядок addEventListener — архитектурный дефект
  • Эмиттер не знает числа подписчиков (0 или много)
  • Решение: явный pipeline вместо событий

8. Декоратор как синтаксис языка вместо паттерна

«Декоратор-синтаксис ≠ GoF-Decorator»

  • TS-декораторы постоянно ломали обратную совместимость
  • @private в TS ≠ настоящий #field (наследуется как public)
  • TS приходит к JS-стандарту, но через эмуляции и хаки
  • Решение: для метаданных — Reflect.metadata API; для GoF Decorator — обычный класс-обёртка

9. Магические числа в коде стандартной библиотеки

Пример: FixedQueue.BASE_SIZE = 2048 в Node.js — почему именно 2048? Нельзя настроить через options. Лучше — параметризация.

10. Циркулярность там, где не нужна

«Очередь: с одного конца пишем, с другого читаем. Циркулярность избыточна»

  • Node.js FixedQueue делает циркулярный буфер, хотя это очередь
  • Усложняет логику без выгоды
  • Решение: Unrolled list с тремя указателями (Head/Tail/Current)

11. Использовать паттерн ради паттерна

«Если решение не переиспользуется — это не паттерн»

  • Делать class TimerFlyweight { ... } для одного использования — не Flyweight
  • Это просто код, оверинжиниринг
  • Решение: применять паттерн, когда видишь повторяющуюся проблему

12. Завязка на фреймворк вместо моделирования

«Между языком и фреймворком должно быть моделирование»

  • Прыгают с языка прямо на фреймворк, пропуская проектирование
  • Фреймворк прячет 1-2 паттерна, переименовав их, и считают это архитектурой
  • Решение: тренировка моделирования через паттерны до фреймворков

Главные тезисы автора

  • Большинство «лучших практик» в JS — упрощения для junior'ов, не оправданные для middle/senior.
  • Сложность не исчезает, а перераспределяется. Прячете куда-то — всплывёт.
  • Паттерны GoF, SOLID, GRASP — это знания, которые AI не заменит (он их знает, но не понимает применимость).

🎓 Источники

См. также