Через пару лет я вернулся к этой книге и наконец с ней ознакомился. К тому моменту у меня уже было несколько лет работы в IT-индустрии. И когда я начал читать, то удивился, насколько книга, написанная в 1975, да ещё и в сфере разработки ПО, по-прежнему актуальна!
В этом году наконец вышло очередное переиздание, поэтому я решил приобрести её в бумажном варианте и перечитать ещё раз. И вместе с вами обсудить некоторые цитаты, которые актуальны до сих пор.
Пара слов об авторе книги
Фредерик Брукс был менеджером проекта по созданию IBM OS/360 (группа операционных систем, разработанных IBM для мейнфреймов System/360). Для ускорения разработки он предпринял попытку привлечь ещё больше разработчиков, но решение это оказалось фатальным. Чтобы никто более не наступал на те же грабли, Брукс впоследствии написал серию очерков, которую мы теперь знаем как «Мифический человеко-месяц». Считается, что каждому менеджеру проекта следует ознакомиться с этой книгой.
Высокая кухня требует времени
Именно такой эпиграф предваряет вторую главу книги, а начинается она со слов:
Все программисты — оптимисты.
Вспомните, сколько раз вы обещали команде, что сделаете эту задачу через пару часов, а в итоге пришлось провозиться весь день? А ведь изначальная оценка трудозатрат, скорее всего, делалась именно из того расчёта, что данная задача займёт, ну, максимум полдня. Чем больший срок мы пытаемся прогнозировать, тем большую погрешность вносят подобные недооценённые задачи. Конечно, опытный продакт всегда в уме умножает на число Пи, но далеко не каждый опытный продакт может сдвигать сроки дедлайнов своего проекта в масштабах всей компании.
Справедливости ради следует заметить, что часто бывает и обратное: мы перезакладываемся на какую-то задачу, а в итоге делаем её заметно быстрее.
Раньше труд разработчиков оценивался в человеко-месяцах. Брукс говорит, что эта единица измерения весьма опасна. Из неё следует, что люди и месяцы взаимозаменяемы. Это верно для сбора хлопка или рытья траншей, но даже приблизительно неверно для разработки ПО. Прежде всего, из-за того, что распределение работы невозможно из-за ограничений в последовательности выполнения определённых участков. Как говорится, девять женщин не могут родить ребёнка за один месяц.
К счастью, в наше время с приходом гибких методологий мы уже ушли от оценки задач с привязкой к реальному времени. Мы оцениваем лишь относительную трудоёмкость задач.
Брукс проводит наглядную аналогию между приготовлением еды и разработкой ПО:
Мило, когда вам обещают приготовить омлет за две минуты. Но когда две минуты прошли, а омлет ещё не готов, у клиента остаётся два варианта — продолжать ждать либо съесть его сырым. Повар может предложить ещё один вариант — добавить огня. В результате омлет уже ничем не спасёшь: подгорит с одной стороны и будет сырым с другой.Также он приводит эмпирическое правило, что написание кода в нашей работе занимает лишь ? от всего времени, которое мы тратим на задачу. Совпадает ли его оценка с вашей?
Часто ошибки планирования заключаются в том, что в график заранее не закладывается время на некоторые участки работ, идущие после самой разработки. Например, на тестирование. При этом на горизонте уже появляется дедлайн, и становится понятно, что работы в срок завершены не будут.
Первая мысль, которая приходит в голову при угрозе срыва сроков, — добавить в команду новых людей. Но лучше сразу отказаться от этой идеи. Ибо тогда команда будет тратить ресурсы на введение в курс дела новых коллег, вместо того, чтобы продолжать работу над проектом.
Поскольку Брукс сам погорел на этом при разработке IBM OS/360, он сформулировал такой закон:
Если проект не укладывается в сроки, то добавление рабочей силы задержит его ещё больше.
Хирургическая бригада
Третью главу Брукс начинает со следующего утверждения:
Очень хорошие профессиональные программисты в 10 раз продуктивнее слабых, при наличии того же уровня обучения и двухлетнего опыта.Продолжая эту мысль, он говорит, что лучше всего небольшая высококлассная команда (не более 10 человек) — чем меньше умов, тем лучше. На ваш взгляд, каков максимально допустимый размер команды?
Есть и обратная сторона такого решения: по-настоящему большие системы такая команда будет создавать очень долго. Как же выйти из положения? Да очень просто: пусть в каждой команде будет один высококлассный специалист («хирург») и несколько человек у него на подхвате, которые оказывают ему всяческое содействие.
Брукс даже приводит примерный состав команды, расписывая роль каждого её участника.
Итак, какой же состав dreamteam по Бруксу:
Хирург — главный программист. Он лично задаёт спецификации, разрабатывает дизайн программы, реализует её и пишет документацию. Имеет большой талант, десятилетний опыт и значительные прикладные знания. В большинстве современных команд это тимлид или архитектор. | |
Второй пилот — альтер эго хирурга, чуть менее опытный, но способен выполнять любой участок работы. Досконально знает весь код. Хирург обсуждает с ним свои идеи, но он не связан по рукам его советами. По описанию очень напоминает старшего программиста. | |
Администратор — собственно, человек, который разгружает хирурга в части решения различных оргвопросов, которых в крупных компаниях бывает немало. | |
Редактор — пишет документацию, беря за основу черновик, написанный хирургом. То есть правильно оформляет её, снабжает ссылками. Получается, это технический писатель. Брукс настаивает на том, что черновик документации должен писать именно тот, кто пишет код, то есть — хирург. | |
Секретарь — обрабатывает переписку, связанную с проектом, а также документы, не относящиеся к продукту. К слову, в нашей команде необходимость найма секретаря уже стала предметом многочисленных шуток. | |
Инструментальщик — создаёт различные специализированные утилиты и скрипты. Очень похоже на роль девопса. |
|
Тестировщик — пишет тест-кейсы и тестирует код приложения. Также занимается созданием вспомогательных средств, необходимых для тестирования компонентов. |
|
Языковой консультант — разбирается в тонкостях используемого языка и умеет находить эффективные решения каких-нибудь каверзных задач. Часто проводит небольшие исследования в течение двух-трёх дней. Один такой консультант может работать сразу с несколькими командами. На мой взгляд, это некий разработчик единой платформы в рамках всей компании, которая позволяет командам сосредоточиться на написании самой бизнес-логики, специфичной для их проекта. |
Организация по типу хирургических бригад с главным программистом позволяет достичь целостности продукта благодаря его проектированию в нескольких головах и общей продуктивности благодаря наличию многочисленных помощников при радикально сокращенном обмене информацией.
Эффект второй системы
Наверняка многим из вас приходилось работать с такими коллегами (а то и самим бывать в такой ситуации), когда уже более-менее опытный разработчик пытается предусмотреть наперёд все варианты дальнейшего развития системы и заранее перезаложиться всевозможными абстракциями, усложняя читаемость кода и повышая его сложность? Брукс называет это «эффектом второй системы».
Вторая система — самая опасная для человека, который её проектирует. Когда он трудится над своей третьей и более поздними, все экземпляры из его предшествующего опыта будут подтверждать друг друга относительно общих характеристик таких систем, и их различия будут определять те части его опыта, которые являются частными и необобщаемыми.
Общая тенденция — делать дизайн второй системы с большим запасом, используя все идеи и излишества, которые были осторожно отклонены в первой.
Тут сложно что-либо добавить или опровергнуть. Я с автором полностью согласен. Решением данной проблемы является, на мой взгляд, принцип KISS (“keep it simple, stupid”). Потому что в современных реалиях любые попытки угадать дальнейшее развитие системы являются делом неблагодарным.
Пилотная установка
В этом мире нет ничего постоянного, кроме непостоянства.
В качестве иллюстрации этой идеи Брукс приводит пример инженеров-химиков. Они давно уже поняли, что процесс, который успешно осуществляется в лаборатории, нельзя повторить в промышленных масштабах. Промежуточный этап, именуемый пилотной установкой, необходим для того, чтобы получить опыт в вертикальном масштабировании системы.
Планируйте выбросить первую версию — вы всё равно это сделаете.
Как вы уже догадались, речь идёт об MVP (“minimum viable product”). Это некий прототип, позволяющий проверить ту или иную гипотезу бизнеса. Продолжая предыдущую тему, на этапе MVP следует выключить внутреннего перфекциониста. А уж если результат эксперимента окажется положительным, тогда получится быстрее с нуля написать вторую версию «как положено».
Два шага вперёд и шаг назад
Брукс приводит интересное наблюдение, что постепенно любая система склонна накапливать ошибки, которые появляются в ней с каждым новым релизом. Число новых модулей системы линейно увеличивается вместе с номером релиза. При этом число затронутых модулей растёт экспоненциально.
Все исправления тяготеют к разрушению структуры, увеличению энтропии и дезорганизации системы.
Я полностью согласен с этим тезисом. Особенно это актуально, на мой взгляд, для микросервисной архитектуры.
Со временем вносить изменения в систему становится всё сложнее, а исправления не дают заметных результатов. В таком случае становится необходимым переписать систему с нуля. Ключевой момент в том, что к этому нужно быть готовым.
И грянул гром
Ещё Софокл говорил, что никто не любит гонца, приносящего дурные вести. И чтобы в своё время никто не оказался на месте такого гонца, следует строго придерживаться заранее оговорённых сроков. Но как проконтролировать, что крупный проект укладывается в эти сроки? Прежде всего, иметь такой график.
В этом графике каждое событие из списка событий, именуемых контрольными точками, имеет дату. Контрольные точки должны быть конкретными, измеримыми событиями. Человек редко будет лгать о прогрессе контрольной точки, если контрольная точка настолько чёткая, что он не сможет обмануть сам себя. А вот если веха расплывчата, начальник (или продакт) часто воспринимает доклад иначе, чем тот, кто ему докладывает. В таком случае ситуация (без злого умысла, подсознательно) преподносится в более оптимистичном ключе.
Часто ли вы сталкиваетесь с ситуацией, когда задача «почти сделана»? Задача должна была быть сделана сегодня, но разработчик говорит, что она будет сделана, скорее всего, завтра. На первый взгляд, однодневное отставание от графика не выглядит слишком катастрофичным. Однако Брукс утверждает, что каждое такое отставание является элементом катастрофы, ибо они имеют свойство накапливаться. Серия таких однодневных отставаний снижает общую динамику всей команды.
Критичность отставания автор предлагает определять с помощью временных диаграмм, которые наглядно покажут, есть у нас запас времени в виде ожидания готовности других компонентов или же дальше другие работы выполняться не смогут, пока не завершится текущая.
Самодокументированные программы
Документацию по работе программы Брукс предлагает встраивать непосредственно в исходный код. Таким образом, это позволит поддерживать документацию всё время в актуальном состоянии. Как только программисту потребуется внести изменения в код, он увидит тут же документацию на этот код и тут же её поправит. Получается, что в 1975 году Брукс предвосхитил нечто такое, что сегодня мы знаем как javadoc. В комментариях он предлагает объяснять не только «как», но и «почему». Назначение является решающим для понимания.
Однако есть и другая точка зрения на этот вопрос. Например, Мартин (Роберт, а не Джордж), автор книги «Чистый код», утверждает, что комментарии — это зло. Даже будучи среди исходного кода они всё равно могут стать неактуальными. «Не трать время на написание комментариев», – говорит Мартин. Вместо этого он предлагает более тщательно подходить к процессу именования переменных, методов и классов. Если всё делается правильно, то и необходимость в комментариях отпадает. А какой точки зрения придерживаетесь вы, документируете ли свой код непосредственно в исходниках?
Модель инкрементальной разработки
Мы должны создать базовый опрашивающий цикл системы реального времени с вызовами подпрограмм (заглушками) для всех функций, при этом сами подпрограммы должны быть пустыми. Скомпилируйте её и протестируйте. Она будет ходить по кругу, буквально ничего не делая, но делая это правильно.
Далее мы реализуем любую из этих заглушек и наша система, не меняя своей структуры и не останавливаясь, начинает уже делать что-то полезное. Важно отметить, что на каждом этапе у нас есть работающая система. Это ли не принцип непрерывной интеграции, он же CI (continuous integration)?
Шаг за шагом, заменяя заглушку реализацией, мы постепенно как бы выращиваем нашу систему. Поскольку в каждый момент времени у нас есть работающая система, мы можем:
- начать тестирование со стороны пользователей очень рано;
- применить стратегию разработки по бюджету, то есть при дефиците бюджета мы просто не будем реализовывать какие-то маловажные модули, не разрушая при этом структуру всей системы.
Зачем понадобилось переиздавать книгу
Тут хотелось бы просто привести цитату Брукса, в которой он иронизирует над самим собой. Он описывает случай, произошедший в самолёте:
Незнакомец, сидевший рядом со мной, читал «Мифический человеко-месяц», и я ждал, как он отреагирует — словом или знаком. Наконец, когда мы вырулили к выходу, я не выдержал:
– Как вам эта книга? Рекомендуете?
– Гм! Ничего такого, чего бы я уже не знал.
Я предпочёл не представляться.
Действительно, зачем переиздавать книгу, которой вот уже 45 лет и которая посвящена такой динамично развивающейся отрасли, как разработка ПО? Весь материал, который я привёл выше, показывает, что многие вопросы, рассматриваемые в книге, актуальны до сих пор. Да, у нас есть scrum, agile, стори-поинты, но если присмотреться, то все новое — забытое, но проявившее себя вновь, старое.
Многие подходы похожим образом были описаны ещё в прошлом веке. И их очередная реинкарнация говорит лишь о том, что в своё время не все усвоили тезисы Брукса. Поэтому время от времени имеет смысл возвращаться к книгам, ставшими уже классикой. Может быть, они откроют вам что-то новое?
gecube
Оба подхода имеют право на жизнь. И похоже золотая середина такая — пиши максимально понятный код без комментариев, но если нужная «чёрная магия», которая неочевидна с полтычка — сделай отдельный комментарий «что тут происходит»
devmark Автор
«Чёрная магия» чаще встречается в legacy-проектах, в которых, возможно, уже успела полностью обновиться команда разработчиков и не осталось тех, которые разрабатывали систему с нуля. Ну а в новых проектах гораздо больше возможности писать «чистый» код.
vagon333
Спустя 10 лет ваш «чистый» код «новых» проектов магически превратится в legacy.
Без комментов в сожных кусках кода все равно обойтись сложно.
devmark Автор
Да, это практически неизбежно, потому что это жизненный цикл любого приложения. Относительно чистый код со временем переходит в legacy. Вопрос лишь в том, насколько быстро это происходит)
gecube
Я больше про какие-то штуки, которые были, например, оптимизированы. И, если, например, изначально код делал какую-то банальную штуку — например, брал квадратный корень наивным способом, то потом превратился в кашу из ассемблера с магическими константами, который пофиксить правильным именованием переменных уже не получится ) Или подобные кейсы.
devmark Автор
Ассемблер и низкоуровневое программирование без комментов вообще никак, увы!
VioletTape
«Черная магия» появляется в коде моментально, как только нет ответа на вопрос: почему так.
К сожалению за почти все 14+ лет разработки я не видел хороших вариантов именно в коде это описать и отобразить, в комментариях тоже. Не помогает и история коммитов, потому что или много изменений кода или же задача в таск трекере имеет только название и никакого анализа и описания.
Будучи архитектором, я не могу часто читать код со всеми деталями, я просто не смогу физически порой найти комментарий «почему» и буду страдать, плакать, идти к автору, если он еще работает и спрашивать почему так. И то не факт, что автор будет понимать и помнить почему так всё вышло.
Нужно описание архитектуры и бизнес истории, причем не в формате как это предлагает эджайл (или кто там это предложил) в формате: «я, как… хочу… чтобы ...» — это вот тоже не дает ответа на вопрос: «а почему ты так хочешь?». Нужны ADR и прочие архитектурные артефакты.
За сим закончу, а то начнет гореть сильно =)
gecube
Полностью согласен. Где-то должны быть отражены РЕШЕНИЯ — почему сделали так, а не иначе. Причём на всех уровнях, начиная с глобальной архитектуры. Именно это позволяет не ходить по граблям (я самый умный и я знаю как, все до меня — дураки, а начинаешь копать и выясняется, что предшественники не просто так сделали именно так и пытались таким образом обойти какую-то проблему).
Но при этом возникает другая проблема. Проектирование — оно однонаправлено. В результате имплементация получается как попало, потому что то, что нарисовано не вписывается в реальный мир. Ну, или разрабы скроили время и сделали как попало. И цикла обратной связи — синхронизации того, что реально сделано с проектной документацией… я не видел. Можно на это потратить кучу энергии и пытаться контролировать это на ручном приводе… но вряд ли это правильно.
aragaer
Некоторое время назад мне пришлось разбираться с одной проблемой в нынешнем проекте. Под подозрение попала загадочная перетасовка битов перед вычислением результата. Коммит, которым эта строчка была добавлена, добавлял только эту одну строчку. Описание коммита — "исправить расположение битов". У меня ушла пара дней сопоставления документации на ECC и на наш контроллер, чтобы убедиться, что эти перестановки (плюс еще последующие 3 строки) действительно нужны.
После этого я сделал коммит, который добавляет к этой строке комментарий с описанием того, почему именно эти биты должны быть переставлены именно так (как пришло из контроллена и как нужно передать в следующую функцию), а комментарий к коммиту содержал вообще около 40 строк с полной табличкой соответствия бит.
Комментарии нужны не столько "что тут происходит", а "зачем это тут вообще делать".
devmark Автор
Абсолютно верно! Особенно если это касается такой низкоуровневой логики, какая бывает в контроллерах.
ruomserg
В авиации есть (было) понятие «доступен летчику средней квалификации». Ну то есть к ручке управления не надо приделывать табличку: «на себя — вверх, от себя — вниз». Но если особенность конструкции не очевидна летчику средней квалификации — ее надо документировать. Аналогично придерживаемся у себя. Если код написан так, что он понятен программисту средней квалификации сам по себе — дополнительных комментариев не требуется. Если код может быть не очевиден для среднего программиста (чем бы это ни было вызвано: сложностью алгоритма, особенностями аппаратуры, странными требованиями клиента) — пишется блок «почему так».
devmark Автор
Во всех подобных формулировках есть один нюанс. Каковы критерии определения этой квалификации? У меня одни критерии, у вас — другие. Нам может казаться, что мы определяем её одинаково, а на деле окажется по-разному.
ruomserg
Согласен — в любой отрасли стандарт меняется. Но обычно разработчик с опытом работы, через которого уже прошла пара джунов/стажеров — интуитивно понимает уровнь людей, на которых надо рассчитывать код. В любом случае, если кому-то приходится объяснять код — это звонок что стоит один раз написать комментарий… Второе возможное упражнение — представить что тебя подняли в 03:30 по звонку, и нужно разобраться где проблема в твоем же коде (и тут ли она?). Если когнитивных способностей разработчика (с учетом пониженной работоспособности, спутанности мыслей из-за прерванного сна, и т.д.) хватает для анализа — код можно дополнительно не документировать.
saboteur_kiev
А надо ли учитывать устаревание языков, библиотек и нюансов? Ведь в ИТ это весьма быстро.
Через каких-то 5 лет приходят в проект новые люди, которые, несмотря на то, что в индустрии недавно, уже сеньоры. Но при этом они начали с более новой версии, и кое-что им будет совершенно неочевидно.
ruomserg
Это правильное замечание, и у меня нет на него точного ответа. На горизонте десяти лет, по нашему опыту, это пока работает. Кардинальных сдвигов в парадигме программирования (например, все разом перешли на FP или ООП вышло из моды) пока нет. Это не гарантия, что такого не случится в будущем (кто постарше из читателей помнит, например, взлет и падение языков 4GL и Rapid Application Development, или период популярности Fortran, или Delphi...). С другой стороны, я бы сказал что есть четкая тенденция к падению квалификации приходящих в отрасль людей. Если раньше почти все условные джуны имели хоть какой-то опыт программирования «для себя» (то есть они по факту сначала заинтересовались программирлованием, а потом пошли на него учиться) — теперь я лично вижу истории типа «хотел поступать в мед, но не хватило баллов — пошел на ИТ». То есть приходят реально люди, весь опыт программирования которых составляет «одна пара в неделю». Обращаясь по аналогии к авиации — это летчики выпуска ускоренной программы военного времени (хотя у нас войны вроде как и нет...). Можно почитать мемуары командиров того периода — первая задача полка, получившего пополнение — было доучить его до минимальных стандартов, которые хотя бы гарантировали что молодые кадры не разложат технику на взлете-посадке и не потеряются при полете по маршруту (даже без воздействия противника). Аналогично я вижу ситуацию и в ИТ — теоретически, можно начать писать софт так, чтобы он был понятен даже попавшему по-ошибке в ИТ медбрату — но кто будет оплачивать эти издержки? Поэтому пока фирмы огранизуют как минимум доучивание на входе (как раз до среднего программиста), или как Яндекс — фактически создают альтернативную систему среднего профессионального образования…
gecube
Все так. Фундамент был заложен 30-50 лет назад и мы до сих пор ездим на принципах и технологиях созданных туда (unix, tcp/ip, KISS, SRP) и пр. И действительно к сожалению у ребят сегодня опыт только в кодировании и конкретных фреймворках. И соответствующий узкий кругозор. С другой стороны — и пофиг. Кто-то будет головой работать (и двигать индустрию вперед) и получаать over дофига, а кто-то будет работать руками и будет заменяемым
click0
Осталось мелочь, чтобы охарактеризовать «летчика средней квалификации», чтобы можно было разделить на летчиков, которые еще не достигли «средней» квалификации и которые уже (значительно) превысили эту квалификацию.
ruomserg
Ну у летчиков-то более менее просто было: учебную программу освоил, на учебном биплане вылетел, на переходном учебном самолете часы налетал — ну все, средняя квалификация достигнута. И в принципе, это и есть какой-то общий знаменатель на который можно равняться. И да, я понимаю, что у нас сейчас каждое учебное заведение, которое готовит якобы ИТ-специалистов понимает под этим то, что ему удобно. Но в целом, в текущем мире я бы ориентировался на хорошего джуна/среднего миддла. Потому что если тело с головой до этого уровня не дотягивает — его надо учить, а не работать…