Comments 26
Любая абстракция – это инструмент, применение которого имеет свою цену, выраженную в увеличении когнитивной нагрузки.
Проще говоря, любой паттерн проектирования – как молоток – нет смысла размахивать ним в воздухе, если у вас нет гвоздей.
Если вы не можете ясно сформулировать, какую именно проблему решаете прямо сейчас добавлением новой абстракции – выкиньте из головы эту идею и просто пишите код.
И да, завуалированные ответы, просто повторяющие определение паттерна проектирования («Я использую декоратор, чтобы динамически добавлять объекту новую функциональность, оборачивая его в обёртку») не считаются. Нужна конкретная ситуация и предложенное решение.
Иначе можно быстро скатиться в энтерпрайз-ориентированную разработку, с 2 проектами, полиморфной фабрикой, декоратором и инверсией контроля для проверки является ли строка палиндромом.
По идее ровно наоборот, пока нет цели разобраться во всей структуре.
вероятность ошибки в строке кода крайне мала, пусть 0.01%. Перемножив вероятности ошибки на большом приложении с 10 000 строк кода, получаем 100%, что там есть ошибка
В этом случае вероятность ошибки будет не 100%, а примерно 63%
Вероятность отсутствия ошибок во всех строках кода: 99.99%^10000=36.8%.
Вероятность наличия хотя бы одной ошибки во всех строках кода: 100%-36.8%=63.2%.
Подозреваю опечатку в своём тексте, думаю правильнее говорить о «сложении» вероятностей.
Вполне могу признать, что мои познания в теории вероятностей далеко не идеальны, но вот сам подсчет мне видится приблизительно верным, ведь подсчитывается вероятность появления хотя бы 1 ошибки, эти события вполне себе совместные, а значит формула:
P(A+B)=P(A)+P(B)−P(A⋅B).
Вероятность наличия ошибки в одной строке кода: 0.01%.
Вероятность наличия ошибки во всех строках кода: 0.01%^10000 = очень мало.
Вероятность наличия хотя бы одной ошибки в любой строке кода: 0.01%*10000 — 0.01%^10000 ~= 100%.
Формула, которую вы привели, для трех событий будет выглядеть следующим образом:
P(A+B+С) = P(A) + P(B) + P(C) − P(A⋅B) − P(B⋅C) − P(C⋅A) + P(A⋅B⋅C)
Для 10000 строк эта формула будет иметь 2^10000-1 слагаемых. Вряд ли вам захочется считать вероятность таким образом.
У нас есть два взаимоисключающих события, одно из которых гарантированно наступает:
- Ни в одной строке кода нет ошибки.
- Хотя бы в одной строке кода есть ошибка.
Посчитайте вероятность первого события, а вероятность второго будет простым его отрицанием.
Вероятность наличия ошибки в одной строке кода: 0.01%.
Вероятность наличия ошибки во всех строках кода: 0.01%^10000 = очень мало.
Вероятность наличия хотя бы одной ошибки в любой строке кода: 0.01%*10000 — 0.01%^10000 ~= 100%.
При переходе от первых двух строк к третьей вы предположили, что ошибка может быть либо в одной строке, либо сразу во всех. Но на самом деле, ошибка может быть в двух строках кода, в трёх, в четырёх и т.п. Все эти случае также потребуется вычесть.
Просто написать сложно.
Интересная тема, спасибо. В общем чуть более чем полностью согласен. Моё скромное мнение заключается в том, что сложность бывает двух типов: естественная сложность автоматизируемого бизнес процесса и искусственная сложность привнесенных абстракций, фоеймверков и прочего, прочего, прочего. Задача проектирования — реализовать естественную сложность в некотором управляемом виде при этом, по возможности, не привнести искусственную сложность. Эту идею можно выразить в простом ритуале — каждый раз добавляя в дизайн приложения новый элимент, нужно спросить себя позволяет ли этот элемент упростить работу с бизнес логикой и если да, то можно ли добиться этого же результата проще? Конечно бывают крайние случаи, когда нам нужно выживать под нагрузкой например. Но тем не менее.
И не много критики.
Про Дашу и бизнес логику. Тут есть обратная сторона в картинке. 80% стека вызовов это фреймверк, то есть общий код, который ты по идее не должен трогать совсем. А вот бизнес логика это 20% которые должны быть довольно простые. Если отказаться от этих 80%, то придётся добавить к бизнес логике ручную работу с транзакциями, безопасность, работу с бд. Не факт, что получится проще.
Ну и ещё один момент. Простые решения требуют непрерывного рефакторинга. В случае, если мы написали код в методе контроллера и наше приложение осталось простым, то мы выиграли, сэкономили кучу времени. Но если приложение начало расти? Где та грань, когда нужно начинать переходить от простых решений к сложными? Когда контроллера два? Когда их 10? Проблема в том, что обычно тебя просят дописать один метод, потом второй. И каждый раз ты думаешь, что это всего то ещё одна фича, ничего страшного. А потом приходится в какой-то момент рефакторить монстра из лапши на тысячи строк кода. Собственно по этому часто и делают оверинжиниринг на будущее, потому что боятся, что потом не будет времени на рефакторинг и надо брать это время пока дают. Не сказал бы, что это хорошо. Лучше когда на каждую фичу закладывается время на рефакторинг, тогда можно идти от простых решений к сложными по необходимости и без проектирования на будущее.
Где та грань, когда нужно начинать переходить от простых решений к сложными? Когда контроллера два? Когда их 10?Выносить логику из контроллера нужно не при достижении определенного количества контроллеров, а как только вы увидите, что разные контроллеры начинают отвечать за одну и ту же функцию (например, выполнение поиска, либо авторизация пользователя).
Это называется принцип единственной ответственности. Он достаточно универсален, и одинаково хорошо применяется как для маленьких проектов, так и для больших.
Поручают молодому «гению» что-то бесконтрольно сочинять. Кто виноват? Тот кто поручил.
Требуют по быстрому в бой. Кто виноват? Кто требует по быстрому.
Веруют в зазнавшегося архитектора. Кто виноват? Верующий.
В общем случае — понятия не имеют о способах организации разработки. Кто виноват? Ответ очевиден.
Программист всегда крайний, поэтому на него и валят. А на самом-то деле…
Дв даже если его разбросать по ту мач абстракциям, вреда будет в разы меньше… как минимум можно будет протестировать абстракции.
Мы как в том анекдоте, едим кактус и плачем. Ооочень медленно вводим абстракции вида
1. Validation layer (валидаци? какая валидация? =))
2. Thin Controller (сейчас это просто куча кода с колбек хелами)
3. Service Layer (отсутствует как класс. вся логика в контроллере)
4. DAO/Repository (сейчас монгуси прямо в контроллерах)
5. Mappers (работаем с внешними апишками)
6. Resources (http clients)
Да, файлов много. Но уже не так потеешь когда открываешь контроллер. И этих файлов будет еще больше. Но альтернатива в виде огромных функций это ад…
Ну и нужно понимать, абстракции потекут если их не поддерживать… и превратятся в гомно, кучу кода с ифами… я думаю все мы это проходили =))
з.ы.
Естественно мы не пытаемся впихнуть невпихуемое типа абстрактной фабрики и т.д. Да и 80% разработчиков и не слышали о них, может это и к лучшему =))
з.ы.
И да, стараемся переписать на микро-сервисы… сложность убегать на уровень выше…
Пара идей, которые возникли у меня в процессе размышлений над похожими проблемами.
- Множество простых решений похоже всегда сильно меньше множества всех возможных решений. Поэтому найти простое решение сложно.
- Задачу, если смотреть на неё, как на математический объект, типа вектора, можно по разному раскладывать на компоненты и от этого зависит сложность. Возможно вы встречали ситуацию, когда после рефакторинга удалялось 90% кода, а система продолжала работать. Это и есть выбор правильного базиса для решения задачи.
В математике есть масса примеров. Матрица может быть «сложной», без какой-либо структуры, но ее можно свести к Жордановой форме. Сигнал может быть сложным, но раскладывается в ряд единообразных компонент — sin/cos, вейвлеты и т.п.
Другими словами, если на множестве решений можно задать операцию дифференцирования, то вопрос с поиском наиболее простого решается несложным алгоритмом. А пока мы ждём этого открытия, каждый программист в своей голове, примерно за 10 лет, выращивает нейронную сеть, которая эту задачу приближенно решает.
если на множестве решений можно задать операцию дифференцирования, то вопрос с поиском наиболее простого решается несложным алгоритмом
Есть одна структура, рядом есть другая. В каждой по 1000 отличающихся от соседа элементов. Эта 1000 = производная. Дайте несложный алгоритм, устраняющий эту сложность.
Кажется вы не поняли моего комментария и задаёт вопрос с ним не связанный...
Если на множестве «решений» есть операция дифференцирования то, все что вам нужно это задать целевую функцию и запустить алгоритм градиентного спуска или аналог.
Чем такой вариант хуже вашего? Он точно так же не показывает сложность промежуточных шагов. И точно так же выглядит «просто».
И за одно — градиентный спуск может остановиться в локальном экстремуме. А целевая функция может не соответствовать потребностям всё из-за той же сложности. Ну и вообще аналогии могут увести в такие дебри, что потом не отмоешься.
Надо признать — сложность просто не устраняется. Ну и жить с этим.
Несколько мыслей по поводу сложности. У сложности есть разные причины и вот некоторые из них.
1. Сложность как следствие предметной области. Это самая честная сложность.
2. Сложность как следствие привычки. В эту ловушку я попал на своем пет проекте. Я его писал прям с нуля и со временем он оброс всякими вспомогательными штуками: системой локализации, тестами АПИ, системой валидации и др. И в какой-то момент мне нужно было сделать другой пет проект так же с нуля за ограниченное время. Ты уже привык к своей среде, а главная ценность время и нужно следовать ей.
А еще меня всегда поражали туториалы уровня Hello world!, где первая часть это настройка IDE, окружения, деплоя, прогона тестов. А вторая часть это Hello world! и все что настроено в первой части по большому счету не используется. Это можно рационально объяснить тем, что «хорошие привычки нужно прививать сразу». Но это не всегда возможно. В конце концов «На ошибках учатся. А некоторым вещам учатся только на ошибках.»
3. Сложность как следствие пессимизации задачи. По своему парадоксальное явление. Люди исходят из того, что не бывает простых задач. Бывают только сложные. И выбирают инструменты для сложных задач. Если бы это был осознанный выбор «на вырост» я бы понял. Но до «выроста» еще дожить надо. Это как: «Нам поручили спроектировать сортир на даче. Мы исходим из концепции хайлоад, поэтому будем дорабатывать проект туалета для молла в центре Москвы».
4. Сложность как следствие плохой структурированности. По большому счету нет разницы засунули ли вы 20 классов в один мегакласс или разложили по одному классу в 21 файл. Задача решается одна и та же тем же способом. Но развивать и поддерживать мегакласс сильно сложнее.
Борьба со сложностью начинается с вопросов «А зачем X нам? Что X дает?» Оказывается это очень непросто выбирать инструмент соразмерно задаче.
А еще меня всегда поражали туториалы уровня Hello world!, где первая часть это настройка IDE, окружения, деплоя, прогона тестов. А вторая часть это Hello world! и все что настроено в первой части по большому счету не используется.
Просто это туториал не по написанию hello world, а по настройке инфраструктуры. Hello world в таком туториале — это лишь способ продемонстрировать, что вся инфраструктура настроена и работает.
Или есть фреймворки, с довольно сложной структурой файлов. При этом допускающие гибкую кастомизацию. Задача у нас простая и стандартная структура избыточна. Можем её упростить, потратив на это время. Но когда новый человек придёт на проект, нам надо будет тратить его и своё время на объяснение нашей структуры, её отличия от стандартной. Ну и если всё будет хорошо, будем тратить время на обратное усложнение. Есть ли в этом смысл?
Пару месяцев назад к нам приходил программист собеседоваться. Невероятно умный. вот просто гений. он ответил на 100% каверзных вопросов про Javascript. но когда я попросил его обрисовать общую схему, как бы он простой бекенд на ноде делал, он сказал — ну как, один файл, там роутер и сразу обработку — достали из базы и вернули. я такой — а как же слои там, все дела? Он — так задачи не стояло сделать что-то расширяемое. Надо было просто решить задачу. Если там появится еще что-то — тогда типа и буду делать слои. а без необходимости — какой смысл?
Я не могу прям 100% с этим согласиться, но некоторое рациональное зерно в этом есть
Имитация Сложности — Антиномия Простого и Сложного