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

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

Обычно боятся править те, кто не представляет что такое тестирование и как его принято организовывать в 21м веке. Если код покрыт тестами, правки не вызывают страха.
Если код запущен и весь в заплатках, то да, без рефакторинга не обойдешься, но рефакторинг не обязательно должен быть кардинальным «здесь, сейчас, всё или ничего». Вполне можно ограничиться рефакторингом сбоящего компонента. Например если сбои чаще в уровне общения с базой, вовсе необязательно рефакторить логгирование и получение данных от юзера. Потом, когда до них дойдут руки, или там возникнут проблемы, то отрефакторить и их.
Если код покрыт тестами, правки не вызывают страха.

Тесты совершенно не являются «серебрянной пулей» против непредвиденных проблем.
Не вижу возражения, хотя по тону прозвучало именно как возражение. Вы что хотели сказать? Что тесты не нужно делать? Или рефакторинг не проводить? Или что нужно продолжать бояться вносить правки?
Я говорю, что ваш тезис про тесты вообще не имеет отношения к тезисам статьи. Статья о том, как что-то «непредвиденно ломается», а может быть еще и в самый неподходящий момент, из-за чего в разработке появляется страх правок, особенно крупных.

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

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

это что-то не было достаточно покрыто тестами

Вы, наверное, должны понимать, что полное покрытие тестами — это точно такая же принципиально недостижимая вещь, как и код без багов.
Статья может много чего не иметь, но тезис статьи о страхе перед правками. И лечение этого страха — тесты. Если вы не понимаете связь — то я могу сделать вывод только что у вас нет опыта. Ничего, — это то, что со временем может пройти. А пока — можете принять на веру моё утверждение, как человека имеющего 30 лет опыта разработки и дизайна в т.ч. крупных проектов, как в России, так и в Штатах последние 18 лет, что тесты как раз резко снижают вероятность, да, не исключают, но резко снижают вероятность непредвиденных ситуаций после правок кода. Это происходит прежде всего потому, что вы видите, что ломаются именно стандартные ситуации — покрытые тестами и вы не пустите такую правку в production, а будете разбираться в чем дело и кто неправ — тесты или правки. Если сломалось то, что не покрыто тестами, а такое тоже бывает, то вы улучшаете создаете покрытие — в т.ч. и проактивно, анализируя отчеты о code coverage, _до_ возникновения проблем. Но если у вас нет тестов, то вероятность обнаружения проблем резко ниже, или вообще равна нулю. Всё просто.
Это конечно прекрасно, что у вас такой обширный опыт, но почему-то вы так и не хотите понять, о чём вам пишут.

Тесты и непредвиденные поломки (и следствия из них) — это две разные, никак не пересекающиеся друг с другом вещи. Я наблюдал и нежелание править нетестируемый код, и точно такое же нежелание ничего особо не трогать при наличии огромной охапки тестов за плечами; и все промежуточные этапы. И то, как до апологетов TDD и прочего тестирования постепенно доходит, что тесты не гарантируют, что у них ничего не поломается (а как максимум не поломается только то, что уже когда-то ломалось или то, где поломки предусмотрели). И в конечном счете стремление решать проблемы через костыли (зато мелкие), чтоб не случилось бы чего страшного непредвиденного из-за больших правок — оно всё опять на месте, тесты or no.
Начинается тут про «30 лет опыта» и т.д. Молодой человек, Вы на Хабре, тут каждый первый такой, никого Вы не удивите.

Почему же Вы не читаете то, что Вам пишут в комментариях? Тесты — это хорошо, но они не помогут от описанных в статье проблем. Условно говоря, вы можете разработать Отвёртку и сделать для неё прекрасные тесты на то, что она откручивает шурупы. И это НИКАК не защитит вас от случаев, когда вашей отверткой решат забивать гвозди, гнуть трубы или засовывать себе в ухо. И вы никогда не предугадаете и не напишете тесты на все эти случаи заранее.

Обычно боятся править те, кто не представляет что такое тестирование и как его принято организовывать в 21м веке. Если код покрыт тестами, правки не вызывают страха.

Это в теории. А на практике просто к исправлению 1000 строк кода добавляется исправление 2000 строк тестов. Что только снижает желание что-то исправлять.

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

Онтопик: Из моего опыта, вероятность наблюдения предложенному нашему вниманию феномена растет со временем; поэтому, одним из способов избежать петли видется дробление проектов на более мелкие ( без фанатизма, конечно ).
Вот самый последний пример. По ряду причин нужно было перенести кусок кода с серверной части сервиса на клиентскую часть. Код, десятки тысяч строк, чрезвычайно сильно переплетен с кодом, который не должен переноситься. Ситуация усложнялась еще и тем, что сервис очень сильно нагруженный — порядка миллиарда запросов в час в гочее время и до миллиарда запросов в сутки в более свободное. Параллельно работает сотня разнообразных клиентских приложений, которые используют действующий API и таким образом вывести в продакшн новый API невозможно без параллельной поддержки старого. И К тому же весь код на С — старый, разработчики начинали его делать в 92м году и в нынешнем виде он существовал примерно с 2003 года. Сотрудники, которые его писали или уже стали боссами или ушли. Документации никакой.
Т.е. как видите, по современным стандартам грамотно организованной разработки не было.

Что мы сделали:
— до моего присоединения к проекту, уже были написаны интеграционные тесты, которые использовали «эталонное» клиентское приложение, довольно популярное среди сотни клиентских приложений, которые работают с сервисом.
— мы выбрали второе по популярности и добавили его тоже в тестирование. Таким образом наши тесты охватили примерно 50% всех клиентских приложений по количеству запросов.
— далее я вычленил код из серверной части сервиса и оформил его в виде библиотеки, без зависимостей от серверного кода. Эту библиотеку могли использовать как клиентские приложения, так и сам сервис. Это трудоемкий и долгий рефакторинг. Как только собранный сервис, с этой библиотекой, прошел тесты, мы выпустили его в продакшн на 1 инстансе из балансируемых 10. Убедились что всё идет как по маслу и сбоев нет. Увеличили охват. Тут обнаружили что некий старый процесс, идущий в фоне по таймеру, работает некорректно. Он один использовал некий функционал, который уже был объявлен deprecated и подлежал переписыванию в любом случае, поэтому ему подняли отдельный выделенный instance со старым сервисным кодом. Эта связка будет продолжать так работать, пока мы не перепишем его.
— тем временем я переписал эталонные приложения с использованием новой библиотеки. Таким образом получился первый клиент, использующий новый API. Мы запустили все тесты как со старым приложением, так и с новым. Т.о. у нас удвоилось количество тестов. Как только тесты прошли, приложение было установлено для обслуживания 1 pipeline. После положительного отклика от группы, кто работает с этим pipeline мы распространили приложение и на другие pipelines.
— далее для серверной стороны был разработан вспомогательный процесс, который поддерживал и обслуживал старый API и транслировал его в новый API. Т.о. сервис полностью избавился от старого кода. Теперь мы уже запускали 4 набора конфигураций интеграционных тестов — новый клиент с новым сервисом, новый клиент со старым сервисом (как в продашене), старый клиент с новым сервисом и старый клиент со старым сервисом. Как только все тесты показали добро, новый сервис с вспомогательным процессом для поддержки старого API вышел в продакшн. И новый клиент полностью и окончательно был заменен на всех pipelines в продакшене.
— тесты сокращены до 1 варианта конфигурации — новый клиент с новым сервисом
— теперь осталось переписать все 100 клиентских приложений — с использованием библиотеки и затем выбросить вспомогательный процесс на сервере.

Итог:
— тесты не переписывались. Они были лишь доработаны, чтобы можно было гибко конфигурировать что собирать и тестировать — разные варианты версий сервера и клиента
— в любой фазе процесса мы были готовы откатить к предыдущему состоянию, на любое количество шагов назад
— для перехода на следующий шанг у нас было «добро» от системы тестирования
— переход на следующую версию осуществлялся плавно, с охватом порядка 10% в начале, и с доведением процента до 100.
— зафиксировано лишь 2 сбоя, не оказавших существенного влияния. Первый — отказ в работе старого (deprecated) приложения, которое и так подлежало переписыванию. Второй — чуть серьезнее — в эталонном приложении обнаружился функционал, не покрытый тестами, который на первый взгляд выглядел как отладочный, поэтому и не был перенесен в новый код. Инцидент был пофикшен довольно просто — тесты расширены, функционал был перенесен, а на время переноса была предоставлена старая версия.

Совет — стройте интеграционные тесты. Ведите хорошее ясное логгирование всех внутренних операций как на клиенте, так и на свервере. Логгирование должно быть централизованным и позволять обнаруживать сбои в онлайн. Выводите в продакшн плавно. Плюс — не забывайте про систему мониторинга, которая следит за используемыми ресурсами (нагрузка CPU, дисков, памяти и пр) плюс интенсивность работы сервиса — как каждой ноды кластера, так и аггрегированный кластер.

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

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

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

Не вижу противоречия. Code coverage не привязан исключительно к unit tests.
В тестировании middle layer, да и не только, покрытие можно, и я уверен — нужно, изучать. В самом деле — вы же запускаете клиентский код с разными вариантами входных данных. Как понять, что этих вариантов достаточно, и не пропущено что-то существенное? А вот так — по code coverage. Вы видите куда пошло ветвление и куда не пошло. И аналогично на сервере — не лишне проверить, какие варианты реакции и сколько из них попали в тестирование, или другими словами, покрыты тестами.
Как понять, что этих вариантов достаточно, и не пропущено что-то существенное?

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


вы же запускаете клиентский код с разными вариантами входных данных. Как понять, что этих вариантов достаточно, и не пропущено что-то существенное? А вот так — по code coverage.

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

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

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

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

«Петля страха» — отличное название. Возьму на вооружение)

А как именно "доказали, что умеете в рефакторинг"?

Детальные code review в режиме презентации, юнит и интеграционные тесты.
А вот юный друг JustDont выше утверждает, что тесты никак не помогут )))
Нам повезло) в легаси аде были «интеграционные» (как бы) тесты, которые позволяли убедиться по крайней мере, что усе работает как и до рефакторинга. Но без нормальных тестов не обошлось.
Было нечто похожее. В итоге завершилось рефакторингом всего кода проекта.
Люди боятся того, чего не понимают. Если они не понимают, что делает код приложения они боятся вносить в него изменения. Именно «что делает код», а не «как работает эта языковая конструкция». Конструкцию понимает большинство, а вот как ее изменение повлияет на работу всей системы — в состоянии понять не многие.

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

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

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

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

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

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

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

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


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

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


Нет, мы покрываем тестами то, что необходимо в процессе переработки.

Речь не о unit tests, поэтому не совсем так.
Вы покрываете use-cases, т.е. варианты использования софта. В новой версии все use cases остаются прежними.

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

ведь тестирвание нужно делать в любом случае

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

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

Все возможные — невозможно, во всяком случае за разумное время. Тут требуется искуство. Расскажу как мы это делаем. Во первых у нас централизованное логгирование. Т.е. мы знаем какие функции когда и кем вызваны. Во вторых — у нас есть аггрегаторы логов, которые позволяют сделать, ну скажем group by, по логам. Сразу видно какие функции наиболее часто используются. Их мы тестируем подробнее в первую очередь. В логгировании есть параметры вызова. Эти параметры тоже аггрегируются и в первую очередь тестируем более частые варианты.
Разумеется, это не дает покрытие всех возможныйх use-cases, но 90% вызовов попадают в 10% функций и из в этих функциях 90% вызовов попадают с 10% вариантов параметров. Т.е. вся эта беконечность use-cases довольно хорошо коллапсирует на практике. Вот по этой статистике мы и формируем тестирование.
Это не идеальное решение, всегда есть риск что-то проспустить. Поэтому, кроме тестирования, мы делаем частые релизы. Мы всегда готовы быстро откатить новую версию назад в случае инцидента. После чего вариант создавший инцидент — обязательно попадает в тесты. Да — инциденты неприятны, но по мере роста объма тестов частота инцидентов становится почти ноль.
И задумайтесь на секунду, откуда страх правок если всё хорошо тестируется? Ну сломали что-то, тесты покажут что сломано. Чего бояться? Продакшн не пострадает.

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

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

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

Вы уж решите для себя — нужно тестировать или не нужно. А меня смешить не нужно. Мне смеха хватает )))
«Тесты не являются серебряной пулей» — это утверждение о бесполезности тестов.

Отнюдь.
Писать тесты для кода устройство которого мало понятно, вероятно приведет к тому, что тесты будут написаны на те части кода, которые вы понимаете. А труднодоступные для понимания, неочевидные, глубоко лежащие вещи так и останутся вне поля видимости. Это чем-то сродни «систематической ошибке выжившего».

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

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

Сделать так, чтобы разработчикам было удобно его писать

это отдельная задача, не связанная с тестированием напрямую. Никакие даже самые идеальные условия не сведут ошибки к нулю. Но хорошие практики, например стандарты кодирования от гугл, с описанием что и как писать, вполне ценны, ибо защищают от многих грабель.
Так или иначе — если вы собираетесь что-то переделывать, пусть даже по идеальным стандартам и в окружении идеальных архитекторов и менеджеров, но у вас не покрыты все самые частые use-cases, ваши изменения скорее всего приведут к ошибкам и проблемам. Мы люди, мы делаем ошибки.
Это, кстати, очень важная штука, о которой повсеместно забывают.
И тут речь не только о работе архитектора, но и о работе PM-а/TL-а, т.к. разработчик, который пишет код, в достаточно большом проекте единовременно может или охватить головой реализацию конкретной функции, или охватить её смысл в рамках проекта(а иногда в принципе этого не может, т.к. не обладает необходимыми познаниями в части технологического стэка). Точно также, как PM/TL/архитектор не может охватить реализацию конкретной функции.
В своей работе сейчас сталкиваюсь с этим.
В идеале, должно получаться разделение труда: Архитектор/PM/TL продумывает разделение решения на модули, каждый из которых решает конкретную задачу. На каждый из этих модулей составляется ТЗ/req.list, после чего разработчик уже не думает о том как этот модуль будет взаимодействовать с остальной системой — он думает о том, как наиболее эффективно и красиво реализовать функционал.

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

if feature_enabled then
do_new_action;
end if
Фичи как правило слишком сильно переплетены. Отключение одной может привести к нежелательным накладкам. Поэтому выключение фич — это рискованно, особенно если все варианты перебора включения-выключения не протестированы. К тому же вам нужно держать и новый и старый код, т.е. ваш if совсем не только c then, но и с else. И это довольно сильно загромождает код. Т.е. да, пробовали так, но не удобно.
Насколько я вижу, в крупных компаниях практика другая. Я опишу с упрощениями, чтобы не загромождать идею слишком большим количетсвом подробностей. Переключают версии. Т.е. kill-switch, о котором вы говорите — да, он есть, он нужен, но реализуют его централизованно и над приложениями, а не внутри них. Если это онлайн сервис, то новую версию вводят постепенно. Это означает, что существует механизм, который делает новую версию доступной только для небольшой части клиентов — например по их IP. Мониторят сбои — как автоматически, например по логам, так и через каналы обратной связи. Если в течение достаточного промежутка времени, а он известен из практики, сбоев нет, то увеличивают процент охвата. И так шаг за шагом вплодь до 100%. Но если есть проблемы — убирают версию, т.е. сокращают ее присутствие до нуля и информируют девелоперов и тестировщиков (девелоперов, пишущих тесты). Это автоматический процесс. Вмешательство человека, если и есть, то минимально. Далее пишут тесты на выявленные недостатки, и ждут девелопмент. Затем выкатывают новую верстю после того как она проходит все тесты, включая новые. Это цикл релизов, если простите мне упрощения, known as CI/CD (continuous integration, continuous delivery). Рекомендую. Почитайте.
Это тоже вариант. Все зависит от условий. Обычно на новые фичи мало чего подвязано, поэтому их включать\отключать можно безболезненно, особенно, если это просто улучшение, без которого можно работать по-старому. В моем, например, случае откатывать весь проект нельзя. В релиз идут сотни правок и проще отключать каждую правку по отдельности.
Делайте чаще релизы.
Тогда в каждый релиз будет попадть меньшее количество правок и а) будет меньше вероятность необходимости отката назад и б) сам откат, в случае необходимости, будет более безболезненный
CI/CD — это всё вокруг частых релизов. Можно и без полного CI/CD всё сделать. Просто автоматизируйте как deployment новой версии, и так откат на старую. Частые релизы станут менее трудозатратными.
Почему-то все 3 способа, приведённые автором в переломном моменте, подразумевают несчастный финал, особенно если пройти по ссылкам далее. Вывод тоже неутешительный.
У нас в компании с одним веб-проектом была похожая ситуация, когда код, полученный от аутсорсеров после сдачи бизнесу фактически нельзя было ни развивать ни нормально править на месте.
Первая реакция местных программистов была именно такая — «выкинуть и всё переписать с нуля», но объём проекта и сроки, данные на исправление от такого шага оградили. Наверное произошло то, что кто-то назвал «масштабным рефакторингом», хотя в нашем случае мы просто «выкинули всё ненужное и непонятное». Сократили в результате код на 25% одновременно улучшив лог и управление исключениями. Всё, что касается бэкенда получилось на мой взгляд неплохо — тесты уже прошли, релиз через 2 дня. Вот с фронтендом ситуация намного хуже. Боюсь, что там придётся таки идти на шаг «переписать всё с нуля» — уж больно динамичен характер мира фреймворков для веба, особенно когда стандартные компоненты типа Vue, VueMaterial смачно сдабриваются сторонними и кастомными компонентами.
На мой опыт, например, счастливых финалов тут нет. В смысле, довести ситуацию до такого состояния, когда отныне и далее всё прекрасно, хорошо, и код правится как надо — невозможно. Вместо этого приходится бороться с этим постоянно и всю дорогу, и с каждым новым релизом ситуация, как правило, вновь обостряется.

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

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

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


может быть только один — менять руководителя разработки, так как это проблема не логическая а психо-логическая
У сотрудников, которые уже давно работают и привыкли работать по-старинке — да, довольно часто распространен синдром страха перед кодом. Но это из-за предыдущих ошибок, которые привели к проблемам в пролдакшене, с кризисами, с прибегающими менеджерами с выпученными глазами (но без слюней и без криков — у нас достаточно просто выпученных глаз), с массивными перелопачиваниями в базах — и всё после работы некорректной новой версии. По-моему все на такое наступают, если нет правильного процесса разработки. Из новичков — тоже вполне бывает что-то разламывают, но скорее по неопытности. Из курьезов — не поверите — но даже в 2001 я влился в один коллектив, где я так и не смог никого убедить что нужен source control. Не говоря о тестах. Правили у себя локально и сразу ручками отправляли в продакшн. Я не смог там задержаться дольше полутора лет.
Но махать шашкой и увольнять — я как-то не вижу такой практики. Организовать процесс — можно или отправив главных разработчиков на тренинги или наняв консультантов. Чаще вижу второе. Того кто не знает как пользовать гитом — научат пользоваться гитом. Того кто не знает как писать интеграционные тесты — научат писать интеграционные тесты. Того кто не знает что такое скрам и стендапы — научат и этому. Для менеджмента отдельные тренинги. Для разработчиков — уже с оттренированными менеджерами. Это работает хорошо, если конечно консультанты хорошие. Хотя тоже не без курьезов ))) Но они смешные.
Понятно, давайте отправим разработчиков на тренинги, наймем консультантов, чтобы проконсультировали какие тренинги правильные, какие нет. Если проконсультируют плохо, отправим на тренинги консультантов или наймем суперконсультантов для обучения консультантов…

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

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

Про ваши глаза — ну это сильно зависит от объема кода. Если, допустим, пара миллионов строк _кода_, не html+css, а именно кода. Что вы будете охватывать глазами?
Про зрелость-незрелость и ваше отторжение новых подходов, ну знаете, это говорит о том что вы сами не готовы к переменам.


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

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


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

Откзаться от предыдущих шагов и рассказать стейковнерам о возникших трудностях до того как эти трудности съедят всё отпущенное время и приведут к задержкам в релизе.


Ну конечно, без скрама я этого сделать не могу. Видимо как в фильме Матрица на допросе Нео: «Зачем вам телефон, мистер Андерсон, ведь сейчас вы немы.»

Если, допустим, пара миллионов строк _кода_, не html+css, а именно кода. Что вы будете охватывать глазами?


Расстояние до двери…
Расстояние до двери…

Может тогда вам не стоило так настаивать на своём, если вы не работали с такими объемами?

Ну конечно, без скрама я этого сделать не могу.

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

кажутся лично мне малоподходящими

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

Может тогда вам не стоило так настаивать на своём, если вы не работали с такими объемами?


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

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


Спасибо, кэп, я поработал, и из опыта этой работы все вышеизложенное и пишу.
из опыта этой работы все вышеизложенное и пишу.

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

Но ни о каких 2 миллионах строк кода в статье речи не идет,


Потребность в озвученных технологиях не возникает, если вы пишете 5 строк кода и ваш код не взаимодействует с другими модулями, также находящимися в разработке.
2млн — это просто пример, когда без подобных технологий коммандам разработчиков невозможно эффективно двигаться вперед. Я наблюдал, когда без должной организации тестирования, без взаимодействия внутри команды (стендапы), без краткосрочного планирования (скрамов) движения вперед не происходит, команды начинают буксовать на разгребании проблем и погружаются всё глубже, создавая новые. Разумеется, 2млн — это совсе не пороговая цифра. Эффеты наблюдаются и при меньших объмах кода. Просто у меня под руками есть именно такой проект и мне легче оперировать практикой из него.
Хочется замолвить слово за методологии, изолирующих изменения от остального кода (SOA и ООП). Просто писать код так, чтобы взаимодействие происходило между «чёрными ящиками», не подозревающими о том, как выглядят данные внутри других «чёрных ящиков».
Все «чёрные ящики», чтобы прочитать, записать или модифицировать данные, обращаются не прямо в БД, а к одному и тому же другому «чёрному ящику».

Видимо, все давно уже умеют это отлично использовать и всё это само собой разумеется, а я попал на роль Капитана Очевидность.

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

При codereview нужно указывать джуниорам на моменты, когда они упускают возможность сделать сервис, а пишут перегруженную фабрику (богообъект), ворочающую всем вокруг себя. И по возможности (помня о сроках) рефакторить такой код.
Трибунал — не худшая реакция на неудачу, а превентивная борьба с конкретным риском, который не должен повториться в будущем. Другой вопрос, что трибунал должен быть не сиюминутным «расстрелом», а наказанием возможностью исправиться усиленным трудом и повышенными результатами за некоторое выделенное время. Выполнил — очистился от «позора». Не выполнил и реализовал ещё один (два… три...) подобный риск — тогда соответствующие меры: вплоть до увольнения, даже со статьёй и даже с конфискацией через суд (в зависимости от размера ответственности и ущерба, а также прав таким образом поступить с сотрудником). Но, никак не сразу «расстрел». Это только сильнее затянет петлю.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории