Работа терминалов управлялась прерываниями. Символы, передаваемые терминалам, хранились в циклических буферах. Каждый раз при передаче символа последовательным портом срабатывало прерывание и к отправке готовился следующий символ циклического буфера.
Со временем выяснилось, что терминал зависал из-за рассинхронизации трех переменных, управлявших циклическим буфером. Мы понятия не имели, почему это происходило, но это было хоть что-то. Где-то в 5K строк кода супервизора содержалась ошибка, которая некорректно работала с одним из этих указателей.
Новая информация также позволила нам снимать блокировку с терминалов вручную! Мы могли при помощи инспектора присвоить этим трем переменным значения по умолчанию, и терминалы, как по волшебству, начинали работать снова. Вскоре мы написали маленький фрагмент кода, который проверял синхронизацию счетчиков и восстанавливал их в случае необходимости. Сначала код запускался специальным тумблером пользовательского прерывания на передней панели, когда заказчики по телефону сообщали о зависании. Позднее мы просто выполняли код восстановления каждую секунду.
Месяц спустя проблема с зависанием исчезла – по крайней мере с точки зрения профсоюза грузоперевозчиков. Время от времени один из их терминалов приостанавливался на полсекунды, но с базовой скоростью передачи 30 символов в секунду никто этого не замечал.
Но почему происходила десинхронизация счетчиков? Мне было 19, и я был полон решимости разобраться.
Автором кода супервизора был Ричард, уехавший на учебу в колледж. Никто из нас толком не разбирался в супервизоре, потому что Ричард относился к своему созданию очень ревниво. Код принадлежал ему, и нам было не положено разбираться в нем. Но теперь Ричарда не было, поэтому я нашел листинг толщиной в несколько дюймов и начал просматривать его страницу за страницей.
Циклические буферы в этой системе были обычными структурами данных FIFO, то есть очередями. Прикладные программы заносили символы с одного конца очереди, пока она не заполнялась. Обработчики прерываний извлекали символы с другого конца очереди, когда принтер был готов принять их. Если в очереди не оставалось символов, принтер останавливался. Из-за ошибки приложения считали, что очередь заполнена, а обработчики прерываний – что она пуста. Обработчики прерываний выполнялись в другом "программном потоке", отдельно от остального кода. Таким образом, счетчики и переменные, доступные для обоих обработчиков и остального кода, должны быть защищены от параллельного обновления. В нашем случае это означало, что перед выполнением любого кода, работавшего с этими тремя переменными, необходимо было запретить прерывания. К тому моменту, когда я сел за код, мне уже стало ясно: нужно искать участок кода, который работает с переменными без предварительного запрета прерываний.
Конечно, сейчас появилось множество мощных инструментов для поиска всех мест изменения переменных в программе. За считанные секунды вы найдете все строки кода, которые работают с переменными. За минуты можно будет определить, где именно автор забыл запретить прерывания. Однако наша история происходила в 1972 году, и у меня таких инструментов не было. Были только мои глаза.
Я просмотрел каждую страницу кода в поисках переменных. К сожалению, переменные использовались везде – почти на каждой странице программа обращалась к ним тем или иным образом. При многих обращениях прерывания не запрещались – они ограничивались чтением, а следовательно, были безвредными. Вдобавок в этом конкретном ассемблере было невозможно проверить, доступна ли переменная только для чтения, без анализа логики кода. Каждое чтение переменной могло сопровождаться ее обновлением. И если при этом прерывания не были запрещены, содержимое переменных могло быть легко испорчено.
Мне потребовалось несколько дней интенсивного изучения кода, но в конце концов я нашел ошибку. В середине кода отыскалось одно место, в котором одна из трех переменных обновлялась без предварительного запрета прерываний.
Я занялся вычислениями. Уязвимость существовала на протяжении двух микросекунд. В системе дюжина терминалов передавала данные на скорости 30 символов в секунду, так что прерывания происходили каждые 3 микросекунды или около того. С учетом размера супервизора и тактовой частоты процессора зависания от этой уязвимости должны были происходить с частотой примерно 1–2 раза в день. Есть!
Конечно, я исправил ошибку, но у меня не хватило смелости отключить автоматический запуск проверки и исправления счетчиков. До сих пор не уверен в том, что в системе не было другой "дыры".
Время отладки
По какой-то неведомой причине разработчики не считают отладку естественной частью процесса разработки. Им кажется, что отладка сродни физиологической потребности: ей занимаются просто потому, что это неизбежно. Однако время отладки обходится фирме ровно в такую же сумму, что и время написания кода, поэтому любые меры по его сокращению будут полезны.
Сейчас я провожу за отладкой намного меньше времени, чем десять лет назад. Я не проводил точных измерений, но, по моим оценкам, продолжительность отладки сократилась раз в 10. Я добился этого воистину радикального сокращения переходом на методологию разработки через тестирование (TDD, Test Driven Development), которая будет рассматриваться в следующей главе.
Независимо от того, используете ли вы TDD или другую методологию аналогичной эффективности, вы как профессионал обязаны стремиться по возможности приблизить время отладки к нулю. Конечно, нуль – цель асимптотическая, но от этого она не перестает быть целью.
Врачи не любят заново делать операции пациентам, чтобы исправить свои прошлые ошибки. Адвокаты не любят повторно браться за "заваленные" дела. Врач или адвокат, который слишком часто допускает ошибки, не будет считаться профессионалом. Аналогичным образом разработчик, создающий слишком много ошибок, действует непрофессионально.
Выбор темпа
Программирование – марафон, а не спринт. Невозможно выиграть забег, набрав максимальную скорость на старте. Побеждает тот, кто бережет силы и разумно выбирает темп. Марафонец должен заботиться о своем организме как до, так и во время соревнований. Точно так же и профессиональные программисты берегут силы и творческий потенциал.
Умейте остановиться
Не можете уйти домой, пока не решили свою задачу? Уйти не только можно, но и нужно! Творческое мышление и интеллектуальная деятельность – недолговечные состояния нашего разума. Когда мы устаем, они исчезают. Если вы будете силой заставлять свой уставший мозг решить задачу в поздний час, скорее всего, это кончится лишь дополнительной усталостью и снижением вероятности того, что задачу удастся решить в душе (или в машине).
Если вы зашли в тупик, если вы устали – отвлекитесь на время. Дайте своему творческому подсознанию отдохнуть от задачи. Внимательно относясь к своим ресурсам, вы сделаете больше за меньшее время и с меньшими усилиями. Сами определяйте темп работы для себя и своей группы. Изучите свои закономерности проявления творческих способностей и озарений и используйте их вместо того, чтобы подчинять насильно.
По дороге домой
Многие задачи были успешно решены мной в машине, когда я возвращался с работы домой. Вождение требует больших затрат нетворческих интеллектуальных ресурсов. Ваши глаза, руки и часть ума заняты управлением машиной, и вы отвлекаетесь от своей задачи на работе. И в этом отвлечении есть что-то такое, что помогает вашему разуму находить другие, более творческие пути поиска решений.
Душ
Неожиданно много задач было решено мной в душе. Возможно, утренние водные процедуры помогают мне проснуться и припомнить все решения, которые возникли в моем мозгу, пока я спал.
Работая над задачей, вы иногда подбираетесь к ней так близко, что не видите всех возможных вариантов. Вы упускаете элегантные решения, потому что творческая часть вашего разума подавляется излишней сосредоточенностью. Иногда лучшее решение задачи – пойти домой, поужинать, посмотреть телевизор и лечь спать, а на следующее утро проснуться и принять душ.
Отставание от графика
Вы будете отставать от графика. Это происходит с самыми лучшими из нас. Это происходит с самыми прилежными. Оценки срываются, а результат запаздывает.
Чтобы свести к минимуму проблемы, связанные с отставанием, помните о двух важнейших аспектах: раннем обнаружении и прозрачности. Хуже всего, когда вы до последнего момента уверяете окружающих, что работа будет завершена вовремя, а потом подводите всех. Не делайте этого. Регулярно проверяйте ход проекта по отношению к конечной цели и представьте три обоснованные конечные даты: лучшую, номинальную и худшую. Будьте по возможности честны в своих оценках. Оптимизму в них не место! Представьте все три даты своей группе и ключевым участникам проекта. Ежедневно обновляйте их.
Надежда
Что делать, если из этих чисел следует, что вы можете не успеть к критической дате? Например, через десять дней начинается торгово-промышленная выставка, на которой должен быть представлен продукт. С другой стороны, тройственная оценка времени готовности подсистемы, над которой вы работаете, равна 8/12/20.
Не надейтесь, что вам удастся сделать все за 10 дней! Надежда убивает проекты. Надежда срывает графики и рушит репутации. Надежда навлечет на вас большие неприятности. Если выставка начнется через 10 дней, а номинальная оценка составляет 12 дней, вы не успеете к сроку. Убедитесь в том, что группа и ключевые участники проекта понимают ситуацию, и не успокаивайтесь до тех пор, пока не будет сформулирован альтернативный план. И не позволяйте надеяться другим.
Спешка
А если начальник приглашает вас на беседу и настоятельно просит успеть к сроку? Если он настаивает, что вы должны сделать "все возможное"? Не отступайте от своих оценок! Исходные оценки всегда точнее любых изменений, вносимых под давлением. Скажите начальнику, что вы уже рассмотрели все варианты (потому что вы их действительно рассмотрели) и что ускорить работу можно только одним способом – усечением части функциональности. Не поддавайтесь искушению ускорить темп.
Горе несчастному разработчику, который уступит давлению и попробует успеть к сроку. Он начинает искать обходные пути и работать сверхурочно в тщетной надежде совершить чудо. Это верный путь к катастрофе, потому что он внушает вам, вашей группе и ключевым участникам проекта неоправданные надежды. Все это оборачивается только нежеланием взглянуть в лицо реальности и откладыванием неприятных, но необходимых решений.
Спешка бессмысленна. Вы не заставите себя программировать быстрее. Вы не заставите себя быстрее решать задачи. А если попытаетесь – вы только замедлите работу и устроите хаос, который замедлит работу других.
Итак, вы должны ответить начальнику, группе и ключевым участникам проекта так, чтобы у них не осталось иллюзий.
Сверхурочные
Начальник говорит: "А если увеличить рабочий день на пару часов? Если работать по воскресеньям? Наверняка как-нибудь можно выжать лишние часы, чтобы успеть ко времени".
Сверхурочная работа возможна, а иногда просто необходима. Иногда можно завершить работу к вроде бы невозможной дате, работая по 10 часов в сутки, с одним-двумя воскресеньями. Однако это очень рискованно. Вряд ли 20 % увеличение продолжительности рабочего дня позволит вам выполнить на 20 % больше работы. Что еще важнее, сверхурочная работа на протяжении более чем двух-трех недель наверняка приведет к провалу.
Следовательно, на сверхурочную работу можно соглашаться только при выполнении некоторых условий: 1 – лично вы можете ее себе позволить; 2 – аврал будет продолжаться недолго, не более двух недель, и 3 – у руководства имеется резервный план на случай, если ваши усилия завершатся неудачей.
Последний критерий имеет решающее значение. Если ваш начальник не может объяснить, что он собирается делать в случае неудачного исхода, не соглашайтесь на сверхурочную работу.
Ложная готовность
Вероятно, худший из всех видов непрофессионализма со стороны программиста – это попытка выдать недоделку за готовый продукт. Иногда это просто открытая ложь, что достаточно плохо. Но гораздо опаснее другая ситуация – попытки подвести рациональную основу под новое определение "готовности". Мы убеждаем себя в том, что сделано достаточно, и переходим к следующей задаче. Мы говорим, что оставшуюся работу можно выполнить позднее, когда у нас будет больше времени.
Эта болезнь заразна. Если одному программисту такое поведение сходит с рук, другие видят и следуют его примеру. Один из них расширяет определение "готовности" еще дальше, и все остальные следуют его примеру. Я видел, как подобные ухищрения доходили до ужасающих крайностей. Один из моих клиентов под "готовностью" понимал регистрацию изменений в базе данных. При этом код мог даже не компилироваться!
Когда группа попадает в эту ловушку, начальство слышит, что все идет нормально. Все отчеты о ходе работ показывают, что работа будет завершена к сроку. Ситуация напоминает пикник слепых на железнодорожных рельсах: никто не видит приближающийся грузовой состав незавершенной работы, пока не будет слишком поздно.
Определение "готовности"
Проблема ложной готовности решается созданием независимого определения "готовности". Для этого следует поручить бизнес-аналитикам и специалистам по тестированию создать автоматизированные приемочные тесты, без прохождения которых продукт не может считаться готовым. Тесты пишутся на тестовых языках – таких как FitNesse, Selenium, RobotFX, Cucumber и т. д. Тесты должны быть понятны для бизнесменов и ключевых участников проекта, не связанных с технической стороной, и они должны выполняться по возможности часто.
Помощь
Программирование – трудная работа. Чем вы моложе, тем слабее в это верится. В конце концов, программный код – всего лишь последовательность команд if и while. Но по мере накопления опыта вы понимаете, что важнейшую роль играет способ объединения этих команд if и while. Нельзя просто свалить их в одну кучу и надеяться на лучшее. Вместо этого систему необходимо тщательно разбить на небольшие, понятные блоки, которые должны быть как можно меньше связаны друг с другом, – а это действительно сложно.
Более того, программирование настолько сложно, что одному человеку с этой работой не справиться. Даже самому квалифицированному специалисту пригодятся мысли и идеи других программистов.
Как помогать другим
Ответственные программисты должны быть готовы помогать друг другу. Программист, который изолируется в своем офисе или кабинке и отказывается отвечать на вопросы других, нарушает профессиональную этику. Ваша работа не настолько важна, чтобы вы не могли выделить немного времени на помощь другим. Честь профессионала обязывает предложить ближним помощь тогда, когда это потребуется.
Это не означает, что вы должны отказаться от "личного времени". Конечно, оно необходимо, но к его выбору следует подойти вежливо и честно. Например, вы можете сообщить, что между 10:00 и полуднем вас нельзя беспокоить, а с 13:00 до 15:00 ваша дверь открыта для других.
Также будьте внимательны к состоянию работы ваших коллег. Если кто-то испытывает затруднения, предложите помощь. Просто удивительно, насколько эффективной порой оказывается такая помощь. Дело не в том, что вы намного умнее своего коллеги; просто свежая точка зрения нередко становится мощным катализатором для решения проблем.
Когда вы кому-то помогаете, сядьте и напишите код вместе. Запланируйте на помощь не менее часа. Возможно, вам понадобится меньше времени, но торопиться не стоит. Сосредоточьтесь на задаче и приложите максимум усилий. Скорее всего, вы в конечном итоге от такого сотрудничества получите больше, чем отдадите.
Как принимать помощь
Когда кто-то предлагает вам помощь, будьте признательны. Примите помощь с благодарностью и отнеситесь к ней со всем вниманием. Не отказывайтесь от помощи, потому что вам не хватает времени. Выделите на разговор около минут 30. Если особой пользы от предложенной помощи не видно, вежливо извинитесь и завершите беседу с благодарностью. Помните: профессионализм обязывает вас не только предлагать, но и принимать предложенную помощь.
Научитесь просить о помощи. Когда вы зашли в тупик или не можете разобраться в задаче, попросите других помочь вам. Если вы находитесь в общей комнате, просто скажите: "Мне нужна помощь". В остальных случаях можно воспользоваться Твиттером, электронной почтой или телефоном. Обратитесь за помощью – в конце концов, это также является делом профессиональной этики. Непрофессионально оставаться в тупике, когда помощь так доступна.
Думаете, я сейчас нарисую радужную картину – хор поет проникновенную песню, пушистые зайчики прыгают на спины единорогам и все дружно отправляются к радуге надежды и изменений? Нет, не совсем так. Видите ли, программисты обычно бывают самонадеянными, эгоцентричными интровертами. Мы идем в эту область не потому, что мы любим людей. Большинство из нас приходит в программирование, потому что нам нравится концентрироваться на мелочах, жонглировать множеством абстрактных концепций и иным образом доказывать себе, что мы являемся обладателями выдающегося интеллекта, – а вовсе не для того, чтобы разбираться с досадными сложностями других людей.
Да, это стереотип. Да, это обобщение со множеством исключений. Однако реальность такова, что программисты не склонны к сотрудничеству. Тем не менее сотрудничество играет исключительно важную роль в эффективном программировании. А раз для многих из нас оно не является инстинктом, значит, нам нужны методологии, которые заставляют нас сотрудничать.