Хочу поднять эту тему для всеобщего обсуждения. Не уверен, что это в Управление Проектами, потому не обижусь, если модераторы переместят нить.
1. Что есть спагетти-код?
Под этим термином понимается локально переусложненный и локально непонятный код. Классические примеры — активное использование goto, особенно вверх по коду, а также функции огромного размера.
Сюда же относится и известная болячка в виде обработки большого количества частных случаев, причем на уровне отдельных строк кода и групп строк, также частных случаев, ломающих парадигму окружающего кода — например, добавление дополнительных условий вместо дополнительных значений переменной "состояние" к конечному автомату.
В некоторых случаях спагетти возникает от того, что к нему располагает окружающая среда платформы, как, например, в файловых системах Windows, где в один обработчик приходит и cached, и noncached, и noncached paging IO, которые существенно отличаются в мелочах.
Ниже эта болячка будет называться "спагетти в узком смысле слова".
2. Чем плох спагетти код?
Высокой сложностью его развития и поддержки, особенно при передаче от человека человеку, а также при возврате к тому же человеку через полгода его занятий чем-то другим (равносильно передаче другому).
Высокой вероятностью внесения крупных багов мелкими правками, связанной с возможным неучетом совершающим правку человеком частных случаев в "спагетти в узком смысле слова", а также перегруженности объема внимания человека синтаксисом языка, мультипарадигмальностью или же просто неряшливым написанием кода. Это есть следствие того, что правильное понимание такого кода требует значительных интеллектуальных усилий — зачастую больших, чем его создание.
Ситуация особо усугубляется тем, что частные случаи могли быть ранее внесены ради правки некоего бага — например, возникшего в граничных условиях. Слом таких случаев неаккуратными последующими правками может привести к повторному возникновению ранее исправленного бага.
3. Является ли спагетти абсолютным злом?
Нет.
Спагетти может быть почти идеально отлажен, реализовывать требования, в т.ч. по производительности.
4. Зависит ли вероятность появления спагетти от языка?
Слабо. Пожалуй, особого рассмотрения заслуживают только мультипарадигменные языки с крайне тяжелым для человека синтаксисом — Perl есть прекрасный пример. Смешение парадигм добавляет нагрузку на интеллект и объем внимания читателя кода, повышает возможность превышения некоего порога, после чего человек обязательно что-то упустит важное. Это верно не только для Perl-образного синтаксиса, но и для более легкого для восприятия, но тоже мультипарадигмального языка Си++ — смешением парадигм можно и на нем сделать спагетти.
"Спагетти в узком смысле слова" от языка не зависит.
5. Общий паттерн, наблюдаемый в спагетти.
Функция DoSomething выполняет действия, существенно зависящие от неких внешних условий. При этом отслеживание этих внешних условий делается в ней в виде if( условие ) две-три-четыре строки операторов. С некоей концептуальной точки зрения функция выполняет два или более различных действий, но все они равномерно перемешаны в ней самой, и не сразу понятно, что в ней к какому виду действия относится.
Такие вещи тяжело поддаются рефакторингу, ведь речь идет по сути либо о делении функции на несколько похожих, либо же о переработке всей архитектуры функции/объекта с его делением на несколько объектов, связанных полиморфными интефейсами.
6. Причины возникновения спагетти.
Злоупотребление мультипарадигмальностью обычно возникает от разудалого желания девелопера продемонстрировать свое владение сложными возможностями языка, или же от его же желания потренироваться в применении этих возможностей.
Со "спагетти в узком смысле слова" дело чуть другое. Как правило, такие вещи возникают — и накапливают негативные явления — в условиях быстрых правок кода под лозунгом "реализуем фичу самым минимальным возможным усилием, минимально трогая оттестированное". Мелкие правки и добавление частных случаев к имеющемуся коду действительно самые дешевые и по разработке, и по затрагиванию уже оттестированно. Но именно они накапливают "макароны".
7. Архитектор и спагетти.
Паттерн "1 умный архитектор — много глупых кодеров" предрасполагает к появлению спагетти. Да, крупноблочных архитектурных ошибок такой архитектор не сделает, но спагетти накапливается на уровне строк (редко десятков строк) кода. Витающий в облаках архитектор, оторванный от кода, этого и не заметит. Кроме того, в таких командах роль кодера принижена до уровня исполнителя распоряжений, а человеку в такой ситуации свойственно сделать побыстрее и получить бонусы, а не сделать по уму, задержав сроки, а то и затронув чужой код.
Помогает не столько архитектор, сколько лид-девелопер с функциями играющего тренера, а еще лучше — вся команда, составленная из толковых девелоперов без жесткого прессинга по срокам имплементации конкретных фич.
8. Неизбежно ли спагетти?
В какой-то мере, видимо, да. Его может быть больше или меньше, но, думаю, что какой-то процент такого кода есть в любом старом, долгоживущем проекте.
9. Проблемы, не имеющие отношения к спагетти.
Abstraction overhead. Спагетти возникает от слишком низкого уровня абстрагирования, оверхед же — от слишком высокого.
Abstraction inversion. Эта проблема мне кажется более серьезной, чем спагетти (именно она зачастую и имеется в виду в тех случаях, когда от кода хочется материться), и возникает от элементарного недоразвития одного из абстрактных слоев. При этом уровень абстрагирования и деления на слои может быть достаточным для качественного кода. Правильное решение проблемы — добавление функционала к бедному абстракному слою, а не обход этих недоделок с использованием побочных эффектов в абстракциях более высокого уровня.
10. Меры, снижающие вред от спагетти.
Массовое написание комментариев, при поддержании их адекватности коду. Лишними комментариями ничего не испортишь, а массовость повысит шансы на то, что наскоро добавленные частные случаи будут по крайней мере откомментированы, т.е. не будет загадочного кода. Это сильно снижает вред от спагетти.
Ослабление ротации девелоперов с подпроекта на подпроект, и ослабление текучести кадров. Чем реже передается владение кодом — тем меньше шанс, что спагетти превратится в реальный баг.
Административный отсев некоторых парадигм программирования, имеющихся в некоторых языках.
Уход от паттерна "1 Умник — много рабов" к паттерну, предполагающему больший интеллект рядовых исполнителей и большую приближенность лучших мозгов команды к непосредственно коду.
Увеличение вложений времени каждого исполнителя в продумывание перед кодированием. Траты времени на продумывание микроархитектуры незначительны по сравнению с тратами времени на поиск багов в спагетти и их правку (без внесения новых багов).
Использование полиморфизма и паттернов типа "страгетия" и "декоратор", вынесенных в отдельные объекты с четко определенными интерфейсами. Добавление частного случая в такой ситуации сводится к написанию еще одной имплементации того же интерфейса. Естественно, что, чтобы отследить исполнение вот этой идеи, нужен достаточно высокий уровень людей, работающих непосредственно с кодом.
В случаях, когда к спагетти предрасполагает окружающая среда, как в ядре Windows — имеет смысл от нее абстрагироваться прокладками, и в дальнейшем эту абстракцию не нарушать.
Роадмапы, т.е. перспективные планы требований к продукту. Чем лучше они проработаны — тем больше информации поступает "на вход" к архитектору или микроархитектору, и тем меньше шансов на то, что новое требование приведет к добавке точечного частного случая, что накапливает спагетти.
Отдельно стоит вопрос о дублировании кода. Если вместо 1 функции, делающей три разных, отличных в мелочах вещи, создается три функции, дублирующие друг друга на 90% — то это зачастую менее болезненно, чем спагетти. Например, функция ReadWrite с булевским параметром Direction имеет намного больший потенциал к развитию спагетти, чем 2 функции Read и Write.
Очень интересно, с некоторыми вещами можно поспорить:
MSS>10. Меры, снижающие вред от спагетти.
MSS>Массовое написание комментариев, при поддержании их адекватности коду. Лишними комментариями ничего не испортишь, а массовость повысит шансы на то, что наскоро добавленные частные случаи будут по крайней мере откомментированы, т.е. не будет загадочного кода. Это сильно снижает вред от спагетти.
В результате запутанный код становится еще длиннее, и его труднее читать. Плюс комментарии 100% будут оторваны от кода, при очередной правке в погонями за сроками комментарий забудут поправить. В результате от комментария будет только вред, он будет запутывать, а не помогать.
MSS>Ослабление ротации девелоперов с подпроекта на подпроект, и ослабление текучести кадров. Чем реже передается владение кодом — тем меньше шанс, что спагетти превратится в реальный баг.
И в результате получим, что очень слабый разработчик будет способен шантажировать компанию своим уходом. В конечном счете даже этот разработчик сам запутается в своем коде, эффекта от него будет минимум, все будет делаться медленно, а обходиться он будет как 10 сеньеров. И его придется держать, так как другие вообще ничерта в коде не будут разбираться. Даже за 5 лет до его уровня знания кода не подняться. Придется держать до тех пор, пока параллельная комманда не создаст аналогичную систему.
Я знаю только одно средство от спагетти — постепенное переписывание и поэтапный мелкий рефакторинг. При внесении изменений в такой код следование правилу чуть-чуть подчисть и сделай больше, чем необходимо для выполнении задачи. Изменения всегда в отдельный метод или класс, вместе с этим поправь соседнее форматирование, вынеси в константы те магические числа, которые выдны глазу и в которых уверен, напиши кучу TODO, переименуй метод, сделай для него осмысленное название, если название совсем не соответствует тому, что он делает, старое название оставь, пометь deprecated и оттуда вызови чть чуть поправленный метод. И далее маленькими шажками по чуть чуть выправляем. Вероятность внесения бага при выправлении ничуть не больше, чем при изменении чего-то, так что объективно риск не повышается. После кучи мелких безопасных относительно шагов, когда уже становится возможным разобраться в том, как работает метод, когда уже начинаеть более серьезный рефакторинг — разносить спагетти по разным классам, применять паттерны и т.д.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>4. Зависит ли вероятность появления спагетти от языка? MSS>6. Причины возникновения спагетти. MSS>8. Неизбежно ли спагетти?
Может зависеть от среды разработки. Например когда я писал на бесике под ZX Spectrum, там был дикий по нынешним меркам редактор кода Например нельзя было вырезать строку и встаивть её в другом месте, необходимо было перенабрать её заново, в итоге когда появлалась необходимость переставить строчки (куски кода) местами это было гораздо проще сделать при помощи пары goto Я думаю не надо объяснять на что был похож код?
ЗЫ. эх, вспомнил молодость...
---=== С наилучшими пожеланиями, Phoenics ===--- _
Здравствуйте, Phoenics, Вы писали:
P>Может зависеть от среды разработки. Например когда я писал на бесике под ZX Spectrum, там был дикий по нынешним меркам редактор кода Например нельзя было вырезать строку и встаивть её в другом месте, необходимо было перенабрать её заново, в итоге когда появлалась необходимость переставить строчки (куски кода) местами это было гораздо проще сделать при помощи пары goto Я думаю не надо объяснять на что был похож код?
Все там можно было, хоть на бейсике я там и не писал, слишком он неудобный был . Точнее писал всегда одно и тоже — загрузить и вызвать код по определенному адресу . На бейсике я на Радио 86РК было дело игрался, там поудобнее . Да и на том же спектруме вполне удобные бейсики были, например betabasic 3, там даже отступы расставлялись. Ассемблерный редактор в последних ассемблерах был там вполне приличный и удобный, практически как FAR. Стандартный редактор тоже возволял перенести строку (кажется команда rename, менялся номер строки и она переезжала в другое место), а затем можно было перенумеровать строки с любым интересующим шагом. Скопировать строку кажется тоже было можно, если мне не изменяет память, это команда Copy была.
MSS>>Массовое написание комментариев, при поддержании их адекватности коду. Лишними комментариями ничего не испортишь, а массовость повысит шансы на то, что наскоро добавленные частные случаи будут по крайней мере откомментированы, т.е. не будет загадочного кода. Это сильно снижает вред от спагетти. E>В результате запутанный код становится еще длиннее, и его труднее читать. Плюс комментарии 100% будут оторваны от кода, при очередной правке в погонями за сроками комментарий забудут поправить. В результате от комментария будет только вред, он будет запутывать, а не помогать.
Тут позвольте тоже поспорить... Комментарии надоделать, потому, как отсутствие комментариев это гораздо большее зло, чем их наличие. И не только с точки зрения лапши.
MSS>>Ослабление ротации девелоперов с подпроекта на подпроект, и ослабление текучести кадров. Чем реже передается владение кодом — тем меньше шанс, что спагетти превратится в реальный баг. E>И в результате получим, что очень слабый разработчик будет способен шантажировать компанию своим уходом. В конечном счете даже этот разработчик сам запутается в своем коде, эффекта от него будет минимум, все будет делаться медленно, а обходиться он будет как 10 сеньеров. И его придется держать, так как другие вообще ничерта в коде не будут разбираться. Даже за 5 лет до его уровня знания кода не подняться. Придется держать до тех пор, пока параллельная комманда не создаст аналогичную систему.
Здесь я соглашусь, потому как мне больше импонирует подход MS — когда люди находятся в постоянной ротациии и могут быстро подхватить чужие блоки.
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Тут позвольте тоже поспорить... Комментарии надоделать, потому, как отсутствие комментариев это гораздо большее зло, чем их наличие. И не только с точки зрения лапши.
Это уже неоднократно обсуждалось. Основной итог — комментарии делать надо, но комментировать надо метод (причем нетривиальный метод) целиком (или вообще класс), а не комментировать переменчивую реализацию. Ну и реализация должна читаться практически как текст, чтоб необходимости в комментариях не было. В реализации нужно комментировать неочевидные вещи (например, что такая реализация сделана из соображений производительности, и лучше не трогать ее, так как она когда-то была узким местом). На практике часто даже в комментариях к методам забывают переименовывать входные параметры при изменениях, забывают удалять лишние параметры и т.д после внесения изменений, даже эти комментарии оказываются часто рассогласованными даже при очертененной поддержке средой разработки (когда неправильный комментарий автоматом помечается как ошибка и при коммите среда начинает материться "а вы уверены, что хотите закоммитить код с предупреждением?"), поддерживать же комментарии в реализации (особенно кривой реализации) просто нереально.
Здравствуйте, elmal, Вы писали:
E>Ну и реализация должна читаться практически как текст, чтоб необходимости в комментариях не было.
Код читающийся как текст уже нельзя называть спагетти.
Я сейчас как раз разгребаю довольно старый спеагетти класс, cделаный давно ушедшим джуниором перед самым дедлайном и проработавший с небольшими изменениями пару лет. Вместо bool в одном случае использован string, в другом int (1/2). Большая часть логики построена на строках и их сравнении, иногда с подстрокой. Комментарии там были бы явно нелишними, но их нет.
В целом согласен с автором топика, спагетти код имеет право на жизнь в исключительных случаях, но когда-то его все равно придется переписать.
E>Это уже неоднократно обсуждалось. Основной итог — комментарии делать надо, но комментировать надо метод (причем нетривиальный метод) целиком (или вообще класс), а не комментировать переменчивую реализацию. Ну и реализация должна читаться практически как текст, чтоб необходимости в комментариях не было. В реализации нужно комментировать неочевидные вещи (например, что такая реализация сделана из соображений производительности, и лучше не трогать ее, так как она когда-то была узким местом). На практике часто даже в комментариях к методам забывают переименовывать входные параметры при изменениях, забывают удалять лишние параметры и т.д после внесения изменений, даже эти комментарии оказываются часто рассогласованными даже при очертененной поддержке средой разработки (когда неправильный комментарий автоматом помечается как ошибка и при коммите среда начинает материться "а вы уверены, что хотите закоммитить код с предупреждением?"), поддерживать же комментарии в реализации (особенно кривой реализации) просто нереально.
Хм... То, что кто-то что-то забывает — это проблемы управления. С таким-же успехом этот кто-то может забыть прокомментировать и саммари о классе/методе. ИМХО комментировать надо все, что вызывает затруднение при понимании. Будет это саммари по методу, либо несколько строк, описывающих конкретное решение программиста внутри метода (пусть даже это решение состоит из одной строки кода). Также саммари требует большего литературного таланта, да и время, затраченное на написания саммари будет малым.
N_C>>Здесь я соглашусь, потому как мне больше импонирует подход MS — когда люди находятся в постоянной ротациии и могут быстро подхватить чужие блоки. MSS>Ну там не совсем быстро, там около года.
Но есть-же. Проекты все одно обычно длиннее.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Хочу поднять эту тему для всеобщего обсуждения. Не уверен, что это в Управление Проектами, потому не обижусь, если модераторы переместят нить.
MSS>1. Что есть спагетти-код?.....
и что тут обсуждать? зачем?
вы на эту тему заметку в журнале хотите написать или лекции читаете студентам?
Если нет, то возникает ассоциация — электрик вместо то того чтобы чинить проводку, лежит на диване, плюет в потолок и рассуждает о безобразии клубка смотанных проводов.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Хочу поднять эту тему для всеобщего обсуждения. Не уверен, что это в Управление Проектами, потому не обижусь, если модераторы переместят нить.
Можно мне?
MSS>1. Что есть спагетти-код?
Сильносвязный код. Код, неизбежно образующийся в любой системе, которая имеет большой возраст, и поддерживается разными людьми. Например, он образуется при симптоматическом исправлении ошибок. Или, при вкручивании фичи в код человеком, который не имеет времени, желания, или возможности разобраться в коде, куда он вносит изменения. Или, при нецелесообразности рефакторинга при внесении такого изменения.
MSS>2. Чем плох спагетти код?
Высокой ценой внесения изменений. Причина в том, что этот код обладает высокой связностью, и при внесении изменений сложно предсказать все побочные эффекты отдельного изменения. Иногда употребляемый синоним — legacy code.
MSS>3. Является ли спагетти абсолютным злом?
Нет, если подсистема стабильна и туда не предполагается вносить крупных и/или частых изменений в будущем.
MSS>4. Зависит ли вероятность появления спагетти от языка?
Функциональные языки в меньшей степени страдают от этого, в силу отсутствия разделяемого мутабельного состояния и побочных эффектов — это самый сильный тип связности. По сути, "спагетти" полагается именно на побочный эффект и учитывает его, что и затрудняет его поддержку и изменения.
MSS>5. Общий паттерн, наблюдаемый в спагетти.
Приводимый пример показывает фрагмент сильно связной по состоянию системы с побочными эффектами.
MSS>6. Причины возникновения спагетти.
Бизнес-необходимость. А именно, необходимость того, чтобы бизнес-значимость результата оправдывалась затратами. Не будь business-value — не было бы и спагетти.
MSS>7. Архитектор и спагетти.
Иногда спагетти-подсистему целесообразно рефакторить, вот и все.
MSS>8. Неизбежно ли спагетти?
Разумеется.
MSS>9. Проблемы, не имеющие отношения к спагетти.
very long list
MSS>10. Меры, снижающие вред от спагетти.
Периодический "лоскутный" рефакторинг. Все перечисленные меры не фвляются эффективными.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>4. Зависит ли вероятность появления спагетти от языка?
MSS>Слабо. Пожалуй, особого рассмотрения заслуживают только мультипарадигменные языки с крайне тяжелым для человека синтаксисом — Perl есть прекрасный пример. Смешение парадигм добавляет нагрузку на интеллект и объем внимания читателя кода, повышает возможность превышения некоего порога, после чего человек обязательно что-то упустит важное. Это верно не только для Perl-образного синтаксиса, но и для более легкого для восприятия, но тоже мультипарадигмального языка Си++ — смешением парадигм можно и на нем сделать спагетти.
Нет, либо я нелюдь. (: Не существует "Perl-образного синтаксиса", есть люди, не придерживающиеся определённых правил оформления кода. Да, Perl позволяет сделать код нечитаемым для неподготовленного человека, но это не значит, что он навязывает подобный стиль.