Как стать автором
Обновить

Воображаемые проблемы — корень плохого ПО

Время на прочтение 7 мин
Количество просмотров 14K
Всего голосов 41: ↑39 и ↓2 +37
Комментарии 18

Комментарии 18

Вы попросили вполне конкретный, узкоспециализированный продукт с парой нужных вам дополнительных фич. Команда разработки услышала это иначе.

Потому что командна разработчиков научена тому, что если реально сделать узкоспециализированный продукт без возможностей расширения — то все, дальше дело труба?


Вот проиндексировать весь Интернет, а после этого выдавать релевантные поисковые результаты за долю секунды — вот это сложно. Однако пара ребят в гараже это сделала.

Это там, где сначала был поиск слов? Нет, это не сложно. Современные алгоритмы google — вот что сложно, но пара парней в гараже их не писала.


Самая большая проблема в том, что очень сложно найти баланс между расширением и сложность архитектуры и производительностью. Ну и всякие процесные проблемы, но они не то, что бы сложные, просто они есть.

сложно найти баланс между расширением и сложность архитектуры и производительностью

Нет — это не так сложно. Есть простые принципы KISS, DRY и YAGNI
Чем проще написан код, тем проще его расширять и поддерживать. Многие программисты страдают от «универсальности». К примеру пришла задача обработать данные по протоколу (К примеру команды по MODBUS или CAN) Некоторые разработчики начинают задавать себе вопрос, а что если завтра нужно будет добавить ещё один протокол или изменить получателя… В результате вместо простой связки интерфейса и класса рождается монстр, близкий по функционалу к BizTalk с возможностью изменения протоколов в конфигурации. Только часто бывает, что день, когда вас попросят добавить 2ой протокол так и не наступит, а вам придётся поддерживать и исправлять ошибки в десятках тысяч строк ненужного кода (а ведь могло быть всего пара сотен строк...) Если бы у меня была такая задача, то я бы сделал интерфейс (для возможной расширяемости в будущем) и его реализацию в виде класса, который делает только то что нужно для данного протокола. Закинул бы его через DI или ServiceLocator и всё…

Когда вам сказали, что абстрактный принцип, сформулированный в качестве красивой аббревиатуры может быть простым — вас обманули. Иначе можно было бы просто следовать принципу ППП (Просто пиши правильно) и не парится.


Что бы не быть голосновным:


KISS — довольно абстрактный принцип, который можно еще записать "не усложняйте сверх меры", но опять же, проблема в том, что бы найти эту меру, а это не тривиальная задача.
DRY — принцип, который можно рассматривать очень по разному. Некоторые программисты считают строчки кода, которые повторяются и если их больше трех, сразу начинают обобщать. Некоторые более опытные описывают DRY в связке с SRP, и говорят, что если для одного изменения для одного актора в программе нужно менять код в двух местах — вот тут проблема в DRY может быть.
YAGNI — Не очень популярный принцип опять же, потому что никогда не понятно, когда на самом деле нужно, а когда нет. Я не знаю бывало ли у вас такое, но я бывал в ситуациях, когда абстракции или какого-то усложнения сверху не было, а изменения понадобились. И все было очень плохо.


Касательно вашего примера, как я понимаю, вместо разделения на абстрактный класс и реализацию просто сделали реализацию в одном классе? Грустно, но это базовые ошибки проектирования, мне кажется, вот тут точно проблем не должно быть.

По поводу абстрактного класса — всё зависит от того — внешний это интерфейс или внутренний. Если интерфейс реализован только для расширяемости / юнит тестирования, либо если это API и наружу виден только интерфейс в виде json-объекта, то вынесение абстрактного класса на данном этапе лишнее, т.к. не факт что 2ой потомок вообще появится. (получается что это будет лишнее усложение. Когда появится — тогда и вынесем). Если же реализована система плагинов или планируется возможность переиспользования и расширения модуля, то тогда абстрактный класс — must have…
По поводу красивых аббривиатур:
Для себя выработал следующие правила
KISS — Поставленная задача должна решаться самым простым из возможных способов. Код должен быть легко-читаемым, легко-модифицируемым и расширяемым, но должно быть реализовано только то, что нужно на данный момент.

DRY — Изменения должны происходить в минимальном количестве мест (не всегда получается сделать что только в одном). При внесении изменений не должен меняться одинаковый код (например поиском и заменой по коду).

YAGNI — Не пиши лишний код и удаляй ненужный (если он есть в системе контроля версий конечно).

По поводу необходимости изменения кода там, где нет абстракций — значит пришло время их добавить… (Что соответствует данным принципам). Если код написан по KISS и DRY — добавление абстракции произойдёт в одном месте и затронет минимальное количество кода.
KISS — Поставленная задача должна решаться самым простым из возможных способов. Код должен быть легко-читаемым, легко-модифицируемым и расширяемым, но должно быть реализовано только то, что нужно на данный момент.

У вас всего лишь три параметра, которые довольно часто обратнозависимы, а так, разумеется, все просто.


DRY — Изменения должны происходить в минимальном количестве мест (не всегда получается сделать что только в одном). При внесении изменений не должен меняться одинаковый код (например поиском и заменой по коду).

Как я уже писал выше "поиск по коду" — это не совсем правильный подход. В таком подходе вы упускаете моменты, когда код выглядит одинаково, но вызывается из разных мест и так и должно быть.


YAGNI — Не пиши лишний код и удаляй ненужный (если он есть в системе контроля версий конечно).

Класс. А какой код нужный и какой код ненужный?


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

Вы когда-то такое делали? Ну, не в домашнем своем проекте, а в реальном, где куча людей, параллельно разрабатывают какие-то фичи, а еще нифига нет тестов и прочее?

У вас всего лишь три параметра, которые довольно часто обратнозависимы
Чаще всего там нет обратной зависимости. Чем меньше кода написано, тем проще его понимать, расширять и модифицировать. (Это конечно не значит, что всё нужно писать в Code-Behind onclick)

код выглядит одинаково, но вызывается из разных мест
— Это как раз признак того, что код написан по DRY — вызовов много, а вызываемый код — общий. Конечно, если нужно поменять сами вызовы, то без поиска и замены не обойтись, но предполагается что весь код написан по DRY — т.е. количество вызовов максимально сокращено…

А какой код нужный и какой код ненужный?
Ненужный в данном случае — неиспользуемый/мёртвый или закомментированный.

Вы когда-то такое делали? Ну, не в домашнем своем проекте, а в реальном, где куча людей, параллельно разрабатывают какие-то фичи, а еще нифига нет тестов и прочее?

Конечно делал! Но соблюдение этих принципов не значит «забивание» на архитектуру приложения. Если код вызывается в куче мест, то он должен быть написан так, чтобы его можно было модифицировать (но если этого не предусмотрели изначально — значит нужно добавить) — всё-же пройтись по десяткам методов и заменить класс на интерфейс сделать проще, чем разбираться с кучей ненужных абстракций, зависимостей и конфигураций

Иначе можно было бы просто следовать принципу ППП (Просто пиши правильно) и не парится.

На самом деле, все очень просто — принципы фиксируют стандартное поведение. Стандартное поведение — это такое, которому стоит следовать, если не получается четко и ясно сформулировать причины, по которым ему следовать не надо.


KISS — не надо делать систему сложнее, если вы не можете объяснить, почему она должна быть сложнее


DRY — стоит избегать дублирования во всех случаях, в которых вы не можете объяснить, почему дублирование необходимо


YAGNI — не стоит добавлять функциональность, если вы не можете аргументировано пояснить, почему ее следует добавить прямо сейчас

А для отката транзакции нужно 1000 человек и полгода.

Вот это очень плохой пример.
Откат транзакции в банке N если что технически не сложен. Но вот пока все банки участвующие в цепочки договорятся о том как будет проходить откат итд, может пройти лет 5.
Основная проблема в законах.

Красное сторно не такая и простая штука. Причем требующая так же особые документы.
Реверс идет уже за вычетом комиссии, итд. + не все банки партнёры работающие с банком N согласятся его делать просто так.

В общем ваш пример полный шлак. Не знаете как оно работает, лучше не пишите. Мне же довелось в написании примерно такого же участвовать.
Нельзя еще забывать об огроменной зарегулированности всех околофинасовых компаний, особенно имеющих дело с чужими деньгами — брокеров, банков и тд.
Да, поддерживаю. При работе с деньгами такие вроде-бы простые на первый взгляд вещи как откат транзакций (да и транзакции вообще), закрытие операционного дня и т.д. превращаются в очень сложные процессы (особенно, когда это происходит для каждого отделения в отдельности). Там очень много тонкостей начиная от округления при расчёте итогов и до конвертации валют. Простой пример: при расчёте пени можно сначала округлить до копеек, а потом посчитать итог, а можно сначала посчитать итог, а потом его уже округлить — а в результате разница в отчёте может оказаться в несколько сотен тысяч рублей…
А вот про единую банковскую платформу — это конечно интересно, но скорее всего невозможно из-за разных процессов в каждом банке (которые они чтят и берегут как традиции), да и сейчас преимущество в софте — это почти единственная разница между банками. Так что единую систему мы ещё очень не скоро увидим…
Я так понимаю это специально в пятницу опубликовали? Пятнично.
Мне вот по опыту чаще приходилось встречаться с софтом, который писался как «простой инструмент для решения одной маленькой частной задачи». А когда задача расширилась — превратился в зверя с обложки книги про perl 6.
А когда придумали слово Agile, написание софта таким способом стало модным.
После перехода всех на Agile общее качество продуктов ухудишлось (зато стала быстрее скорость доставки — что нужно бизнесу). Всё дело в том, что в стандартной водопадной модели проектирования очень много внимания уделяется проектированию, архитектуре и пишутся технические задания и т.д. Т.е. система рассматривается и разрабатывается от целого к частному. В Agile же система — это набор фич. И в результате получается что лепится ядро, а потом к нему начинают прилеплять фичи. А так как о большинстве фич изначально даже и не думали, то появляются монстры которые трудно поддерживать…
Я конечно не агитирую за возврат к водопадной модели, но даже с аджайлом можно уделять больше внимания проектированию и архитектуре.
«простой инструмент для решения одной маленькой частной задачи» — ну это unix-way )
Вспоминается пример из промышленной автоматизации — протокол OPC DA. Не знаю как сейчас обстоят дела, но лет 10 назад каждая контора билась над созданием собственного сервера OPC для своего оборудования. Серверы эти стоили не мало и создавались большим трудом. В то же время, протокол по сути решает простейшую задачу доступа к массиву чисел с указанием признаков качества значения и времени его обновления.
Не цеплясь к отельным примерам, статья очень правильная!
У меня был случай, когда менеджмент на столько запутался, что в один прекрасный момент (за пару месяцев до краха стартапа) выкатил задачу быть лучше конкурентов по всем подряд аспектам… Пару раз я даже попытался достучаться до инвесторов, а потом расслабился и получал удовольствие.

Надо простую задачу пдавать как вызов для разработчиков: ааот сделайте это просто и надежно...


Что реально сложно (или невозможно) — предусмотреть все возможные точки расширения функционала.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий