Pull to refresh

Comments 151

UFO just landed and posted this here
В статье речь о получении предсказуемого и воспроизводимого результата в организации.

Как в слепой печати: можно и двумя пальцами научиться печатать быстрее, чем 10-тью, но методика обучения с более-менее гарантированным результатом существует только для 10-пальцевого метода.

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

С фрилансерами всё еще сложнее: они ведь могут быть и частью распределенной команды.

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

Не стоит принимать написанное в статье на личный счет. Статья о том, как построить хорошую команду, а не о том, что не бывает профессионалов-одиночек: бывают, но на всех их не хватает.
UFO just landed and posted this here
Это называется «долина смерти»: точка роста, которую очень трудно пересечь.

Сложнее всего вырасти с одного разработчика до трёх. Особенно если проект не имеет финансовых резервов.

Многие мои знакомые фрилансеры отказались от попыток вырасти именно из-за нее. Предпочли поберечь здоровье, достаток и крепкий сон.
Есть другое объяснение этому эффекту, вот, например, статья.

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

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

В нашей компании есть человек, который индуцирует культуру программирования, заставляет использовать все новые фичи С++. При этом, навязываемая им культура иногда проибретает причудливые формы. Например, он может в чужих текстах объявления всех переменных поменять на «auto». Потом прививает новый стиль программирования и именования переменных, при этом отвергая тот стиль, который сам же навязывал три года назад (в угоду новым модам).
Это кстати не правильно. Я сам знаком с auto и предпочитаю иногда написанный strong type что бы было понятно с чем оперируем. (легче читать).

А все новые фишки C++, это тоже не подарок — т.е. драматического улучшения софта из-за них я не вижу.

Лучше давать рекоммендации, и смотреть возьмут ли программисты их в употребление. (т.е. дать им самим подумать и выбрать)
Согласен, auto удобно использовать внутри цикла, чтобы не думать что там за тип итератора. Очень удобно в шаблонах — чтобы не создавать уродливые typename T::X а вот в обычном коде — это зло, которое кстати не очень удачно раскрывают все ИДЕ, и которое вредит если у нас функция возвращала char* а переменная нужна типа std::string.
т.е. драматического улучшения


Eng
dramatic сущ. | Вебстер | фразы
общ. бросающийся в глаза


грандиозный
dramatical прил. в начало
общ. очень сильный

Именно поэтому быть техническим лидом — тяжелый философский выбор.
Как заставлять других применять те или иные подходы, если по опыту уже знаешь, что через 3 года переосмыслишь свои текущие подходы, и найдутся более эффективные и лаконичные решение.
Более того, индустрия уйдет в перед, и даст средства которые позволят решать задачи с помощью подходов, которые раньше было нельзя применять из-за ограничения языка/платформы, и текущие подходы придется поменять.


Но, пожалуй, худшие вариант, когда техлид и/или носитель культуры застрял в своих подходах N-летней давности, или подходах, бывших актуальными на момент старта текущего проекта.
И вот когда пытаются команду остаться в таком технологическом и культурологическом легаси — хуже всего.


Наверное, можно выработать какой-то протокол, что через каждые N времени (и/или по какому то триггеру, как то выход новой версии платформы) рассматриваем новые фичи и вырабатываем уточненные подходы в культуре.


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


Что касается auto, то, к примеру, в C# это var. И это хороший пример, показывающий, что культура это неизмеримая величина, и именно из этого нужно исходить при ее внедрении:


Есть два крайних подхода, когда var не используется нигде (кроме анонимных типов, для чего var и был придуман), и до того, что используется везде.
Во втором случае пишется даже "var someFlag = false". Но ведь это же явно другая крайность, которая для статически типизированного языка не подходит.


А где тогда золотая середина?
Очевидно, тут:


var myList = new List<object>();

А вот дальше уже неочевидно:


var myList = someObject.GetSomeList();

или


List<object> myList = someObject.GetSomeList();

Если второе (в общем случае GetSomeList может называться иначе, когда из название тип результат неочевиден), то как (культурно ли?) будут смотреться рядом две строки:


var myList = new List<object>();
List<object> myList1 = someObject.GetSomeList();

Несмотря на все официальные Style Guides, четкого ответа на все случаи нет.


Даже если принять жестко какую-то систему, например, "всегда var", то даже здесь есть место исключениям. Согласно тем же Style Guides, если нам нужен не "полный" тип результата, а только какая то его часть (базовый тип или один из множества реализованных в типе интерфейсов), то нужно писать так:


ISomeInterface myObject = someObject.GetSomeValue();

Поэтому важно понимать, как именно обращаться с культурой программирования, и как ее внедрять.
Нужно понимать, что это именно культура — что-то, относящееся к гуманитарной сфере, неизмеримое и не втискиваемое в скрамы и в джиру.
И при этом — совершенно необходимое.

Несмотря на все официальные Style Guides, четкого ответа на все случаи нет.


Очень многие проекты (open source) которые я видел имеют какой то свой стиль кодирования, который бывает иногда очень четко прописан — но с моей точки зрения это больше относится к тому как пишешь — стиль, а это не так важно с точки зрения deadlineов или общей работоспособности кода.

Более важно было бы документировать как код делался. Каким способом, каким подходом. Почему он лучше работает чем остальные варианты.

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

            } //for
        } //using

        return csInfo;
    } //getCsFileInfo
} //class CsScript


Знаю что многие будут над этим смеяться, но мне самому удобно, и я сам никому свой стиль не навязываю. :)
делал то же самое для своего кода на Ruby (даже плагин для vim'а пробовал писать, который бы это сам ставил), и потому догадывался, почему Вы так делаете, но не ожидал увидеть простыни на ~350 строк 8-0…
Между C# и C++ разница, на самом деле, довольно большая. Если в первом случае любая IDE всегда корректно показывает тип для var, то вот в C++ такого уже не наблюдается из-за невероятной сложности языка.

Кстати, из-за этого, программируя на C++, я вместо предпочтительного гайдлайнами std::make_unique(...) пишу std::unique_ptr(new T {… }), потому что во втором случае IDE подсказывает аргументы.

P.S. А вообще, мне не очень понятно, поэтому соглашения о правилах написания кода рассматриваются в контекте культуры программирования в целом.

Естественно, культура программирования это нечто большее, чем соглашения о стиле кода.
Тем не менее, такую частность, как auto/var, упомянули в дискуссии, и это показалось хорошим примером показать "неизмеримость" культуры программирования.

Кстати, ещё хочу добавить насчёт:

var myList = new List<object>();


Когда я пишу код и объявляю переменную, не являющуюся результатом вызова функции, мне проще сначала определиться с типом переменной, а уже потом с тем, каким образом я хочу её проинициализировать. А вдруг я захочу проинициализировать null?

Поэтому в моём случае код всегда будет выглядеть так:
List<object> myList = new List<object>();


Принудительная замена явного указания типа на var — это насилие над разработчиком в данном случае, т.к. нисколько не меняет качество кода, а вот отвлекаться заставляет. Code Style Guides должны давать разработчику здесь некоторую свободу.

Это необходимое насилие и свобода тут даром не нужна.
Почему необходимое?
Из-за самой природы антипаттернов: вредных приемов, которые популярны из-за особенностей работы человеческого мозга.
Вариант с явным указание типов здесь читается ужасно из-за тавтологичности. В этом можно легко убедиться заменив список на словарь с парочкой уровней вложенности генериков.
Вдобавок это еще и экзотика — такие списки нужны редко и (даже когда нужны) в 99% случаев создаются с помощью ToList().

Вариант с явным указание типов здесь читается ужасно из-за тавтологичности. В этом можно легко убедиться заменив список на словарь с парочкой уровней вложенности генериков.

Не спорю, но как от этой тавтологичности избавиться, если myList — не переменная, а член класса?


Правильным решением здесь будет эволюция языка:


List<object> myList = new();

Но, к сожалению, это предложение находится в состоянии обсуждения уже 2 года.

С членом класса от типа при объявлении не избавиться никак.
Но можно избавиться от типа при вызове конструктора, используя ToList в конструкторе класса-владельца.

А вдруг я захочу проинициализировать null?

Хороший пример. Допустим, нам нужен List(Of Object), который мы хотим инициализировать как null.
В случае "всегда var" наш код будет выглядеть так:


var myList = (List<object>)null;

Очевидно, что "всегда var" здесь выглядит очень искусственно.


С другой стороны, это показывает необходимость not nullability в языке.

UFO just landed and posted this here
По идее все что нужно знать — что managedProjects это коллекция.

Те, кто привык к Entity Framework, при чтении чужого кода далеко не сразу осознают этот факт. Потому что там Find возвращает одну сущность.

UFO just landed and posted this here

Осторожно! Сейчас вас могут заклеймить венгерской нотацией.
Хотя наверно лучше было бы назвать метод FindAll

UFO just landed and posted this here
Венгерская нотация это про префиксы, а не про единственное или множественное число, например iNumber, nFound.

К тому же, ничего плохого в них нет. Префикс необязательно на тип данных указывает.

Так культура может внедряться не только хорошая :)
И профессионалы непрерывно растут над собой.
Да и подходы за три года в некоторых областях могут измениться радикально.


Например, он может в чужих текстах объявления всех переменных поменять на «auto».

У вас нет коллективного владения кодом?

Менять уже написанное работающее «просто так» — это уж точно bad style, можно жестко поговорить и руки оторвать. Если только это не делается в рамках запланированного рефакторинга.
auto же — очень хороший practice, почему бы его не использовать за исключением случаев, когда это невозможно (объявление переменной, например)? Вместо того, чтобы писать типпеременнойизмногобукав.
С фрилансерами всё сложнее.
Чаще всего те, кто работает над проектами в одиночку скатываются в говнокод. Те, кто работает в распределённой команде обычно не скатываются. В то же время очень важна частота смены проектов/технологий и состав команды. По поводу технологий -даже самый лучший разработчик скатится в говнокод работая над одним проектом долгие годы. К тому же сами проекты, которым больше 5 лет превращаются в рассадник говнокода (вернее legacy-кода) и становятся настолько хрупкими, что изменения ведут к непредсказуемому результату, из-за чего разработчики теряют инициативу и просто плывут по течению. Опять же сложность системы ведёт к появлению костылей и приводит к созданию нового ещё более некачественного кода. По поводу команды — нужны люди, которые болеют за проект и чтобы всегда можно было бы учиться друг у друга.
Относительно «старения» проектов и команд — все мы противостоим энтропии. Задача эта безнадёжная, вопрос только в том, как продержаться подольше :)
Тоже на этом споткнулся, вспомнил свою команду, где всё отлично, и производная качества вроде бы неотрицательна, и пошёл задавать в точности такой же вопрос.
Отличная статья, со многим соглашусь. Будет ли продолжение? Описанные подходы выглядят как первые шаги. В том плане, что в цикле forming-storming-norming-performing эти активности позволяют удержать контроль на первых двух этапах (конечно особенно сложных). Интересно было бы поделиться также механиками на терьем и четвертом :)
Про Adjourning никто не хочет вспоминать :-)
Про модель командной динамики Брюса Тукманна и так довольно много публикаций.

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

Надо позволять группе принимать ошибочные решения, если не можете убедительно доказать их ошибочность без насилия. Если продолжаете следить за кодом, делайте это скрытно, чтобы у разработчиков не создавалось ощущение безопасности, что их подстрахует если что — оно ведёт к безалаберности.

Задача лидера здесь — помогать группе, оберегать её от найма «токсичных» участников, следить за сохранением комфортных условий труда, справедливой и своевременной оплатой. Снабдить группу лучшими практиками: стандартом оформления кода, рекомендациями по кодированию и архитектуре, методологией разработки, методикой и процедурами управления кодом, ошибками, требованиями, документацией и т.д., но вводить их не силой и все сразу, а убеждением и когда все почувствуют их необходимость.

Выполнять роль фильтра между проблемным заказчиком и группой разработки: прогнозировать будущие изменения требований и заранее их предусматривать в проекте, проверять на ценность, обоснованность и целесообразность требования заказчика, разъяснять и убеждать скорректировать, а не спускать их сразу разработчикам. Принимать на себя удар в случае внешний конфликтов и противостоять искушению сразу же обрушиться грозой на команду.

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

В общем, все написанное в статье и в комментарии, можно упаковать в короткую фразу, которую любит мой знакомый инвестор. Привожу здесь приличную ее версию: не быть [упрямым, ленивым, эгоистичным самодуром, похожим на кастрированного осла] и не брать таких в команду. :-)

Я согласен с преведущим комментарием — статья отличная. Но дело в том что обсуждаются проблемы плохого кода, а не способы их решения.

Я бы предпочел какой-то документ, для разработчиков — о том как надо писать хороший код.

Я сам когда-то писал и инспектировал мануалы для программирования. Думаю что основная проблема в том что написать и проверить хороший документ сложно и займет много времени.

Потом после инспектирования все равно есть шанс что документ сделан не до конца хорошо, и хотя бы что то там да и не учитывается. До и сам документ надо обновлять и постоянно учитывать новые техники.

Да и в общем то когда пишешь coding manual, то все равно найдутся люди которые скажут — а на кой это надо вообще — давайте сделаем так. Ну т.е. консенсуса между программистами не достугнуть никогда, хотя бы просто потому что все свои собственные личности.

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

Из того, что я пробовал, и давало хорошие результаты (но в конце концов, все-равно надо ожидать трений):
  • Минимальный мануал по стилю, зачастую можно ограничится одной фразой: «Код пишется для людей, а не для машины». Стену текста все-равно никто не читает, а так хотя бы будет минимальный чек-лист, по которому реально проверять код перед коммитом.
  • Все максимально автоматизировано: коммит вызывает CI цепочку — проверка стиля кода, запуск юнит-тестов, генерация документации, генерация конечного пакета, интеграционные тесты с использованием конечного пакета.
  • Семинары по обмену опытом, показ используемых инструментов, их настроек и как это облегчает жизнь. Даже такие мелочи, как fish shell, могут сделать процесс разработки приятнее.
  • Использовать промышленные стандарты, так проще ссылаться — «если все сообщество так работает, то почему бы нам не следовать их стандартам».
  • Code review, но нужно быть очень осторожным в высказываниях. Желательно подкреплять примерами. Совсем недавно у меня было откровение, что еще не все знают, что singleton — это антипаттерн, который стоит использовать очень осторожно. Мне не верили, пришлось искать статью с подтверждением.
Тут на самом деле много дельных рекомендаций, и хотел бы их получить в виде документа.
Желательно на английском языке, потому что сам с Финляндии, но если это будет сложно, можете написать и на русском.

Здесь сделал в google документ для свободного editирования:

docs.google.com/document/d/1c5Aq4DcpKx98MNRi5YI766k6VqwDjxEhFwCSV9WHMts/edit?usp=sharing

Т.е. у всех есть доступ его модифицировать.

Ну и набросал туда кое что от себя, но думаю об этом надо отдельно обговорить, возможно часть текста можно и изменить и модифицировать.
Singleton резко становится антипаттерном, когда у вас все через контейнер и там лайфстайлы. А вот если у вас какая-нибудь асинхронная производительная многопоточность, так все на статике, чтобы аллокаций не было. Подходы чаще бывают не «хорошие» и «плохия», а работающие в данном контексте или нет.

Синглтон и в этом случае антипаттерн, и в любом другом тоже.

Я использую такое правило: если объект в теории может существовать в нескольких экземплярах, сингтлоном он быть не должен.

Вот примеры ситуаций, когда никаких проблем с синглтоном нет: хранение результатов парсинга аргументов командной строки, стандартный ввод-вывод, параллельный планировщик, менеджер динамической памяти.

А вот примеры ситуаций, когда синглтон может быть нежелателен и при развитии программы может привести к необходимости рефакторинга: соединение с базой данных, хранение настроек.
Не вижу поводов делать синглтоном ни ввод-вывод, ни парсинг, ни менеджер, ни планировщик. Кто его знает, что там будет при развитии программы?

В долгосрочной перспективе важна гибкость. Если не закладываться на нее, то синглтоном можно сделать почти все — и объект юзера и настройки его и даже объект текущей ошибки.
> Не вижу поводов делать синглтоном ни ввод-вывод, ни парсинг, ни менеджер, ни планировщик. Кто его знает, что там будет при развитии программы?

Но у программы аргументы могут быть только в единственном экземпляре, например — другого просто не придумали.
А если придумают и реализуют? Или потребуется возможность использовать два набора аргументов с возможностью переключения между ними в рантайме? Проблема синглтона не в том, что он не работает или работает плохо. Проблема его в том, что несмотря на свою простоту и эффективность, рано или поздно он может привести к проблемам, для решения которых придется просматривать весь код.

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

Я согласен, что в энтерпрайз-разработке удобство сопровождения и стоимость изменнний — одни из ключевых параметров. Однако, стоит учитывать, что более гибкие решения дольше и дороже а реализации (в т.ч. потому что требуют привлечения более квалифицированных специалистов) и в общем случае уступают в производительности.

Решение следует принимать не по принципу «всегда делали так», а в контексте. Вот какова вероятность, что вы внезапно решите заменить ORM? С EF на NHibernate — едва ли (хотя я знаю один такой случай). А вот на Dapper — вполне возможно. Значит мешать нам будет код, завязанный на IQueryable. При этом если, скажем, кеш будет singleton'ом проблем не возникнет.
У синглтона есть еще одна проблема — в большинстве случаев он просто не нужен от слова совсем. У меня синглтон существует только в одном месте — сервис-локатор. Все. За два года я не встретил ни одного случая чтобы какой-то класс по логике был бы одиночкой, и при этом не был бы сервисом. А сервис-локатор и так идеально справляется с тем чтобы инстанцирование было один раз. При этом если к примеру то же соединение с базой нужно будет два раза, то мы просто меняем конфиг, не трогая код, который спокойно себе инстанцируется столько сколько нужно.
Во всяком случае в однопоточном коде я совсем не вижу места для синглтона.

Сервис-локатор тоже тот еще антипаттерн. Код, активно его использующий, скрывает тем самым свои зависимости. Лучше уж синглтоны — там хоть с помощью IDE можно найти кто от чего зависит.

Скрывает от кого?
Сразу видно что ты вызвал тот или иной сервис. Прозрачное внедрение которое подменяет несуществующие методы одноименными сервисами — зло конечно. ИДЕ да, может не понять, но это решается или допиливанием ИДЕ (обычно есть механизмы настроек) или в тупую генерацией псевдокода отдельной консольной командой, чтобы ИДЕ подхватывала синтаксис из него.
А синглтоны там где уместен сервислокатор… Ну вот есть у вас кодовая база на 500 классов. Среди них класс связи с СУБД сделан синглтоном. Проект живет и здравствует. И вдруг задача — сделать репликацию мастер-слейв, чтобы запись шла в мастер-сервер, чтение из пула слейвов. Плевое дело — пишем врапер, если запрос начинается с SELECT значит случайный слейв, если с чего-то другого, то на мастера. Потом еще пару минут чтобы поиском и заменой заменить название класса по всей кодовой базе. И таск можно закрывать.
Потом прилетает таск на расширение сервиса отправки писем, чтобы слать не через одно соединение а через пул, ну и с очередью. Работы часа на два с тестами. И еще минуты три чтобы везде классы поменять.
На закуску нам нужно кастомизировать шаблонизатор добавив в него новую фичу. Аккурат до конца дня работы.
У нас пятница, так что надо побыстрее выкатить в мастер…
Что? У нас еще сотня проектов с тем же ядром, и там эти фичи не нужны? А нет, повезло. Нужны… Правда фреймворк мы писали не сами, и на следующей неделе крупное обновление. К счастью совместимое со старой кодовой базой.
Нужно будет только накатить наши обновление имен классов по всему фреймворку, ведь сервислокатор это зло…
А давайте этот процесс автоматизируем?.. Поздравляю. Вы изобрели VQMOD.

Джентльмены, а как же обычное внедрение зависимостей?

DI и ServiceLocator это близкие по сути подходы, отличающиеся в основном синтаксисом. При сервисЛокаторе компонент запрашивает то что ему нужно, при внедрении зависмости — ядро дает ему то что ему нужно. Внедрятель чуть более гибкий, СЛ — проще и понятнее. Во многом дело вкуса. Лично я пока не упирался в необходимость индивидуального изменения зависимостей кроме персональной подмены базы для некоторых классов, но у меня это сделано в конфиге, так что не вижу смысла утяжелять. Будет потребность — перепишу, благо это быстро. Но в споре синглтона против сервисЛокатора ДИ явно на стороне СЛ.
DI и ServiceLocator это близкие по сути подходы, отличающиеся в основном синтаксисом

Нет. Локатор использует контракт со скрытым состоянием (возвращает любой сервис, но только зарегистрированный), нарушение которого ведет к ошибками периода выполнения, причем в любой момент.
Если ваш класс зависит от локатора, то реальные зависимости надо искать по всему его коду, а с DI достаточно параметров конструктора.

Это да, но в контексте одиночки они не очень отличаются.

Ничего не понял, но вам отвечу.


А сервислокатор там где уместны синглтоны… Ну вот есть у вас кодовая база на 500 классов. Среди них класс связи с СУБД зарегистрирован в сервислокаторе. Проект живет и здравствует. И вдруг задача — сделать репликацию мастер-слейв, чтобы запись шла в мастер-сервер, чтение из пула слейвов. Плевое дело — пишем врапер, если запрос начинается с SELECT значит случайный слейв, если с чего-то другого, то на мастера. Потом еще пару минут чтобы поиском и заменой заменить название класса по всей кодовой базе. И таск можно закрывать.
Потом прилетает таск на расширение сервиса отправки писем, чтобы слать не через одно соединение а через пул, ну и с очередью. Работы часа на два с тестами. И еще минуты три чтобы везде классы поменять.
На закуску нам нужно кастомизировать шаблонизатор добавив в него новую фичу. Аккурат до конца дня работы.
У нас пятница, так что надо побыстрее выкатить в мастер…
Что? У нас еще сотня проектов с тем же ядром, и там эти фичи не нужны? А нет, повезло. Нужны… Правда фреймворк мы писали не сами, и на следующей неделе крупное обновление. К счастью совместимое со старой кодовой базой.
Нужно будет только накатить наши обновление имен классов по всему фреймворку, ведь синглтоны это зло…
А давайте этот процесс автоматизируем?.. Поздравляю. Вы изобрели VQMOD.

А разве сокрытие деталей реализации не один из столпов ООП?

Хотя да, бывает не очень удобно.

Зависимости — это часть контракта, а не деталь реализации. Просто потому что компонент, имеющий внешние зависимости, невозможно использовать в среде где этих зависимостей нет.

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

Если пользователю компонента не требуется создавать компонент — он его и не создает, а запрашивает его в качестве зависимости. Или запрашивает фабрику компонентов, если таковых ему нужно много.


А вот тому, кто компонент таки создает — знать его зависимости нужно.

1) Не всегда это протечка абстракции, не всегда из-за чего-то в недрах. Отвественностью класса может быть работа с инстансами другого класса, которые укаызвает клиент.


2) Даже если это протечка и клиенту нужно сначала создать, а потом заинжектить что-то что ему самому лично не нужно, то всё равно это часть контракта. Ну вот такой текущий контракт.

Если конструктор в явном виде принимает все зависимости, но при этом клиенты конструируемого класса не имеют возможности его явно создать и не знают конкретного класса, инкапсуляция соблюдена? Это будет DI.
но при этом клиенты конструируемого класса не имеют возможности его явно создать

Вот это — лишнее.

Это исключает каскадные изменения при замене реализации какого-либо интерфейса. Такой уж он этот di.

Для того чтобы исключить каскадные изменения, достаточно не создавать класс явно там, где это излишне. Вовсе не обязательно еще и запрещать такое создание техническими средствами :-)

Запрет выражает намерение более явно. А говорящий код является чуть ли не основным принципом джентльмена.

Запрет делает обязательным применение "тяжелых" инструментов вроде IoC-контейнеров. И мешает, к примеру, составить MCVE для исследования проблемы.

Запрет делает обязательным применение "тяжелых" инструментов вроде IoC-контейнеров.

Не делает. Контейнер — всего лишь инструмент для реализации точки сборки. Можно обойтись и без него.

А если придумают и реализуют?
Если, если,…
Вы всегда разрабатываете архитектуру для всех возможных вариантов?
Конечно. Задача архитектуры в том и состоит — система должна быть расширяющийся и масштабируемой, независимо от тех требований, которые появятся в будущем. Решается это через управление сложностью таким образом, чтобы любой кусок кода можно было бы взять и заменить. Иначе в чем ее смысл?

А касаемо «если-если»… если не если, то подойдет любое решение, хоть как-то работоспособное. Ведь мы не рассматриваем его применимость в изменившейся ситуации.
Проектировать нужно не для всех возможных случаев, а только для текущего, но предусматривать возможность расширения. Довольно тонкая грань. Еще лучше — проектировать только для естественных в автоматизируемой предметной области случаев. Но… может вы это и имели ввиду?
чтобы любой кусок кода можно было бы взять и заменить
Ну так внутри сиглтона мы можем поменять все что захотим
Можем… до первого обновления. Если фреймворк «не наш». Или «наш» но не до конца. Вы (все кто так отстаивает одиночку) таки почитайте что такое VQMOD. Этот король антипатернов применяется далеко не только для каскадных обновлений. Для вот таких вот точечных — очень даже.
Меня тут на хабре лет пять назад и за меньшее тухлыми помидорами закидали.
Можем… до первого обновления. Если фреймворк «не наш». Или «наш» но не до конца.
И что же будет? если контракт не поменялся то все ок, если поменялся, то если бы у нас был бы интерфейс то он тоже бы поменялся, так что проблемы будут и там и там
Смотрите. Есть фреймворк. Его пишу допустим я.
Я застрял в прошлом и не пользуюсь ДИ/СЛ, а использую синглтоны везде.
Есть вы, который использует мой фреймворк.
Вы подтягиваете его через менеджер зависимостей того языка на котором сделан фреймворк. Ну или тупо гитом, не суть. Плюс модули написанные третьей стороной. У модулей и у самого фреймворка разумеется постоянно используются и СУБД и шаблонизатор и нотификейшены и т.п.
Ну и в своем коде вы их используете конечно.
И тут вам понадобилось внести в них небольшие изменения (см. выше). Плёвое дело — отнаследовались от моих классов, расширили буквально парой строчек и вперед.
А дальше нужно сделать так, чтобы теперь вся груда кода вызывала именно ваш класс а не мой.
Путей здесь несколько.
1) можно втупую изменить МОИ классы вместо наследования
2) можно отнаследоваться и в ИДЕ поиск-замена по имени класса сделать
3) использовать VQMOD или написать свой аналог. (похоже на 1) и 2) т.е. мы меняем чужой код, но делаем это автоматически, с помощью патчера
4) можно переправить код, но положить его у себя, и включать его грязным хаком за который меня тут пять лет назад закидали гнилыми помидорами (в целом справедливо, хотя я и тогда говорил что это грязный хак, и исключительно для кривой архитектуры)

Первые два варианта вызовут большую головную боль при обновлении фреймворка и/или модулей. Нужно будет снова и снова, при каждом обновлении накатывать ваши правки.
Третий и четвертый варианты смягчают эту проблему, и при многих обновлениях все пройдет автоматом, но не при всех, да и они так воняют, что лучше такого избегать.

Такой подход прямо противоречит «The Open Closed Principle». Причем по всем фронтам. Закрытость для расширения вынуждает нас открываться для изменения.
Смотрите. Есть фреймворк
А если синглтоны только в моем коде приложения? не в фреймворках, не в библиотеках.

Каким образом обсуждение перешло с архитектуры прикладной программы на фреймворки?


Да, у разработки фреймворков есть особенности. И главная заключается в том, что трудно найти программу, которая бы использовала все фичи фреймворка — то есть во фреймворке всегда есть ненужные части. Поэтому от фреймворка требуется уметь прятать ненужные настройки. Сервис-локатор тут действительно решает эту задачу. А проблемы самого сервис-локатора решаются тщательной документацией.


Но в прикладной программе синглтоны по-прежнему лучше сервис-локатора, а DI через конструктор лучше синглтонов.

DI через конструктор лучше синглтонов
Можно узнать чем же?
DI через конструктор лучше синглтонов
Можно узнать чем же?

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

Абсолютно всем.

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

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


Во-вторых, зависимость может быть и конкретным классом, хоть это и считается не по канону. Но лучше уж конкретная зависимость чем синглтон.

Во-вторых, зависимость может быть и конкретным классом
Мы не можем быть в этом уверены, т.к. могут передать потомка класса

Слово "конкретный" подразумевало что потомков у такого класса нет. Просто потому что в таких потомках нет смысла.

Слово «конкретный» подразумевало что потомков у такого класса нет. Просто потому что в таких потомках нет смысла.
Какой смысл тогда передовать этот класс через DI если можно его создать прямо в классе?

Очевидно, возможные зависимости этого конкретного класса.

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

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

А какая разница? Нет, если прикладная программа на два-пять экранов кода, то можно что угодно городить.
По мере роста можно переделать «по взрослому». Но если у вас большая команда с разными участками ответственности и высоким уровнем повторного использования кода, то необходимость инверсии зависимостей становится все очевиднее и очевиднее.
При этом даже если приложение небольшое, то скорее всего оно живет не в вакууме а в каком-то бекграунде, где скорее всего уже есть сервислокатор или ДИ.
А проблемы самого сервис-локатора решаются тщательной документацией

И какие же проблемы у сервислокатора в сравнении с синглтоном? У синглтона да, проблем полно. Начиная от сложностей тестирования (внятно тестировать синглтон можно разве что при условии что он у нас без состояний, но тогда нафига ему быть синглтоном?), и заканчивая банальной зависимостью от сущностей а не абстракций, т.е. жирной протечки абстракции я противоречия половине принципов SOLID со всеми вытекающими отсюда проблемами.
Преимущества? Преимущество одно — любая тупая ИДЕ даже без настроек его понимает.
Но в прикладной программе синглтоны по-прежнему лучше сервис-локатора, а DI через конструктор лучше синглтонов.

У вас как-то всё смешалось, люди, кони.
Что вам мешает передавать через ДИ синглтоны?
ДИ это метод получения объектом его зависимостей, при котором зависимости ему предают извне.
СервисЛокатор это по сути фабрика поставляющая запрашиваемые сущности, при этом она возвращает одни и теже сужности на одни и теже запросы, инстанцируя новые сущности только если таковых еще нет в ее хранилище.
Синглтон это по сути СервисЛокатор в миниатюре. Это тоже фабрика и тоже внутреннее хранилище, но сильно упрощенная фабрика с сильно упрощенным хранилищем. Синглтон возвращает сущность из внутреннего хранилища, при этом если внутреннее хранилище пусто, то создает новую сущность, которая может быть только одного вида — экземпляром класса самого синглтона.
По сути синглтон нарушает большинство принципов SOLID:
1) The Single Responsibility Principle — у нас минимум две ответственности. Бизнеслогика и функционал «одиночества». Статическая часть с ее «фабричным» методом никак не связана с бизнеслогикой нашего одиночки, а значит по честному должна быть в отдельном классе
2) The Open Closed Principle — см. выше. Осложняется расширение и вынуждает открывать изменения, провоцируя каскадные изменения
3) The Liskov Substitution Principle — формально не нарушается, но де-факто мы не можем им воспользоваться поскольку нарушен пункт 2)
4) The Interface Segregation Principle — явно нарушен, поскольку у нас смешаны два интерфейса — бизнеслогика и инстанцирование. см. пункт 1)
5) The Dependency Inversion Principle — разумеется нарушается, поскольку клиентский код вызывающий нашего одиночку жестко завязан на конкретике, на классе нашего одиночки а не на абстракции.
Что вам мешает передавать через ДИ синглтоны?

Тот факт, что я говорил о синглтоне как о порождающем паттерне, а не как о характеристике времени жизни объекта.


Это подразумевается самой постановкой вопроса: "синглтон vs сервис локатор", поскольку синглтон как характеристика времени жизни объекта не является альтернативой сервис-локатору. Кажется, все кроме вас это поняли.

у нас минимум две ответственности. Бизнеслогика и функционал «одиночества».

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

object EmailService {
    def send() {
    }
}

одна ответственности, один интерфейс
И каким боком он стал синглтоном?
> А если придумают и реализуют?

Тогда весь Linux придётся переписывать и ломать все стандартные библиотеки.

> Или потребуется возможность использовать два набора аргументов с возможностью переключения между ними в рантайме?

Но передаваться-то они будут все равно одной строкой.

> Проблема синглтона не в том, что он не работает или работает плохо. Проблема его в том, что несмотря на свою простоту и эффективность, рано или поздно он может привести к проблемам, для решения которых придется просматривать весь код.

Если код периодически не пересматривать, то он превратится в окаменелость. Даже реализацию алгоритмов нужно периодически подправлять с учётом обновляющихся процессорных архитектур.

Кстати, стандартные библиотеки на синглтонах построены и синглтонами погоняют, но ничего, всех это устраивает. Вот смотрите: java.lang.Runtime в Java — синглтон, std::cin, std::cout в C++ — тоже синглтоны.

Если говорить о синглтоне как о конкретном порождающем паттерне — то std::cin и std::cout в C++ — не синглтоны, а глобальные переменные. Правда, недостатки у глобальных переменных те же самые, так что можно на этом не останавливаться.


Вы почему-то считаете, что если вы написали консольную программу — она таковой и останется. Но я ниже уже приводил пример: веб-приложение. Если у вас по всему коду раскиданы те же std::cout — то преобразовать вашу программу в веб-приложение со встроенным сервером уже не получится (хотя со внешним сервером есть варианты).


Всех устраивает ситуация с "синглтоном" std::cout по той простой причине что большинстве консольных утилит никогда не обзаведутся высоконагруженными веб-версиями. Тем не менее, даже для того же вывода в std::cout давно придуманы хорошие практики, позволяющие избежать его использования как синглтона.


К примеру, вместо метода dumpToStdout() использующего std::cout рекомендуется писать dumpTo(ostream& out) или вовсе переопределить оператор <<. Это ничуть не усложнит код — но позволит сменить вывод в консоль на вывод в файл даже если в целом не требуется превращать программу в веб-приложение.


И внутри метода dumpTo(ostream& out) ссылка out не является синглтоном даже если туда передан std::cout.

> Вы почему-то считаете, что если вы написали консольную программу — она таковой и останется.…

Да, она таковой и останется, потому что она была так спроектирована изначально. Принцип KISS тоже никто не отменял. Зачем плодить те же сервис-локаторы, когда можно обойтись, например, одной глобальной переменной?

Ну а если мне вдруг захочется спроектировать программу так, чтобы она могла работать и как веб-приложение, то архитектура у него будет другая: инвариантная к представлению логика будет полностью отделена от модуля представления данных.

Дело в том, что представление данных при записи в разные потоки (консоль, файл, сокет и т.д.) будет разное, и здесь нужна более высокоуровневая абстракция, чем прямая запись в поток. И при переделывании консольной программы в веб-приложение его таки придётся переписать, заменив модуль представления на требуемый.
Вот примеры ситуаций, когда никаких проблем с синглтоном нет: хранение результатов парсинга аргументов командной строки, стандартный ввод-вывод, параллельный планировщик, менеджер динамической памяти.

Зато проблемы появляются когда кто-то начинает использовать эти синглтоны (особенно первые два) по всему коду — становится затруднительно сделать, к примеру, веб-версию той же программы. Или вин-сервис.

Синглтон, как концепция, сам по себе — не антипаттерн. Антипаттерном является его классическая реализация. Она сложна в юнит-тестах и не может быть реиспользована в случае изменившихся условий — к примеру, если понадобился еще один объект этого класса, который является синглтоном. В общем и целом получается статический/асбтрактный класс, вид в профиль.

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

Через контейнер будет не синглтон, а частный случай внедрения зависимостей.
Определение синглтона от банды четырех не дает ни малейшего простора для маневра: там явно прописаны и контроль классом числа своих экземпляров, и глобальная точка доступа.

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

Есть такие понятия как субординация, единоначалие и дисциплина.

Есть не противоречащие им понятия демократия и диктатура.

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

Вообще мне кажется самое это то что все в программировании развивается, и техники, и языки, а единственное что не меняется или меняется не координально — это естественный язык (русский или английский). Т.е. язык с Legacy на 2017 лет, без refactoringа и без улучшений.

А язык это та же каша, только программистам проще изъяснятся языком, т.к. они мыслят языком.

А каша в языке дает кашу и в программе.

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

Которые для команды не годятся чисто в силу ее размера.
Но консенсус не нужен в любом случае.

Для диктатуры или демократии в команде достаточно двух человек.


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

Для диктатуры или демократии в команде достаточно двух человек.

Недостаточно. И диктатура, и демократия подразумевают механизмы, которые для двух человек даром не сдались.


Консенсунс нужен в виде хотя бы "кому это делать не против"

И в этом виде не нужен. Я могу быть против решения (и не менять этой позиции), но все равно выполнять, так как оно исходит от лица, имеющего право его принимать.


"не будешь делать — уволю"

Это уже нельзя назвать консенсусом.

И диктатура, и демократия подразумевают механизмы, которые для двух человек даром не сдались.

"даром не сдались" и "исключены" не одно и то же. Причём механизмы практически бесплатны могут быть, типа мержа в мастер только при апруве второго члена команды или только от конкретного.


И в этом виде не нужен. Я могу быть против решения (и не менять этой позиции), но все равно выполнять, так как оно исходит от лица, имеющего право его принимать.

Делать — это уже "не против". Позиция меняется с "я против" на "я не против, хоть и считаю решение ошибочным, но ты — начальник...".


Это уже нельзя назвать консенсусом.

Можно. Стороны пришли с согласию исполнять принятое решение.

Я согласен с преведущим комментарием — статья отличная. Но дело в том что обсуждаются проблемы плохого кода, а не способы их решения.

Говнокод это не проблема, а только симптом. Сама проблема нетехническая.

Я сам когда-то писал и инспектировал мануалы для программирования. Думаю что основная проблема в том что написать и проверить хороший документ сложно и займет много времени.

И никому не нужно. Сложный выверенный мануал для малой группы это гробик. Культура это что-то живое, это что-то, что формируется во взаимодействии. Можно сказать, что это общее поле, которое создается всеми и влияет на каждого. Например, если в команде принято комитить сыроватый код сразу в мастер, один отдельный взятый чистюля ничего не изменит (неоткуда отпочковываться). Наоборот, если master зеленый, то отдельный взятый раздолбай все-таки напряжется и причешет свой код. Просто потому, что в противном случае на него посмотрят косо. Ему будет просто стыдно.
В статье много полезного. Но если выражаться проще, в команде должен быть правильный процесс code review. Когда плохой код возвращают разработчику на доработку. И у заказчиков\руководства должно быть понимание того что на хороший, поддерживаемый код надо тратить +10-30% больше времени. Иначе рано или поздно он свалиться и похоронит весь проект.
К сожалению заложить в бюджет время на рефакторинг очень непростая задача. Т.к. денег и времени на него уходит много, а польза для бизнеса около 0, а иногда и отрицательная. Что уж говорить про рефакторинг, когда многие заказчики не готовы даже платить за автоматизацию тестирования, хотя это может принести как экономию на ресурсах и деньгах в дальнейшем, так и повышает стабильность продукта…

В последнее время всё больше склоняюсь к мысли, что срок жизни проекта в современном мире не должен превышать 5-10 лет. После чего он должен быть полностью переписан на актуальных технологиях и с учётом предыдущих ошибок.

Это стереотип. Вам нужно научиться рефактоить вовремя, а не когда все уже пропало и обосновывать необходимость рефакторинг понятными словами. ISO-9126, например чётко выделяет "удобство сопровождения" как неотъемлемую часть "качества" по.
Тесты заложить уж вообще не проблема. Не надо в плане проекта месяц добавлять на "автоматизацию". Пишите тесты в рамках задач, в которых они действительно нужны.

Я сам привык делать refactoring в тот момент когда закладываю новую функциональность и тестирую её помодульно. То что принято называть архитектура или high level design просто результат того что подходишь к одной вещи с нескольких сторон.

Но ещё проблема в том что сторон подхода может быть больше чем 2 (модульно или с программы) — и не факт что в момент написания программист видит все возможные подходы.

Но у меня в основном необходимость refactorить очень маленькая, засчёт разностороннего подхода, но это не всегда относится к чужому коду.

Но для того что бы делать хорошо, нужны инструкции как это делать, хотя бы с примером как делать.

Пока что я не видел хороших инструкций для написания хорошего кода. Т.е. как сделать практичный и хороший design.
Смотрите комментарий выше:

Минимальный мануал по стилю, зачастую можно ограничится одной фразой: «Код пишется для людей, а не для машины». Стену текста все-равно никто не читает, а так хотя бы будет минимальный чек-лист, по которому реально проверять код перед коммитом.


Я не совсем согласен с этой фразой, но основная идея правильная — документации очень много, и она очень сильно «разбавлена» — т.е. нет хорошего / сильного документа. А здесь вообще сборник кучи книг. Не заставишь ведь всю группу прочитать эти все документы ?!
нет хорошего / сильного документа
Нет не только такого документа, но еще и серебряной пули.
Не заставишь ведь всю группу прочитать эти все документы ?!
В нашей компании знание и умение применять на практике подходы, описанные в этих книгах встроены в план обучения. Никого не заставляем, всем нравится учиться, поднимать свой уровень и с каждым днем делать свою работу чуточку лучше.

UFO just landed and posted this here
Заказчик не должен платить за ревью кода или автоматизацию тестирования. Заказчика вообще не особо касается, как именно происходит работа. Заказчик платит за результат. И если он готов платить, то надо ему выдавать поддерживаемый, стабильный продукт в оговоренный срок. Чтобы он мог ориентироваться на разработку, как на один из столпов бизнеса, а не смотреть на нас как на источник проблем и головняка, каждую неделю вещающих о том, что нужны прокладки 3 на 2, поскольку без заземления лучше брать кабель на полтора и вообще скоро шабат.
Когда в команде культура разработки присутствует, code review превращается в простую формальность. На случай если «глаз замылился».
Опять же поддерживать код придётся в любом случае, только тратить на это время до релиза более безболезненно, чем тушить пожары после.
Есть такая «менеджерская» поговорка — «Культура ест стратегию на завтрак». Какую бы не выбрали идеальную стратегию, как бы ее красиво не разложили на прекрасно приоритезированные action items — без культуры это все глубоко бессмысленно :-( А прививание позитивной культуры — тот еще процесс… Но тот у кого получилось — тот и на коне )
Культуру нельзя купить или украсть, сманив разработчика.

Вообще-то можно. Если сманивать именно тех, кто определяет культуру, именно с этой целью и самое главное — не мешать им впоследствии.
Впрочем, обычно для этого необходима серьезная недооценка на прежнем месте работы.


я делаю так, как мне сказали

Почему это плохо? Можно оспаривать неудачное с точки зрения разработчика решение, но если убедить ЛПР не удалось, то надо делать именно то, что сказано. Это верно для 95% вариантов.


«мне за это не платят»

Это вообще нормальная реакция на любителей выезжать на чужом горбу на халяву.


«главное чтобы на сдаче-приемке работало»

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


«Поставьте мне четкую задачу и дайте инструкцию, как ее исполнить»

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


«В регламенте эта процедура не прописана»

Если за процедуру не по регламенту отрывают голову, то это может быть единственно правильным ответом.


«Главное, чтобы работало»

Но это действительно главное — если не работает, то все прочие критерии качества нерелевантны.


Я неоднократно наблюдал, как стремительно деградирует культура разработки, когда программистов остается меньше трех.

Дело не в числе программистов: на сопровождении оставляют не сильнейших и это, как правило, не источники, а потребители культуры. И в таких случаях менеджмент может в лучшем случае не навредить, но для устранения деградации нужен источник культуры-разработчик.


Мне удобнее нанимать джуниоров

Это опасная стратегия — на джунов проще клонируется имеющаяся культура, но зрелые специалисты могут принести гораздо больше больше пользы, как источники культуры в том числе.

Ваш комментарий мне очень понравился. Но есть несколько нюансов.

«мне за это не платят»

Это вообще нормальная реакция на любителей выезжать на чужом горбу на халяву.

К сожалению, таски в жире не покрывают всё многообразие ситуаций. Представьте — посередине дня упал билд-сервер, который кто-то из команды раньше ставил. Надо разбираться, поднимать. А «мне за это не платят». Коллега «утонул» в своей задаче, и вы видите легкое для него решение, но подсказывать не будете — за это же не платят.

У многих разработчиков проявляется такое плохое, кмк, качество — «я отвечаю вот отсюда вот до сюда, а там трава не расти». В крайнем случае (видел своими глазами) — «я тут код поправлю, а что у других возможно упадет — не моя проблема». Вы пишете код не в вакууме, а для какой-то глобальной цели (работающий продукт, сервис и т.п). И когда нет ответственности за конечный результат (а не за завершение таски) — это ужасно. Сугубое имхо.

В ту же кассу «главное, что работает» — иногда лучше бы не работало. Человек пишет инфраструктурный код, пишет его заведомо с костылями — и это то, обо что сам и коллеги потом споткнутся не один раз. Но «работает» же! Задача выполнена, галочку поставили. Хотя лучше задачу завалить в дедлайн, и потратить еще неделю на нормальное обдуманное решение. Баланс между сдачей в срок и добавляемым техническим долгом иногда (не всегда) очень сложно нащупать.
«мне за это не платят»

С другой стороны, выполнение нежелательных нерелевантных должности задач — путь к профессиональному провалу.
Квалифицированный разработчик, которого заставили половину времени писать мануалы по настройке продукта, т.к. он в этом «лучше всех разбирается», не сможет аппелировать к этому, когда ему откажут в повышении запрлаты/должности исходя из результатов работы. Т.к. компания действительно не готова за это платить.
На мой взгляд, эту фразу должен в голове держать именно непосредственный руководитель разработчика. И если разработчик ее произносит вслух, то дело уже не в культуре разработки: все очень плохо либо с его мотивацией как сотрудника, либо в организации в целом.
К сожалению, таски в жире не покрывают всё многообразие ситуаций. Представьте — посередине дня упал билд-сервер, который кто-то из команды раньше ставил. Надо разбираться, поднимать. А «мне за это не платят». Коллега «утонул» в своей задаче, и вы видите легкое для него решение, но подсказывать не будете — за это же не платят.

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


И когда нет ответственности за конечный результат

… то это безусловно плохая культура. Правда культивируется она обычно руководством, которое само получает вкусные плюшки за хороший результат, а оплачивает только таски.


В ту же кассу «главное, что работает» — иногда лучше бы не работало.

Не лучше. Но всегда надо добавлять, что для проекта "работает" — условие необходимое, но не достаточное.

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

Если речь идёт о кодере (технике-программисте), то возможно, но в случае программиста (инженера-программиста), это даже не идеал, к которому менеджеру нужно стремиться, а нивелирование инженера до техника. В том числе и по оплате его труда, скорее всего.


Это вообще нормальная реакция на любителей выезжать на чужом горбу на халяву.

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

хорошая статья. удивило. начал читать и немного советский стиль изложения немного глаза резал. а оказалось намного внятнее и чётче многих статей с 'современным' стилем)

Это не обязательно советский стиль — это академический стиль. Почитайте статьи или их переводы западных основоположников Computer Sciense — там такой же стиль.

— Безразличие: «я делаю так, как мне сказали», «мне за это не платят», «главное чтобы на сдаче-приемке работало».
— Злой сарказм и травля в коллективе.
— Ненависть к заказчику и пользователю: «копытом буквы набивают», «неправильно используют продукт», «жмоты», «сами не знают, чего хотят».
— Ненависть к руководству: «Некомпетентные эксплуататоры».
— Ненависть к коллегам: «Криворукие дураки».
— Саботаж через исполнительность: «Поставьте мне четкую задачу и дайте инструкцию, как ее исполнить», «В регламенте эта процедура не прописана» и т.п.
— Бездумное исполнение пожеланий заказчик: «Мне сказали — я сделал. На документирование, комментирование, проектирование и прочие глупости нет времени. Заказчик хочет, чтобы все работало ровно так, как он попросил, самым дешевым и быстрым способом. Про развиваемость, обслуживаемость и обновляемость заказчик ничего не говорил».
— Излишняя концентрация на внешнем функционале: «Главное, чтобы работало».
— Излишняя концентрация на архитектуре, в ущерб функционалу: «С точки зрения архитектуры ввод всех этих данных в одном окне сделать нельзя».

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

Не согласен.
Культуру нельзя купить или украсть, сманив разработчика.

Не согласен. Вопрос полномочий и soft skills нового сотрудника, ну и в целом атмосферы в коллективе и настроя сотрудников. Есть неплохие команды с плохой культурой разработки, сложившейся по различным причинам (legacy и т.п.), и там это сработает. А есть просто плохие команды, в которых правят приспособленцы, лизоблюды, просиживатели штанов, люди которые ненавидят начальство или вообще свою работу. Я бы разделял эти вещи, много ваших примеров именно про второй случай.
И еще, как мне кажется, ваша категоричнгость связана с тем, что вы любите нанимать джуниоров. Конечно они более склонны подчиняться правилам коллектива. Кстати почему, вам некомфортно работать с людьми с устоявшимися взглядами или менее склонными к подчинению?
Я занимался разработкой ПО в нескольких организациях и по разным причинам несколько раз перенабирал команду с нуля

Это пугает, почему так случилось? B вы прямо целую команды джуниоров нанимали? Тогда конечно, культура разработки будет страдать.
Хорошая статья, теперь осталось только написать вторую часть: Как воспитать конструктивную культуру в компании.
«Деструктивную культуру», как правило, успешно высевают «сверху». Это не значит, что все разработчики — невинные несчастные, которых «тёмные силы злобно гнетут». Но во многих компаниях, особенно аутсорсных, атмосфера такова, что описанные признаки — это нормальная попытка психически здорового человека сохранить свою психику.

«я делаю так, как мне сказали», «мне за это не платят», «главное чтобы на сдаче-приемке работало»

Вполне нормальный результат, если любое проявление инициативы в лучшем случае игнорировать, в худшем — давить. Особенно, если речь идёт о «галерно-аутсорсной» разработке.

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

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

Ненависть к пользователю: «копытом буквы набивают», «неправильно используют продукт»

Сэкономили на специалисте по UX и отдали его работу на откуп программистам? Кого теперь винить в том, что «продукт» получился хоть и дружелюбным, но крайне разборчивым в друзьях?

Ненависть к заказчику: «жмоты», «сами не знают, чего хотят».

А отдел маркетинга, который продавил заказ по урезанной вдвое оценке, ходит весь в белом. И менеджеры/тим-лиды, задача которых, вообще говоря и заключается в том, чтобы переводить с человеческого на программистский и обратно — тоже ни при чём. Это не считая той ситуации, когда заказчик — действительно, жмот, который сам не знает, чего он хочет.

Ненависть к руководству: «Некомпетентные эксплуататоры».

Бывают ещё компетентные эксплуататоры. И некомпетентные эксплуататоры, которым хватает ума делегировать полномочия компетентным управленцам. Но мы живём в обществе, поделённом на эксплуататоров и эксплуатируемых. Ситуацию можно называть разными красивыми словами, но конфликта интересов это не отменит. Лучший способ взаимодействия с «высоким начальством» — это когда рядовой исполнитель о его существовании смутно догадывается.

Ненависть к коллегам: «Криворукие дураки»

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

Саботаж через исполнительность: «Поставьте мне четкую задачу и дайте инструкцию, как ее исполнить», «В регламенте эта процедура не прописана» и т.п.

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

Бездумное исполнение пожеланий заказчика: «Мне сказали — я сделал. На документирование, комментирование, проектирование и прочие глупости нет времени. Заказчик хочет, чтобы все работало ровно так, как он попросил, самым дешевым и быстрым способом. Про развиваемость, обслуживаемость и обновляемость заказчик ничего не говорил».

О… миддл проданный как сеньор и допущенный непосредственно до общения с заказчиком, потому что второго миддла продали как тимлида. Классика жанра. И да, это тот самый проект, за который взялись по уполовиненной оценке.

И почему это рядовые исполнители не горят желанием вытаскивать на своём горбу косяки руководства и заказчика?

Излишняя концентрация на внешнем функционале: «Главное, чтобы работало».

На эту тему уже высказались выше. Как бы, если оно не работает, то всё остальное уже как-то и не важно. Другой вопрос, что в силу личных качеств разработчиков или проявлений управленческого «гения» это может перерасти в «Работает, ну и ладно».

Излишняя концентрация на архитектуре, в ущерб функционалу: «С точки зрения архитектуры ввод всех этих данных в одном окне сделать нельзя»

«Зачем нам специалист по UX? Часть вторая». Ну не могут, за редким исключением, программисты спроектировать удобный интерфейс. И, да, между UX и архитектурой неизбежен конфликт. Но он должен идти между двумя квалифицированными специалистами до полной победы разума.

Резюмируя: «города сдают солдаты — генералы их берут». Автор, в общем, сам описал как можно довести команду до ручки. И, более того, ожидая от новой команды худшего, разработчик скорее всего окажется прав. Сама модель «аутсорса» неизбежно порождает такую ситуацию. В ней владелец компании заинтересован, чтобы заплатить как можно меньше, получив с заказчика как можно больше. Заказчик заинтересован получить продукт так быстро и дёшево, как это возможно. А непосредственные исполнители не могут не сталкиваться с нехваткой времени, людей, документации и здравого смысла.

«Продуктовые» команды, в целом, обычно здоровее. Есть счастливые исключения и в аутсорсе. Но нет такой команды, которую не может испортить дорвавшийся до руля жлобствующий самодур.
Успели долго поработать в аутсорсе или активно взаимодействуете с последним?:) Уж больно типажи узнаваемые, особенно два миддла, преданные как сеньиор и тим-лид и «компетентные эксплуататоры».

В защиту аутсорсинга: бывают случаи, когда внешняя команда привлекается как раз потому что она, наоборот, более квалифицирована и обладает культурой, а продуктовая команда — нет. Чаще всего это legacy-системы в крупных предприятиях, допиливаемые напильником десителетиями. В таких компаниях IT-отдел как правило находится в изоляции и не способен активно влиять на принятия решений. В итоге, когда всем становится ясно, что так жить нельзя, приходится обращаться за помощью к внешним консультантам как для разработки, так и для реформирования собственного отдела, чтобы тол мог осуществлять поддержку.

Бывают случаи, когда со своей командой все в порядке, но бизнес требует краткосрочного буста, например вывести на рынок новы продут ща 3 месяца. Своя команда занята развитием флагманского направления, а аутсорс тут как тут.

С «галерами» все проще: соблюдается принцип «мусор на входе — мусор на выходе». Если клиент настроен выбирать подрядчика по принципу «дешевле и быстрее», а заодно планирует применить штрафные санкции / не оплатить часть работ, сославшись на *подставьте отмазку*, наверное он понимает, что пострадает качество. Если клиент никак не верит в треугольник компромиссов, что же, это его право, но работать с таким человеком я бы не стал.
— Излишняя концентрация на архитектуре, в ущерб функционалу: «С точки зрения архитектуры ввод всех этих данных в одном окне сделать нельзя».

А можете вот это чуть больше раскрыть? Интересно. И да — спасибо за статью.
Как вырожденный пример могу предложить такого сферического коня в вакууме:
В форме настроек компании в B2B сервисе нельзя сделать редактирование списка администраторов, выбор тарифного плана и загрузку логотипа компании в одной форме.

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

С другой стороны, ничто не мешает расположить три разных формы на одном экране. Особенно если проверять права еще до отображения форм и что-нибудь делать с формами, которым для работы не хватает прав.

На уровне архитектуры решается созданием единого фасада для этих сервисов, за которым прячется координатор распределенных транзакций в том или ином виде от игнорирования ошибок до работы с каким-нибудь "промышленным" DTC. Этот фасад может находиться и непосредственно на клиенте.


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

«Если нельзя но очень хочется, то можно». Это как раз про такой случай.
А вообще есть хорошее слово — целесообразность.
Если целесообразно, то делается, если нецелесообразно, то не делается.
Если звучит «нельзя» то это или культура или не компетенция. Если «нецелесообразно», то тут уже вопрос в том чья ответственность определять целесообразность и вопрос культуры да, как это будет донесено, разрулено и т.п. Тут уже весь спектр от «я так сказал!» до «да сделай ты ему уже, сколько можно мучаться с этим заказом» или «ну я уже пообещал, на будущее буду внимательнее».
Автору спасибо за статью, добавлю в закладки. Очень хороший взгляд на создание команды.
UFO just landed and posted this here
Боюсь, это просто, но дорого. Не один триллион долларов будет потерян на продуктах, которые резко станет некому писать.

А как отличить хороший код от плохого? Есть какой-то исчерпывающий материал про это? Интересно для себя.

Тут выше проводили ссылки на годную литературу.
А так "Чистый код" Роберта Мартина и "Совершенный код" Макконнелла могу рекомендовать к прочтению. Вторая вещь более глобальная о ремесле в общем, первая конкретно о коде.


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

Практики, нацеленные на создание производственной культуры, позволяющей создавать программные продукты в понятные сроки и с гарантированным качеством мне кажется давно описаны старожилами отрасли, такими как Кент Бек, Роберт Мартин, Мартин Фаулер и другими.


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


В общем за 20 лет работы в отрасли я ни разу не встретил команду, которая бы все это практиковала. В такой команде я бы был готов поработать не то что волонтером, но даже доплатил бы за такую возможность.

Три года назад общался с разработчиком из Яндекса. Он сейчас работает в Лондоне в Фейсбуке. Не знаю, как сейчас, но тогда в Яндексе ему все нравилось и культура была и разработчики сильные. Решение о смене работы он принял а первую очередь из-за осознанного желания иммигрировать из России (т.е. смена работы связана не с работодателем, а со страной). Мы работали с Бындюсофтом. Тоже впечатление от сотрудничества только позитивное: и с процессами и с кадрами у них хорошо. Вы уверены, что именно необходимо практиковать все одновременно? Я воспринимаю этот набор практик как «Лего»: берёшь те детали, которые нужны и строишь то, что тебе нужно.

Да, я пожалуй что уверен что все, что предполагает экстремальное программирование (Agile), нужно практиковать одновременно, потому что все это как нельзя лучше соответствует семантике слова культура.


А вы как считаете? Что бы вы лично выкинули? И если вас не затруднит, то почему?

У нас так: парное программирование только для сложных задач или быстрого обучения / введения нового члена команды. Test First — по желанию, юниты пишутся на бизнес-логику и библиотечный код, покрытие не считаем. Не всегда получается уложиться в сорок часов в неделю (хотя чаще всего по инициативе клиента). Чаще всего есть up front — design. Остальные практики все используем.

Причина простая: парное программирование = двукратное удорожание системы и двукратное увеличение сроков (ну примерно, не готов вести полемику на тему «одна голова хорошо, две — лучше).

Юнит-тесты — тоже зачастую слишком долго и дорого. Такой уровень качества просто не требуется для всей кодовой базы. Обучить манки-кликеров дешево и быстро. Кроме этого сильная система типов, отказ от исключений для control flow и прочие примочки ФП (в разумных пределах) имхо справляются с задачей уменьшения глупых багов лучше тестов (о баге сообщает компилятор, а не CI).

С up front дизайном история такая: собрать команду в которой каждый член — отличный проектировщик не только сложно и долго, но и дорого. Один проектировщик по нашим подсчетам может „обслуживать“ до пяти проектов. Это не отменяет необходимости в рефакторинге и принятии решений об архитектуре в процессе: все заранее не продумаешь и не учтешь. Однако, определенные ограничения спускаем сверху.

Спасибо за подробное описание, однако я ориентировался на исходный смысл латинского термина культура (возделывание, воспитание, развитие). И именно в его контексте интересно было бы понять почему вы отказались от той или иной практики.


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

Тогда в нашей «культуре» есть элементы «бережливого производства» и «здравого смысла»:). Использование всех практик XP просто потому что так сказали Кент Бек и другие умные дяди — это не круто. Использование конкретных в контексте, с четким пониманием зачем это делается и какие преимущества дает — круто. Как-то так.
Есть одна вещь, которая является чуть ли не основопологающей в куче гибких подходов — начиная от Кента Бека и заканчивая девопсингом. Это отсутствие боязни экспериментировать. Как показывает моя практика — если из десятка начинаний два начнут приносить пользу, это уже успех. Поскольку роблемы начинаются не в текущей работе, а в моменты кризисов. Зачастую не хватает одного-двух элементов в процессах, системе или привычках, чтобы разработка не прогнулась под давлением обстоятельств и не скатилась из эффективности в обычное говно.
Это не только в разработке так. Принцип работает и в бизнесе и в жизни в целом. Пример из бизнеса — Нокиа решила, что у нее все хорошо и весь хайп с айфонами — штука временная, а Андроид посреди разработки сменил роадмеп и запилил тач-скрин (андроид единственная живая мобильная ОС, разработка которой началась до IOS, остальные подохли, а новые писали с нуля уже в числе догоняющих). А Ицхак Адизес так вообще считает, что без постоянных изменений и «предпринимательского духа» любая компания обречена на старение и смерть.

Любые книги и тренинги по личному росту расскажут Вам о важности выйти «из зоны комфорта».

Я вообще заметил, что если какой-то принцип хорош работает в разных областях он по-любому правильный и нужный.
Правильные вещи пишете, я бы хотел работать в таком коллективе. Да я думаю, все бы хотели.
Добавьте это в требования к вакансии и интервьюируйте по ним работодателей: собеседование при трудоустройстве — это же двусторонний процесс :-)

Я пробовал добавить в резюме в качестве требований к вакансии комплект практик гибкой разработки. Полторы тысячи просмотров работодателями, около сотни собеседований, результат нулевой. Даже TDD в классическом смысле никто из пригласивших меня на интервью не практикует, не говоря уже о парном программировании. Я сделал вывод что компаний, практикующих XP, у нас нет.

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

Не вопрос, могу перефразировать свой вывод — компаний, практикующих XP значительно-значительно меньше, нежели компаний XP не практикующих.

Если ищете работу по принципу наличия или отсутствия TDD и других практик то долго будете искать. А как вам такой вариант: найти интересную для вас работу без TDD и прийти со своим уставом в чужой монастырь, то есть показать им на практике как это круто — TDD и парное прогрммирование? Сразу и работа интересная, и TDD будет.

У меня есть такой опыт. И он свидетельствует о том, что чтобы людей, годами программирующих традиционно, перевести на рельсы XP, требуются недюжинные усилия и вагон времени.


Люди — это устройства с несовместимыми интерфейсами, а парное программирование требует очень хороших коммуникационных навыков, которые как правило отсутствуют у программистов, годами работающих в одиночку.


К тому же ни один из известных мне руководителей или собственников софтверных компаний не готов нести затраты на внедрение практик, перспективных в долгосрочной перспективе, особенно на фоне того, что старый подход "каждый новый проект, как первый", приносит неплохой доход.

Sign up to leave a comment.

Articles