| |
Пять миров программирования
Почти никогда во всей литературе, посвящённой программированию и разработке программного обеспечения, не упоминается нечто важное, из-за чего мы иногда недопонимаем друг друга..
Вы - разработчик программного обеспечения. Я тоже. Но у нас могут быть совсем неодинаковые цели и требования. На самом деле, существует несколько различных миров разработки ПО, и к разным мирам применимы разные правила.
Допустим, Вы читаете книжку по UML моделированию, а в ней нигде не говорится о её бессмысленности для программирования драйверов. Или же Вы читаете статью, утверждающую, что "20МБ среда [требующаяся для .NET] - это НЕ проблема", а она не содержит очевидного: если Вы пытаетесь написать код для пейджера с 32КБ ROM, то это ещё та проблема!
Я считаю, что в программировании есть пять миров, иногда пересекающихся друг с другом, а в основном нет. Это:
- Ширпотреб
- Внутреннее ПО
- Встроенное ПО
- Игры
- Одноразовое ПО
При чтении одной из последних книг по Экстремальному программированию, одной из замечательных книг Стива МакКонннелла, сайта "Джоэль о софте" или журнала "Разработка программного обеспечения Вы видите много утверждений, как же разрабатывать ПО, однако вряд ли Вы когда-либо замечали малейшее упоминание, о каком же всё-таки типе разработки они ведут речь, что довольно-таки плохо, ведь иногда в разных мирах необходимо разрабатывать по-разному.
Давайте кратко пройдёмся по этим категориям.
Ширпотреб - это ПО, которое должно "серьёзно" использоваться многими людьми. Оно может быть обёрнуто в целлофан и продаваться в сети компьютерных магазинов, или скачиваться по Интернет. Оно может быть коммерческим, шароварным, с открытым исходным кодом, под лицензией GNU или любым другим - главное в том, что такое ПО будет установлено и будет использоваться тысячами или миллионами людей.
Разработка ширпотреба сталкивается со своими проблемами из-за его особенных свойств:
- Из-за большого количества пользователей, у которых часто есть альтернативное ПО, пользовательский интерфейс должен быть проще среднего, чтобы достичь успеха.
- Так как оно запускается на таком множестве компьютеров, код должен быть необычайно эластичен к различиям между компьютерами. На прошлой неделе один из пользователей написал мне об ошибке в CityDesk, которая проявляется только на польских Windows из-за того, как операционная система использует правый Alt для ввода специальных символов. Мы тестировали Windows 95, 95OSR2, 98, 98SE, Me, NT 4.0, 2000 и XP. Мы проводили тестирования с установленным IE 5.01, 5.5 или 6.0. Мы тестировали американские, испанские, французские, израильские и китайские версии Windows. Но мы не совсем ещё добрались до польских.
Существует три наибольшие вариации ширпотреба. ПО с открытым исходным кодом часто разрабатывается в условиях, когда за разработку никому не платят, что кардинально меняет его динамику. Например, вещи, не считающиеся "прикольными", обычно не реализовываются командой добровольцев и, как красноречиво обращает наше внимание Метью Томас, это может повредить юзабилити. Разработка обычно географически рассредоточена, что выливается в абсолютно другое качество командного общения. В мире открытых исходников редко происходят личные встречи вокруг досок с прямоугольниками и стрелочками, поэтому те решения по дизайну, которые выигрывают от рисования, обычно плохо решаются в подобных проектах. Как результат, географически рассредоточенные команды намного лучше преуспели в клонировании существующего ПО, для которого не требуется или требуется принимать очень мало решений по дизайну.
Консультационное ПО - это вариант ширпотреба, который требует так много настроек и установок, что для его установки нужна целая армия консультантов по с непомерными ценами. Пакеты CRM и CMS обычно попадают в данную категорию. Может возникнуть ощущение, что эти пакеты на самом деле совсем ничего не делают, а лишь являются предлогом для прибытия армии консультантов по $300 в час. Хоть консультационное ПО и притворяется ширпотребом, высокая стоимость реализации означает, что оно больше похоже на внутреннее.
Коммерческое веб-ПО, такое как Salesforce.com или даже более тепличный вариант eBay всё равно должно быть простым в использовании и выполняться на множестве браузеров. Хотя у разработчиков есть роскошь (по крайней мере) некоторого контроля над средой "развёртывания" - компьютерами в информационном центре - им необходимо иметь дело с большим разнообразием веб-браузеров и большим количеством пользователей, поэтому я считаю данное ПО вариацией ширпотреба.
Внутреннее ПО должно лишь работать в одной ситуации на компьютерах одной компании, что делает его разработку намного проще. Вы можете сделать множество предположений о среде, в которой оно будет идти. Вы можете потребовать определённую версию Internet Explorer, Microsoft Office или Windows. Если нужен график, пусть за Вас его построит Excel; у всех в нашем отделе есть Excel (но только попробуйте такое с ширпотребом и вы уже устранили половину своих потенциальных покупателей).
Юзабилити здесь не так важно, ведь программным обеспечением необходимо будет пользоваться небольшому количеству людей, да и у них по существу нет никакого выбора, так что им придётся как-то уж с ним уживаться. Скорость разработки более важна. Так как вся стоимость разработки ложится всего лишь на одну компанию, количество обоснованно выделенных ресурсов на разработку значительно меньше. Microsoft может себе позволить потратить $500,000,000 на разработку операционной системы, которая обойдётся среднему пользователю примерно в $80. Но когда Detroit Edison разрабатывает платформу для торговли электроэнергией, размер инвестиции должен иметь смысл для одной компании. Для получения приемлемой окупаемости нельзя тратить столько же, сколько и на ширпотреб. Поэтому увы, но большинство внутреннего ПО просто вызывает отвращение.
Встроенное ПО имеет то уникальное свойство, что оно идёт вместе с куском железа, и в большинстве случаев не может быть обновлено. Это совсем другой мир, скажу я Вам. Требования к качеству намного выше, ведь другого шанса не будет. Возможно, придётся иметь дело с процессором намного более медленным, чем типичный процессор персоналки, поэтому код придётся долго оптимизировать. Быстрый код важнее красивого кода. Доступные устройства ввода-вывода могут быть очень ограничены. У системы глобальной навигации в машине, которую я на прошлой неделе взял напрокат, был настолько жалкий интерфейс, что её использование нагоняло смертельную тоску. Вы никогда не пробовали вводить в одну из таких штуковин адрес? Они показывают на экране "клавиатуру" и вы должны выбирать буквы из 5 матриц по 9 букв каждая (другие примеры этого интерфейса расположены по данной ссылке. У системы глобальной навигации в моей собственной машине экран реагирует на нажатия, что поразительно улучшает интерфейс. Однако, я отвлёкся).
Игры уникальны по двум причинам. Во-первых, экономика разработки игр ориентирована на хиты. Некоторые игры становятся хитами, но намного больше игр проваливаются, поэтому если Вы хотите делать деньги на разработке игр, это необходимо понять и иметь портфолио игр, так чтобы хит-блокбастер покрывал расходы провалов. Это больше похоже на кино, чем на программное обеспечение.
Ещё большей проблемой при разработке игр является наличие всего одной версии. Как только Ваши пользователи прошли Duke Nukem 3D, они не будут обновляться до Duke Nukem 3.1D только ради исправления нескольких ошибок и нового оружия. За некоторым исключением после прохождения игры до конца, опять в неё играть довольно скучно. Поэтому у игр такие же требования к качеству, как и у встроенного ПО, и существует невероятный финансовый императив правильно сделать всё с первого раза. Разработчики ширпотреба могут позволить себе роскошь знания, что если версия 1.0 не удовлетворяет желания пользователей и не продаётся, то, возможно, версия 2.0 будет.
И, наконец, Одноразовый код, который создаётся на время исключительно с целью получения чего-либо ещё, что никогда более не понадобится. К примеру, маленький скрипт, перемалывающий полученный входной файл в какой-нибудь другой формат, нужный всего один раз.
Есть, наверное, и другие типы разработки программ, которые я уже забыл.
Важно знать, что когда бы Вы ни читали книгу о методологии программирования, написанную гуру/консультантом по разработке ПО, работающим на полную ставку, можете быть спокойны, что он говорит о разработке внутреннего, корпоративного ПО. Не ширпотребного, не встроенного и, конечно же, не игр. Почему? Потому что именно корпорации нанимают этих гуру. Они им платят. (Поверьте мне, id software никогда и ни за что не собирается нанимать Эда Йордона, чтобы тот размышлял о структурном анализе.)
На прошлой неделе Кент Бэк заявил, что системы отслеживания ошибок не нужны при экстремальном программировании, потому что комбинация парного программирования (с постоянной проверкой кода) и разработка, основанная на тестах (гарантирующая 100% покрытия кода автоматическими тестами) означает, что у Вас вряд ли когда-либо будут ошибки. Это показалось мне неправильным. Я заглянул в нашу собственную базу хранения ошибок, чтобы проверить, каких типов ошибок в ней особенно много.
Имеющий глаза да увидит – я обнаружил, что очень мало тамошних ошибок могли бы быть обнаружены парным программированием или тестами. Многие наши "ошибки" на языке экстремального программирования называются историями – по существу, это лишь запросы о возможных улучшениях. Мы используем систему отслеживания ошибок для запоминания, выставления приоритетов и управления всеми маленькими улучшениями и большими возможностями, которые мы хотели бы реализовать.
Много других ошибок были обнаружены только лишь после длительного использования на месте. Проблема с польской клавиатурой. Нет способа, которым парное программирование поможет найти такие вот ошибки. Ещё есть логические ошибки использования различных возможностей, которые никогда с нами не случались. Чем больше и сложнее программа, тем больше взаимодействий между возможностями, о которых Вы и не догадываетесь. Определённая последовательность символов ({${?, если хотите знать) вводит в замешательство лексер. Некоторые ftp-сервера сообщают об ошибке при попытке удаления несуществующего файла (наш на такое не жалуется, поэтому такого никогда с нами не случалось).
Я внимательно изучил каждую ошибку. Из 106 ошибок, которые мы исправили в первом пакете обновления для CityDesk, ровно 5 могли бы быть предотвращены с помощью парного программирования или разработки на основе тестов. У нас на самом деле было больше ошибок, о которых мы уже знали, но не считали их важными (только если нас не поправят наши покупатели!), чем ошибок, которые могли бы быть выловлены методами ХР.
Но Кен прав, только вот для других типов разработки. Для большинства корпоративных приложений ни одна из рассмотренных выше не считалась бы ошибкой. Программа вылетает от неверного ввода? Запустите ещё раз, и на этот раз следите за своими {${?! И мы используем только лишь один FTP-сервер и никто во всей компании не использует Польские Windows.
В личных разговорах и в сети я регулярно слышу мнение “Любой программист должен знать X, Y и Z”. Естественно, каждый раз люди называют в этом обязательном списки совершенно разные предметы. Lisp, C, Java, Python, Ruby, Javascript, ассемблер, высшая математика вообще и отдельные ее разделы в частности, английский язык, архитектура x86, design patterns, HTML, основы веб, javascript, принципы юзабилити, принципы параллельного программирования, регулярные выражения, XML, SQL, реляционные БД, теория сложности алгоритмов, lambda calculus и основы функционального программирования, архитектура Windows и Linux, 65535 других вещей. При найме программистов это мнение, за неимением лучшего, часто пытаются превратить в критерий отбора кандидатов.
В результате получается лажа. Некоторые люди, замечательно подошедшие бы в команду, отсеивается по этому критерию. Вместо них берут людей, подходящих по знаниям и опыту, но плохо подходящих для того, чтобы работать в команде. Или плохо подходящих для того, чтобы делать какую-либо полезную работу вообще.
Проблема в том, что набор “обязательного минимума знаний” часто формируется менеджером проекта (бывшим программистом) на базе собственного опыта, а не требований проекта. Если такой менеджер долго писал на C++ и научился на нем замечательным вещам, то он будет требовать от кандидатов знания C/C++. Даже если это абсолютно бесполезно для текущего проекта. И для всех будущих проектов тоже. Причина может быть и в инерции мышления и в стремлении найти “идеального программиста”, который не “может знать X, Y и Z”. И в том и в другом случае вы ухудшаете свои шансы найти лучшего кандидата.
Большей частью разработка ПО одинакова вне зависимости от проекта, но не вся. Когда Вам говорят о методологии, подумайте о том, как она согласуется с делаемой именно Вами работой. Подумайте, откуда говорящий. Стив МакКоннелл, Стив Магуайр и я все из одной песочницы: мира ширпотребных электронных таблиц, написанных в Редмонде, штат Вашингтон. И у таких у нас высокая планка на лёгкость в использовании и низкая для ошибок. Большинство других гуру методологии зарабатывают свой кусок хлеба, консультируя внутренние корпоративные разработки, поэтому о них они и говорят. В любом случае, мы должны уметь учиться друг у друга.
8 приемов эффективной трансформации прототипа в продукт
Начну с небольшой истории. Жил был проект, и работало над ним три программиста. Начиналось все очень бодро и весело - за неделю была создана демострационная версия, показывающая принципиальную осуществимость задачи проекта на одном маленьком примере. Потом за две недели был создан и доведен до уровня пригодности к использованию пользовательский интерфейс. Потом для демонстрации финансовому директору была добавлена фича X, а для демонстрации генеральному - фича Y. Руководство было в восторге от темпов и легкости решения задач, которые раньше казались неразрешимыми. Шло время, новые фичи добавлялись одна за другой. Затем было решено взять эту разработку и: ставить во все филиалы, в 10 раз увеличить количество пользователей, продавать ее другим организациям (нужное подчеркнуть). Тут выяснилось, что не все так гладко. То и дело из программы вылезали странные ошибки, не поддающиеся воспроизведению. Программа регулярно падала без объявления войны. Изначальные предположения о данных не оправдывались, и требовалась немедленная доработка, чтобы продолжать работать с какой-нибудь нетипичной информацией. Где-то раз в неделю пользователи натыкались в программе на очередное сообщение об ошибке, использующее английские матерные выражения для большей экспрессивности. Развертывание системы на каждом новом сервере было кошмаром. Разработка замечательных новых функций замедлилась, команда тратила все больше времени на исправление ошибок и техподдержку. В общем, запахло жареным.
Знакомо, не правда ли?
Это может случиться и с лучшими из нас. Такая ситуация типична для большинства проектов, начинавшихся не с мертворожденного ТЗ в трех томах, а с небольшой программы, делающей что-то полезное, или с прототипа, демонстрирующего интерфейс и/или принципиальную возможность решить какую-то технологическую проблему. Все дело в том, что прототип и продукт на его базе - это абсолютно разные вещи. При этом прототип, как правило, не выкидывают, а пытаются превратить его в продукт. Я считаю, что это правильный подход, при условии, что вы делаете это с умом. Попробую помочь несколькими советами в этом нелегком деле.
- Не надейтесь на чудо. Я знаю людей, которые считают, что “вычистить код”, который писался полгода - дело нескольких недель. Безнадежные оптимисты… Фред Брукс в своем “Мифическом человеко-месяце” пишет, что качественный программный продукт требует для разработки в три раза больше ресурсов, чем отдельная программа с аналогичной функциональностью. Разница между затратами на разработку прототипа и готовой версии - такого же порядка. Документация, инсталлятор, устранение ошибок и повышение общего качества кода - все это требует времени. Если ваш прототип не предназначался для сейлз-презентаций, добавьте время придумывание и реализацию хорошего пользовательского интерфейса. На стадии прототипа у вас нет ничего вышепересиленного Значит, придется делать все это на стадии трансформации в продукт, и это действительно долго. Так что не надейтесь на чудо. И всеми средствами уничтожайте подобные надежды вашего руководства (в частности, см. пункт 6).
- Объявите новый этап проекта. Стиль разработки продукта сильно отличается от разработки прототипа. Например, при создании продукта нужно перестать срезать углы и вместо быстрых решений использовать правильные. Обеспечить высокое покрытие тестами всего кода, Обрабатывать все ошибки, которые могут возникнуть. Обращать внимание на производительность. Совсем по-другому подходить к тестированию и оценке критичности найденных ошибок. Едва ли будет ошибкой сказать, что по-другому нужно будет подходить практически к каждому аспекту разработки. Даже если разработчики знают об этом эффекте, им будет тяжело переключиться. Помогите им. Проведите общее собрание и объявите о начале нового этапа проекта. Напомните, что приоритеты кардинально изменились и дайте им время осознать и прочувствовать это. На некоторое время оторвите всех от написания кода, например, проведите серию встреч по восстановлению задач проекта, основных проектных решений, архитектуры. Если проект длинный, то отправьте всех участников в недельный отпуск - им будет легче переключиться.
- Ротация разработчиков. Когда долго работаешь над одной задачей, то критическое восприятие ее притупляется. Если у вас хотя бы два разработчика, пусть они поменяются участками работ. В результате у каждого разработчика будет свежий взгляд на задачу, что поможет ему легче преодолеть инерцию и перейти в новый режим работы. Кроме того, так вы можете гарантировать, что хоть один человек посмотрит на код критическим взглядом (см. пункт 7).
- Заменяйте прототип по кускам. Ни в коем случае не выкидывайте весь прототип, чтобы начать с чистого листа. В коде накапливается множество знаний о нюансах, которые вы никогда не сможете получить из спецификации. Кроме того, очень удобно, когда в любой момент у вас на руках есть работающий продукт. Таким образом, гораздо лучше, если вы будете постепенно заменять модули прототипа кодом продуктового качества. Наверняка вы захотите параллельно с этим преобразовать стихийно сложившуюся архитектуру прототипа в что-нибудь более стройное и логичное - это тоже возможно. Пусть каждый разработчик перечитает книгу Мартина Фаулера “Рефакторинг” - и вперед. В некоторых случаях вместо проведения рефакторинга проще написать новый модуль с чистого листа и заменить им старый. Это тоже хороший вариант, главное, - обратите особенно внимание на то чтобы не сделать в новой версии неочевидных ошибок, которые уже были сделаны при написании прототипа; для этого вам пригодится рецензирование кода (пункт 7).
- Используйте юнит-тесты! Возможно, для прототипа вам удалось обойтись вообще без тестов. Или достаточно было нескольких тестов, проверяющих базовые сценарии использования. Для продукта этого недостаточно. Вам понадобится много тестов. Обеспечьте высокий процент тестового покрытия. Теперь вам недостаточно знать, что программа корректно работает в трех основных презентационных сценариях, вам нужна уверенность в том, что программа будет корректно работать и во всех исключительных случаях. Это недостижимый идеал, но в ваших интересах приблизиться к нему. По моему опыту, разумный минимум - это более 85% code coverage во всех тестируемых модулях (некоторые модули вообще нет смысла тестировать, не включайте их в эту статистику)
- Визуализируйте изменения. Скорее всего, вам будет тяжело объяснить заказчику или спонсору проекта, почему над готовым прототипом нужно работать еще столько времени, чтобы сделать из него продукт. Если спонсор проекта - человек, далекий от программирования, то вам будет очень тяжело. С точки зрения спонсора, проект практически готов, и требуется лишь немного времени на доводку. Вы же собираетесь потратить больше времени, чем было затрачено на разработку прототипа. Чтобы облегчить свое положение, сделайте так, чтобы увидев рабочую версию, можно было в течение 5 секунд получить представление о степени ее готовности. Помните, что непрограммист может оценить состояние проекта только по состоянию интерфейса. Поэтому начните новый этап с того, что испортите интерфейс продукта, так чтобы его качество примерно соответствовало качеству содержимого. Покажите этот интерфейс заказчику и объясните, что примерно в таком состоянии находится весь продукт в данный момент. По мере продвижения к готовому продукту постепенно улучшайте интерфейс, чтобы продемонстрировать внутренние изменения.
- Отрецензируйте каждую строку кода. Если вы последуете предыдущим советами, то не начнете реализацию продукта с чистого листа, а будете трансформировать прототип, последовательно заменяя его модули переработанными модулями продуктового уровня качества. Здесь есть одна опасность - имея на руках работающую систему, вы можете забыть, что часть ее - это модули из прототипа, вероятно, не обладающие нужным уровнем качества. Если в итоговый продукт попадут такие модули, то через некоторое время вы будете иметь дело с очень забавными ошибками - вылетом программы при вводе неправильного значения, отсутствием проверки прав доступа к функции, отсутствием валидации вводимых данных, “кривыми” сообщениями об ошибках и т.д. Чтобы этого избежать, надо гарантировать, что ни одна строка непроверенного кода из прототипа не попадет в итоговый продукт. Лучший способ - систематически использовать рецензирование кода (code review), которое должно проводиться разработчиком, не являющимся автором кода. О том, как правильно проводить рецензирование кода, можно прочитать здесь. Будет здорово, если вы добавите в весь исходный код прототипа информацию о том, что рецензия еще не проводилась, например с помощью комментариев “This block needs code review”, напишете небольшой скрипт для сбора статистики о количество строк неотрецензированного кода в проекте, и использовать эту статистику как один из показателей степени завершенности продукта. Обратите особое внимание на проверку всех интерфейсов и сообщений об ошибках! В итоговый продукт ни в коем случае не должны проникнуть ни опечатки, ни “тестовые” тексты (”Здесь будет подробное сообщение об ошибке”) из прототипа.
- Напишите спецификацию. Возможно, спецификации, по которым создавался прототип, покрывают только небольшую часть функциональности. Возможно, прототип создавался вообще без спецификаций. Это нормально для прототипа. Но для продукта вам понадобятся спецификации. В первую очередь для того, чтобы проверить предполагаемую функциональность и архитектуру на логичность и внутреннюю непротиворечивость: если вам очень трудно выразить словами свое представление о том, как должен работать продукт, значит, его надо сделать проще. Если создателям продукта сложно его понять, то каково будет пользователям? Потратьте время на то, чтобы функциональность вашего продукта можно было легко описать в спецификации, это окупится.
Очень обидно осознавать, что около года назад, когда перед проектом, которым я руководил, стояла задача трансформации прототипа в продукт, я даже не увидел, что начинается новый этап разработки. Из вышеперечисленных техник для стабилизации продукта были использованы только юнит-тесты и спецификации. И то и другое - в недостаточном количестве. Соответственно, качество продукта было весьма плачевным. От этого не спас даже очень высокий профессиональный уровень разработчиков - даже самый лучший разработчик не напишет код промышленного качества, если он концентрируется на какой-то другой задаче. Чтобы улучшить ситуацию понадобилось больше полугода… Что ж, теперь я знаю, насколько важно правильно перейти от создания прототипа к созданию продукта.
Ссылки
Joel on Software
Career Calculus
Что должен знать каждый программист
8 приемов эффективной трансформации прототипа в продукт
Прогулки по граблям
| | |