21 ошибка программиста PHP, часть 3
Стерлинг Хьюз.
29-09-2003
Описания семи, последних, "смертельных" ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимание, уделённое как проекту в целом, так и коду программы, в частности
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой стороной: PHP очень прост в изучении. Это привлекает многих людей; однако, несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf()или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от некритических до смертельных. Наряду с анализом этих ошибок, представлены способы их избежания, а также некоторые маленькие хитрости, накопленные за многие годы практики программирования.
Часть третья
7 "смертельных" ошибок
Целевая аудитория
Эта серия статей предназначена для тех программистов на языке PHP, которые хотят избежать наиболее общих ошибок в написании кода. Читатель, как минимум, должен знать общий синтаксис PHP, а также весьма желателен некоторый опыт использования языка на практике.
Введение
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой стороной: PHP очень прост в изучении. Это привлекает многих людей; однако, несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf()или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от "некритических" до "смертельных". Наряду с анализом этих ошибок, представлены способы их избежания, а также некоторые "маленькие хитрости", накопленные за многие годы практики программирования.
Часть 1: Описываются 7 "детских" ошибок (#21-15, в обратном порядке, в соответствии со степенью серьёзности по нашей классификации). Такие ошибки не вызывают серьёзных проблем, но приводят к уменьшению эффективности работы программы, а также выражаются в громоздком трудночитаемом коде, в который, к тому же, трудно вносить изменения.
Часть 2: Следующие 7 ошибок (#14-8) относятся к "серьёзным". Они ведут к ещё более значительному уменьшению скорости выполнения кода, уменьшению безопасности скриптов; код становится еще более запутанным.
Часть 3: Описания семи, последних, "смертельных" ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимание, уделённое как проекту в целом, так и коду программы, в частности.
7. Программирование методом "вырезать-вставить": неверный подход
Некоторым новичкам свойственно копировать чужой код, например скрипты проверки адресов, отправки почты, обработчики форм. Результатом такой деятельности является мешанина операторов, хоть и криво, но делающая своё дело.
И если при всех оптимальных условиях код и будет работать, то при настоящей проверке обязательно свалится. Причём латание "дыр" никогда не сделает ваш код:
- расширяемым: программа будет выглядеть как набор обрывков кода сляпанных вместе. Попросите опытного программиста что-либо изменить в таком скрипте, и он почти наверняка предпочтёт написать свой. Нечитаемый код - нерасширяемый код.
- безопасным: вы вставляете в свой проект код чужого человека; при этом вы точно не знаете, что же именно код делает. Задумайтесь над этим. А что если код содержи подложный системный вызов, который сносит все файлы с вашего жёсткого диска? Кроме того, один и тот же код не может быть одинаково защищён и безопасен для разных систем. Ну и, наконец, вы просто копируете чужие ошибки.
- быстрым: если вы собираете код из кусочков разных скриптов, не ждите от него быстрой работы. Ибо в этом случае логическое развитие скрипта попросту отсутствует; а всем известно, что в основе быстродействия скрипта лежит его логика.
Правильный подход: изучить, потом скопировать
Прежде чем копировать чужой код, внимательно его изучите. Проанализируйте, чтО было сделано и как. И только если код хорошо читается, вписывается в логику вашей программы и не содержит ошибок, только тогда его можно рассматривать как кандидатуру на копирование. Если эти правила соблюдены, скопированная часть может быть быстро и безболезненно интегрирована в проект.
Библиотеки: то, что вам нужно
Используйте библиотеки PHP-функций только из надёжных источников, как, например,
PEAR или
PHP Classes Repository. Дополнительная функциональность, которую предоставляют вам API из этих библиотек, пойдут вашему проекту только на пользу. Итак, если вы нашли уже готовую библиотеку нужных вам функций (из надёжного источника), то её использование только приветствуется.
6. Отсутствие в проекте технических директив
Как-то раз, в самом начале моей карьеры, я работал над одним капитальным проектом (на PERL) в команде с тремя другими программистами. Поскольку я был ещё молод (и к тому же не был руководителем проекта), никаких технических директив (ТД) у нас не было. Каждый взял свою часть проекта и работал над ней в одиночку. Когда же мы стали собирать проект, наши части сильно разнились по своему стилю.
Например, один из моих коллег в названии переменных и функций использовал БиКапитализацию. Я использовал underscore ("_"). Руководитель проекта избрал самый оригинальный стиль и использовал оба метода в зависимости от настроения (что, собственно, и вызвало конфликты в пространстве имён; всем известно, какая это головная боль для разработчика).
В общем, это был не проект, а свалка. Понадобилось 20 часов дополнительного времени; хотя ничего бы подобного не случилось, если бы мы удосужились составить чёткие ТД для своего проекта.
ТД описывают структуру и внешнее представление исходного кода проекта, определяют методы и стратегию реализации конечного продукта.
Для разработки любого проекта нужно составить ТД и следовать им. В ТД должны определяться самые разнообразные аспекты работы: как общие моменты, например, разбитие исходного кода (его файловая структура), так и более конкретные, например, правила именования переменных (суффиксы и префиксы, глобальные прописью).
ТД - это пакет соглашений, которые должны выполняться независимо от личных предпочтений программистов. В ТД фиксируются решения по всем спорным моментам, как например:
- какие переменные объявлять глобальными, и как они будут дифференцироваться от локальных переменных.
- Структуру хранения документов, то есть правила, в какую директорию поместить тот или иной файл: src или lib.
- Стиль и содержание комментариев.
- Правила документирования.
- Ширина строк.
ТД должны быть оформлены в своего рода документ; копию его должен получить каждый из участников проекта. По завершении проекта, этот документ должен храниться для последующих обращений к исходному коду.
Пример ТД проекта
Для лучшего понимания, попробуем составить образец ТД: без подробностей, только основные пункты. Естественно, все необходимые элементы будут представлены, но представлены скорее схематично: настоящие ТД могут свободно занимать 10-40 страниц. Стоит заметить, что для каждого нового проекта не обязательно писать новые ТД с нуля: достаточно просто обзавестись готовым шаблоном и при необходимости вносить в него изменения.
ТД по представлению проекта DesignMultimedia.com
Итак, приведём пример ТД по внешнему представлению проекта. Разработав подобный документ, вы можете сосредоточиться только на непосредственной реализации проекта и не беспокоиться более о несвязности исходников, конфликтов именования или о структуре сайта, если вы разрабатываете свой сайт.
Введение
Эта часть определяет следующие моменты:
- файловая структура
- колонтитулы файла
- документирование исходников
- комментарии: стиль, значения, определения
- директивы по сборке проекта
- правила именования переменных
Файловая структура (на примере DesignMultimedia.com)
<Здесь описываются правила хранения файлов в приложении (то есть какой файл куда идёт), а также соглашения по именованию файлов.>
Пояснения по структуре:
<Здесь приводятся пояснения по всем указанным выше правилам и соглашениям, чтобы исключить любое неверное истолкование.>
Колонтитулы
Каждая страница исходного текста проекта содержит следующий заголовок:
<Здесь указывается заголовок. Он может включать в себя всё что угодно: от образца кода до простого копирайта.>
Например, все .c и .h файлы проекта PHP4 имели следующий стандартный заголовок:
/*
+-----------------------------------------------------------------------+
| PHP версия 4.0 |
+-----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
+-----------------------------------------------------------------------+
| данный файл с исходным кодом подпадает под действие лицензии PHP |
| версии 2.02, копия которой хранится в данном пакете программ в файле |
| LICENSE; также данную лицензию вы можете получить по адресу в WWW |
| http://www.php.net/license/2_02.txt. |
| Если вы не получили копию лицензии PHP и не имеете возможности |
| получить её через WWW, просьба сообщить об этом по адресу |
| license@php.net и лицензия будет незамедлительно выслана. |
+-----------------------------------------------------------------------+
| Авторы: Sterling Hughes <sterling@php.net> |
+-----------------------------------------------------------------------+
*/
И нижний колонтитул:
<Здесь помещаете свой стандартный нижний колонтитул; в проекте PHP4 во всех файлах .c и .h можно увидеть следующее:>
/*
* Локальные переменные:
* ширина табуляции: 4
* основной отступ C: 4
* Окончание:
*/
И если потребуется, пояснения.
<Здесь приводятся пояснения по содержанию верхнего и нижнего колонтитулов, возможно, причины их наличия (всё это зависит от содержания колонтитулов).>
Документация
<В этой части определяется стиль документирования кода в приложении: будет ли он оформлен в виде javadoc или XML Docbook.>
Стиль комментариев
<Здесь описывается стиль комментариев с разъяснением сокращений или тех или иных "фраз".>
Правила сборки проекта
<Здесь даются правила в виде запретов или директив, например: "Не хранить разные классы в одном файле" или "Хранить каждый класс в отдельном файле".>
Именование переменных
<Здесь вы можете написать примерно следующее:>
В именовании переменных данного проекта должны соблюдаться следующие правила:
[1] В именах классов используется бикапитализация.
[2] Имена функций пишутся строчными буквами, для разделения слов используется underscore ("_").
[3] Прочие, более конкретные правила.
5. Отсутствие экспертной оценки программы
Идея добавить этот пункт появилась у меня во время подобной проверки, о которой попросил меня друг. Изучив его код, я был в состоянии сократить количество переменных на треть (что давало 400% увеличение скорости обращения к базе данных), количество строк в программе примерно вдвое, кроме того, внести ещё множество улучшений с общим результатом 1000% увеличение скорости (то есть в десять раз быстрее).
Мораль? Мораль в том, что качество, скорость и защищённость вашего кода возрастёт неимоверно, если у вас под рукой всегда есть другой опытный программист, который изучит ваш код. Другой человек заметит ошибки, о которых вы даже и не знали, найдёт более простые пути решения той или иной задачи. Кроме того, ему легче найти в вашем коде места, сильно замедляющие работу PHP или представляющие собой потенциальные "дыры".
Одна из причин небывалого успеха проекта PHP, языка с открытыми исходниками, - это то, что код видело множество людей. Тысячи людей бесплатно проверяли PHP на наличие ошибок, на потенциальные сбои, на несовместимость, на скорость работы и т. д. Таким образом, к появлению новой версии PHP, код просмотрело как минимум 2-3 очень опытных программиста.
В идеале, проекты среднего/крупного масштаба просматривают, по меньшей мере, два "приглашённых со стороны" программиста. Как и в любом другом виде творчества, свежий взгляд всегда очень кстати. Однако в большинстве случаев люд обходятся одним проверяющим.
Опытным экспертом считается тот, кто способен быстро дать оценку проделанной работе и внести конструктивные предложения как по содержанию кода, так и по реализации проекта в целом.
Нередко бывает полезно составить небольшой список вопросов к человеку, просматривающему код. Вот несколько возможных вопросов:
- Какова цель кода XXX?
- Как файл XXX вписывается в остальную часть проекта?
- Каков алгоритм нейтрализации ошибок?
- Можем ли мы отследить действия среднего пользователя данной программы?
- Где пользователь программы может ошибиться?
4. "Латание" проекта
Допустим, вы написали приложение и приходите к мысли, что некоторые вещи стоит переделать. "Латать" - значит писать патчи ("заплатки") вместо того, чтобы изучить причину возникновения ошибки и устранить её.
Если вы допустили подобную ошибку и понаписали патчей, то получите криво работающий код, компромисс и в скорости и в безопасности.
Показатели недоработок проекта
Естественно, при первоначальном проектировании приложения, вы считаете, что ваш ход мыслей - самый правильный. И понять, что что-то пошло не так, вы можете в самый последний момент, когда приложение (а также его части) написано почти до конца.
Существуют два показателя того, что план проекта не состоялся:
- Слишком частая реанимация кода: программа постоянно "сваливается" и вы находите различные способы заставить её работать. Однако это никак не вписывается в изначальный план проекта. С другой стороны, хотя это и не лучший выход из положения, но в рамках уже составленного вами плана это лучшее, что можно найти.
- Слишком сложная реализация. Для решения простых задач выполняются сложные операции. В приведённом ниже примере для вывода строки используется цикл for:
<?php
$GeorgeBush = "Если вы посмотрите на рыночные тенденции, я думаю, вы увидите, что большая часть импорта в США приходит из-за рубежа";
for ($idx = 0; $idx < strlen($GeorgeBush); $idx++) {
print $GeorgeBush[$idx];
}
?>
Этот код хорошо написан; он циклом проходит по строке и правильно выводит знаменитую фразу Джорджа Буша. Правильный синтаксис, правильная вложенность. И, тем не менее, тот же результат мы могли бы получить при простом выводе целой строки (print()).
Исправление недостатков программы
Когда вы осознаёте, что в программе есть недостатки или же она попросту реализована не лучшим образом, вы предпринимаете соответствующие шаги по оптимизации кода. И в этом направлении есть масса вариантов: вы можете оставить всё как есть, можете изменить некоторые модули программы или же переписать весь проект заново.
В большинстве случаев бывает весьма полезно получить мнение независимого эксперта: просмотрев и изучив код, он сможет адекватно оценить масштаб недоработок.
Рассмотрим три категории ошибок:
- Небольшие, локализованные ошибки: иногда ошибки в вашей программе не так критичны для её функионирования и внесение исправлений не оправдывает затраченные деньги и время.
- Как исправить: в этом случае необходимо как-либо задокументировать факт наличия ошибки. Когда вы решите изменить или обновить ваше приложение, заодно вы исправите и эту ошибку. Примером ошибки подобного класса может послужить неправильная организация данных в отдельной части приложения (простой массив там, где логичнее было бы использовать ассоциативный; стек там, где дерево более уместно).
- Значительные, но локализованные ошибки: иногда же получается так, что вы действительно вынуждены переписать часть приложения. Например, если вы пишете свою ОС, в Диспетчере Окон может быть уйма ошибок, в то время как остальной код в полном порядке.
- Как исправить: всё, что вам нужно - это перепроектировать именно Диспетчер Окон (а не всю ОС). Это, наверное, самый распространённый случай в практике программирования: отдельные модули содержат ошибки, но в целом структура проекта выполнена корректно.
- Значительные, обширные ошибки: наконец, крайний случай, когда ошибки содержатся в реализации самой инфраструктуры проекта.
- Как исправить: если неверно спроектирована сама инфраструктура приложения, обычно необходима переорганизация на уровне взаимодействия различных модулей программы. Это, безусловно, самая трудоёмкая работа, отнимающая уйму времени, и к ней редко прибегают, если речь идёт о проектах, находящихся на последних стадиях реализации. Примером таких ошибок могло бы стать использование простых файлов для хранения всей информации в такой огромной поисковой системе как Yahoo.
3. Исключение конечного пользователя из процесса разработки
Ситуация: вы получили задание по разработке корпоративного приложения специально для вашей компании. Вы потратили часы на поиск и документирование тонны требований, определили стоимость проекта, поставили задачи и выполнили их. Через три месяца вы приносите законченный проект только чтобы услышать в ответ:
- "Это не то, чего бы мы хотели".
- "Наши требования изменились".
- "Да, но...".
- "Ммм, какая программа?" (Ваш изначальный заказчик уже давно уволился из компании!!!!).
Случалось ли подобное с вами?
Последнее слово в оценке вашего приложения всегда остаётся за пользователем. По определению именно он будет использовать ваше приложение (и, конечно, много раз пытаться использовать его криво). Многие программисты пишут вещи, которые сами по себе являются шедеврами, но, тем не менее, не отвечают требованиям пользователей. И часто это случается из-за одного или нескольких "недопониманий".
А недопонимания приходят, когда вы в процессе разработки игнорируете конечного пользователя. При создании приложения никогда не забывайте о будущих пользователях. Всегда держите в голове, чтО хотят получить от вас пользователи и каким именно образом программа должна достигать поставленных целей. Принципиально важно поддерживать связь с пользователем и уделять время следующим процедурам:
- непрерывная обратная связь
- создание макетов
- бета-тестирование
Непрерывная обратная связь
Бенджамин Франклин в своём "Альманахе Бедного Ричарда" писал: "Один стежок вовремя девяти стоит". То же правило применимо и в разработке приложений. Если вы действительно хотите завершить проект как можно раньше, очень важно поддерживать связь с пользователем и интересоваться его мнением о вашей программе. Что им нравится? Что им не нравится? Как можно улучшить приложение?
Создание макетов
Создание макетов программы - это целая система, включающая множество тестов и общение с пользователями и действующая на протяжении всей разработки приложения. Однако, проведение тестов на макетах - процесс достаточно длительный. И начинать его стоит с определения пользовательской группы "тестеров".
Проверяйте приложение на соответствие требованиям, но не обделяйте вниманием и прямые отзывы самих пользователей. Кроме того, многие программисты тестируют приложение только после того, как оно уже было написано и собрано. И совершают ошибку, ибо это может провалить вам весь проект: слишком велики расхождения между тем, чего хотелось бы пользователю и тем, что вы сделали. К тому же, очень часто пользователи только и понимают, что им действительно нужно, только имея перед глазами уже сколько-нибудь осязаемый образец. Короче говоря, требования пользователя к проекту постоянно меняются в ходе разработки (хотя многим программистам больше понравилось бы обратное).
Есть способ избавить себя от этой головной боли: разбить весь процесс разработки приложения на несколько этапов. По завершении каждого этапа обдумайте следующее:
- Работа, которую вы проделали на данном этапе, приносит ли она какой-либо полезный для пользователя результат?
- Остались ли какие-нибудь вещи, которые пользователь хотел бы увидеть в вашем приложении, но до сих пор в проекте не представленные?
- Будет ли задействована дополнительная функциональность, которая была добавлена на данном этапе?
- Какова "чистая прибыль" в функциональности вашего приложения?
- Проделанная вами работа улучшила или ухудшила приложение?
Как разбивать работу на этапы?
Обычно хорошей практикой считается разграничение этапов, когда завершена какая-либо значительная порция пользовательского интерфейса.
Я, например, считаю первый этап завершенным, когда готов первый вариант общего интерфейса приложения. Это происходит, когда дизайнеры определяют костяк сайта. Следующий тест пользовательского интерфейса можно проводить, когда будет готова самая общая демо-модель функциональности вашего приложения.
Затем тесты проводятся по завершении каждого "модуля" или "компоненты". Примером такого "модуля" можно назвать диспетчер пользователей в приложении или систему поиска по сайту. При тестировании я собираю свою группу тестеров (а также привлекаю других, наиболее компетентных в том, что в данный момент тестируется) и предлагаю тот же список вопросов, предложенный ранее. Это позволит вам увидеть "общий" результат вашей работы на данном этапе. Можно задать дополнительные вопросы, можно придерживаться изначального списка.
Бета-тестирование
Это самая распространённая форма тестирования, чем-то напоминающая макетирование, но обычно бета-тестирование проводится, когда проект находится на последней стадии разработки. Проект высылается определённой группе бета-пользователей; они тестируют приложение и направляют разработчику свои отзывы, комментарии и сообщения об ошибках. Бета-тестирование не так интерактивно как создание макетов и проводить его стоит как можно раньше. Однако провести бета-тестирование просто необходимо и только потом выпускать приложение в свет.
2. Отсутствие плана
В наше время многие проекты пишутся по принципу "главное начать, дальше само пойдёт". Например, один из моих первых проектов я написал, основываясь на 40-минутном разговоре по телефону. И хотя всё работало, риск того, что оно могло "упасть", был очень велик. Естественно, если бы я всё продумал и распланировал до того, как сел кодировать, риск появления сбоев в работе был бы намного меньше.
Например, абстракция в проекте была почти нулевая. То есть, если бы заказчик пожелал вместо MSSQL использовать MySQL, пришлось бы переписывать весь проект целиком. Кроме того, вся система защиты была сделана кое-как. Все данные хранились в "куке". Была ещё парочка недочётов, слишком незначительных, чтобы описывать их подробно...
В своей работе "The Mythical Man Month", посвящённой процессу разработки программного обеспечения, Фред Брукс описал оптимальный план работы над приложением следующим образом:
1/3 Проектирование: вы разрабатываете концепцию функционирования. Что включают в себя разные модули приложения (или части этих модулей)? Как они взаимодействуют? Что должно делать приложение в целом? Ответы на эти вопросы служат основой для всей разработки системы.
- 1/6 Кодирование: это то, что мы все просто обожаем делать. Проводим концепцию в жизнь.
- 1/4 Тестирование отдельных модулей и общее тестирование на ранней стадии разработки. При создании больших систем наступает такой момент: приложение ещё не готово, но уже доведено до той стадии, когда можно уже тестировать его основную функциональность.
- 1/4 Тестирование всей системы: это последняя стадия, где мы уже имеем на руках готовое приложение: теперь его надо проверять и перепроверять, чтобы оставить в нём как можно меньше "багов".
Однако на сегодняшний день мы уже счастливы, если первой части (проектированию) посвящается ну от силы 1/6 всего времени. Чаще всего программисты яростно бросаются набивать код, даже не имея в голове ясной идеи, что им нужно написать, без взвешенной оценки задачи и уж конечно без готовых путей её решения. Такое их поведение можно сравнить с попытками написания курсовой работы без составления плана.
Кодирование должно быть простым изложением того, что вы уже спроектировали, не более того. Многие программисты доходят до того, что пишут всё приложение (или наиболее мудрёные места) в псевдокоде и только потом кодируют проект на реальном языке, как, например, PHP.
Примечание: проектирование того, как будет выглядеть, работать и "думать" ваше приложение, также известно как информационная архитектура.
Этапы разработки проекта
На эту тему можно говорить бесконечно много. Достаточно перечислить: отладка, составление блок-схем, макетирование, управление проектом и определение сроков. Однако ограничимся минимальным набором общих рекомендаций.
Итак, образцовый план проекта включает в себя:
- этап изучения требований
- этап разработки
- этап тестирования
тап изучения требований
Полный анализ требований к программе - это первый шаг в планировании приложения; вы определяете, что именно вам нужно сделать. Вы подробно и точно расписываете, что будет делать программа и как она будет работать. Это один из принципиальных моментов разработки.
Определение пользовательских требований
Так как же выяснить, что именно ваш заказчик хочет от приложения? Станьте на время консультантом по определению потребностей заказчика и, в частности, постарайтесь получше узнать, что он собой представляет. Например, вас должно интересовать следующее:
- Чем они занимаются?
- Что делает их продукт лучше других (или уникальным)?
- Как они хотели бы представить себя целевой аудитории?
- Какие особенности/свойства их сайта помогут им достичь своего рынка?
Последний из перечисленных вопросов может помочь вам в расширении проекта. Вы можете предложить дополнительную функциональность, и таким образом сделать их сайт лучше? Если да, то ваш заказчик будет просто счастлив, а вы получите на руки больший проект (а он и стоит больше).
Методы исследования могут варьироваться от распространения анкет до личных бесед с людьми, занимающими в компании ключевые посты. Но какой бы способ вы ни избрали, всегда очень важно задать все вопросы, перечисленные в этой главе.
Определение технологических требований
На данном этапе вам необходимо определить, какие технологии будет использовать ваше приложение. Ключевой вопрос: сможем ли мы выполнить требования заказчика, располагаем ли мы техническими и технологическими возможностями для этого? При этом вы должны учитывать язык, на котором реализовано приложение, ОС платформы, скорость сервера и соединения с ним и многое другое.
Этап разработки
Итак, спецификации готовы. На данном этапе вы должны решить, как будет реализовано приложение, что когда в нём будет происходить. Можете сделать даже несколько набросков в псевдокоде, если какие-либо места покажутся вам слишком запутанными. Другими словами, вам будет нужно:
- смоделировать приложение
- проиллюстрировать его
- сделать наброски в псевдокоде
Смоделировать
Перед тем, как засесть за кодирование, вам необходимо продумать, как различные части вашего приложения будут общаться друг с другом.
В качестве примера рассмотрим скрипт с формой для отправки сообщения. Распишем все возможные действия, которые пользователь может совершить при заполнении такой формы. На высшем уровне абстракции приложения действий всего два: пользователь либо отправил данные, либо не отправил данные.
Если данные не отправлены, то нам нужно отправить пользователю страницу с пустой формой. В противном случае, необходимо (ещё раз) произвести одно из двух следующих действий:
- если полученные данные корректны (т. е. соответствуют критериям корректных данных), то нужно отослать сообщение адресату, а пользователю отправить страничку с благодарностями.
- Иначе выдаём сообщение об ошибке с указанием того, что было не так и предоставляем возможность всё исправить.
Проиллюстрировать
Ниже представлена очень несложная диаграмма, иллюстрирующая работу приложения. Диаграмма выполнена в Microsoft Visio(r) и идеально подходит для приложения по простой отправке сообщений.
Однако когда речь заходит о более сложных проектах, всегда полезно иметь под рукой специальный инструмент для построения диаграмм. Вот три, пользующиеся наибольшим успехом:
Dia,
Visio и
UML.
Псевдокод
Псевдокод (т. е. код, только описывающий работу приложения, и не соотнесённый с каким-либо языком) - весьма распространённая практика среди разработчиков. Обычно к нему прибегают, когда сталкиваются с какими-нибудь запутанными ситуациями или когда общая картина работы приложения неясна. Я тоже считаю, что псевдокод очень полезен при определении интерфейсов приложений, в частности, когда созданные мной интерфейсы используются другими разработчиками. Псевдокод помогает найти наиболее простые, но достаточные методы общения с той или иной частью приложения.
Для примера вернёмся к нашему скрипту для отправки сообщений:
if (formSubmitted) {
valid_name(name) or error() and output_form();
valid_email(email) or error() and output_form();
valid_message(message) or error() and output_form();
message = name & email;
send_mail(email, message);
print "Thank you for your feedback";
} else {
print "Send us feedback";
formelement(name);
formelement(email);
formelement(message);
}
Этот код определяет основную структуру скрипта и демонстрирует все необходимые элементы. Код, конечно, не является рабочим скриптом PHP (хотя и мог бы им быть). Миссия псевдокода - определить задачи и, возможно, подвести под них теоретическую основу. Уровень абстракции псевдокода зависит только от вас. Лично я склоняюсь в сторону менее абстрактного кода. Всё зависит от того, насколько комфортно вы чувствуете себя в программировании.
Наконец, когда вы распланировали всё приложение, вы можете начать кодировать: вам уже известны все шаги, которые будет нужно предпринять; известно и то, что в конечном счёте нужно получить.
Этап тестирования
Один из наиболее важных этапов разработки приложений, до которого очень часто не доходят руки, - это (окончательное) тестирование. Нередко под давлением менеджера или из-за нехватки времени фаза тестирования сокращается до минимума или пропускается вовсе. Ну, а приложение считают "условно годным".
Скажем честно: программисты ненавидят тестирование. Это, наверное, самое нудное и раздражающее занятие в программировании. Тестирование - это часы и дни погони за неизвестными багами, отладка, проверка всех возможных комбинаций и ситуаций, - и всё для того, чтобы убедиться, что программа в большинстве случаев работает правильно. И в довершение всего, код без багов вы всё равно НЕ ПОЛУЧИТЕ! Всё равно, что-нибудь да ускользнёт от вашего внимания. И вещи, которые (вы уверены!) не должны случиться, обязательно случаются.
Регрессивные испытания
Редко кто ограничивается одной версией приложения: большинство из них находятся на постоянном, бесконечном обновлении. Убедитесь, что при добавлении новых функций не искажается работа старых функций: функций, от которых пользователи в некоторой степени зависят. Для этого вам необходимо узнать о таком понятии как
Цикл Регрессивных Испытаний. Так называют тесты, которые подтверждают, что функциональность настоящей версии продукта не будет нарушена в следующем релизе. Каждая новая версия самого проекта PHP проходит серию регрессивных испытаний и поэтому при добавлении нововведений прежняя функциональность полностью сохраняется. Таким образом, достигается не только обратная совместимость всех версий PHP (т.е. новые возможности добавляются, но старые скрипты продолжают корректно работать), но и подтверждается то, что нововведения не нарушают работу всех остальных функций (и даже никак не изменяют их поведение).
Нагрузочные испытания
Ура, ваше приложение отлично работает с одним пользователем: все вроде бы получается и происходит с огромной скоростью. А что будет, если с приложением одновременно заработают 20 пользователей? А 30? Или даже 100? Насколько быстро работает ваш проект теперь? Не стал ли выдавать странные ошибки?
Какие тесты бы вы не проводили, обязательно убедитесь, что ваш проект корректно работает при больших нагрузках и в изменяющихся условиях.
Но это не означает, что всю работу по тестированию можно отфутболить пользователям (просто выложить всё на сайт и ждать отзывов и писем о найденных ошибках, ну вы знаете...). Проведите бета-тестирование (как описано в предыдущей главе). Кроме того, существуют специальные сервисные программы, эмулирующие большой наплыв пользователей. На ум сразу приходит AB от Apache. AB или Apache Benchmark (Анализатор Производительности Apache) делает заданное количество запросов к странице и выдаёт количество успешных запросов, количество неудавшихся запросов, среднюю скорость и т.д.
Настоятельно рекомендуется проверять ваши web-проекты с помощью AB (если, конечно, вы приняли бесконечно верное решение использовать Apache-сервер). Так вы сможете обнаружить и оптимизировать страницы, "съедающие" память, или загружающиеся слишком долго.
(Также, подумайте о приобретении замечательной утилиты по кэшированию от Zend - The Zend Cache).
1. Сорванные сроки
Ошибка номер один, испокон веков и по сей день преследующая программистов - полное неумение рассчитать временные затраты.
Сильно не пугайтесь - все мы оптимисты. Вполне естественно полагать, что реализация проекта займёт ровно столько времени, сколько она должна занять. И ни один сбой в работе не берётся в расчёт. Более того, нам и в голову зачастую не приходит, что у нас могут возникнуть ну хоть какие-нибудь проблемы с реализацией чего-либо.
Итак, давайте возьмём себя в руки, подавим этот инстинкт и рассчитаем временные затраты по-другому. Из моей личной практики: когда я берусь за новый проект, я учитываю все факторы, которые только могу вспомнить, оцениваю время для каждого, всё суммирую. Затем полученный результат я удваиваю. Неаккуратно? Я бы так не сказал: заказчики очень довольны, когда вы приносите готовый проект до положенного срока (и наоборот: если вы опаздываете, они совсем не рады этому).
Такой "перестраховочный" метод определения временных затрат помогает мне проделать качественную работу по проекту и уложиться в указанные мной сроки. Но, конечно, здесь всё зависит от точности первоначальных расчётов.
Каждый программист недооценивает (намного реже - переоценивает) время, необходимое на разработку проекта. При этом каждый имеет свой постоянный коэффициент допуска возможных ошибок. Для меня это 2, для кого-то это 1.5, а у кого-то и все 3. Вся хитрость состоит в том, чтобы найти свой личный коэффициент и впоследствии вносить поправки в расчёты.
Недооценка временных затрат на разработку проекта чревато следующими последствиями (слишком хорошо знакомыми мне):
- Вы принесёте в жертву проекту всё выше личное время. День и ночь вы будете проводить в работе над проектом, а для отдыха времени не останется (не то чтобы я хочу сказать, что девелоперство это плохо, но программирование в режиме нон-стоп - это невыносимо).
- Вы будете торопиться. Это означает, что вы будете заботиться не столько о читаемости, защищённости и скорости работы проекта, сколько о том, чтобы выложить готовый проект в срок.
- В спешке вы пропустите такие важные этапы, как проверка кода или отладка. Это может не понравиться заказчику и добавить вам работы.
- Может быть, вам придётся прибегнуть к сторонней помощи. Это стоит денег и далеко не всегда помогает (согласно закону Брукса, особенно при разработке капитальных проектов).
Обязательно выделяйте достаточно времени на длительную отладку (столько же, сколько вы выделяете на весь этап кодирования или даже больше) и не менее 1/3 всего времени на планирование. Эти два этапа являются определяющими этапами для всей разработки проекта.
SterlingWonderfulToyland.com: Пример
Рассмотрим в качестве примера простой проект: сайт SterlingWonderfulToyland.com. Это самый обычный интернет-коммерческий сайт. Его задача - продажа через интернет игровых приставок и видеоигр по сниженным ценам; количество наименований ограничено.
Будучи приверженцем старых и проверенных способов продаж, владелец сайта Джон Джузеппе хочет пригласить посетителей зайти на склад и самим выбрать товар. Таким образом, сайт можно логически разделить на две части:
- сама система заказов;
- html-часть сайта, приглашающая посетителей зайти на склад и дизайн самого склада.
При планировании я бы составил две отдельные сметы на эти части. Потом бы я сложил два полученных срока, накинул сверху неделю на сборку двух частей и вывалил заказчику конечный срок (для всего сайта, как если бы никакого разделения не было).
Html-часть
Для простой, статической части сайта я бы взял за основу время, необходимое для вёрстки одной страницы в каком-нибудь редакторе, например Macromedia Dreamweaver(c). И поскольку динамические элементы в данной части отсутствуют, для определения общих временных затрат можно просто перемножить время вёрстки страницы на количество страниц (при условии, что дизайн страниц уже разработан). Затем я бы удвоил полученный результат (поскольку мой личный коэффициент 2). Вообще, этой части не требуется действительно тщательное планирование (посудите сами: дизайнер даёт вам схему того, как всё должно выглядеть, а ваша задача - наверстать страницы в Dreamweaver-е).
Примечание: добавьте ещё интервал примерно с 1/3 всего времени, оно уйдёт на сборку проекта. Например, если вы запланировали разработку скрипта для проверки кредиток за 9 дней, то добавьте ещё 2-3 дня.
Система заказов
Для динамической части распределение регламента - вещь достаточно сложная. Что может вам здесь помочь - я думаю, это просто разбить задачу на несколько простых составляющих (как, например, операции с кредитными карточками, моментальный заказ за один клик, каталог продуктов, система управления этим каталогом и т. д.) и произвести расчёт без коэффициента. Потом складываю и умножаю сумму на свой личный коэффициент допускаемых ошибок. Только после этого я вычисляю треть времени всей разработки и добавляю её. Всё добавочное время уделяю отладке и тестированию с тем, чтобы предоставить заказчику корректно работающий проект.
Примечание: добавьте ещё 1/3 всего времени на планирование, как и в части простого html.
Как преподнести такие сроки заказчику
То, что вы сумели правильно рассчитать сроки реализации проекта, вовсе не означает, что заказчику они понравятся также как и вам. Иногда, указав слишком большие сроки, вы теряете заказ. Ваши конкуренты дадут более привлекательные сроки лишь для того, чтобы получить этот заказ, а потом просто-напросто не выдержат их. Так что же нужно сделать, чтобы и заказчик был доволен, и вы могли честно выдержать указанные сроки?
Здесь наступает момент, когда нужно правильно преподнести заказчику свои услуги и свои сроки... Какие сроки вы можете предложить? Что делает ваши сроки выполнимыми и почему поставлен именно такой срок, а не какой-либо другой? Нередко помогает мысленный разговор с заказчиком: представьте себя на его месте и подумайте, чего было бы справедливо ожидать от потенциального исполнителя заказа?
Вот некоторые из аспектов, которые показались мне наиболее важными:
- Делайте качественную работу. Другие могут выполнить работу немного быстрее вас, но ваш козырь в том, что вы сделаете всё правильно и корректно, что, в конечном счете, сэкономит заказчику время и деньги (особенно сильный аргумент, если вы получаете почасовую оплату).
- Выдерживайте сроки. Вы даёте сроки и собираетесь выдержать их. Другие могут дать более привлекательные сроки, но далеко не собираются их выдерживать.
- Предоставьте рекомендации. Ваши предыдущие проекты говорят сами за себя. Соберите с ваших довольных заказчиков различные отзывы и рекомендательные письма с тем, чтобы впоследствии предъявить их потенциальному начальнику/заказчику.
- Будьте тщательны! Проверьте орфографию и грамматику во всех ваших выкладках. Добавьте схемы и графики, подробно и ясно опишите всё, что вы планируете сделать и сколько времени займёт выполнение каждой из поставленных задач.
- Предлагайте несколько вариантов. Не ограничивайтесь простым заявлением о том, сколько времени, по вашему мнению, займёт выполнение какой-либо задачи. Дайте несколько возможных вариантов. Как будут развиваться события по самому оптимистичному сценарию? А как по самому пессимистичному?
- Указывайте наикратчайшие возможные сроки. Чем больше срок выполнения, тем больше вероятность того, что он не будет выдержан.
Если вы всё же указали слишком короткие сроки
Если же подобная неприятность всё же случилась и вы, мягко скажем, не успеваете, у вас всё ещё есть несколько возможных вариантов действий (о которых не очень часто вспоминают в подобных ситуациях):
- Поддерживайте связь! Постоянная связь программиста с заказчиком - вряд ли можно придумать что-либо важнее. Постоянно информируйте заказчика о том, что вы в данный момент делаете по проекту. Если вдруг вам понадобится увеличить срок, сделать это будет намного легче, чем, если бы от вас всё это время не было никаких новостей.
- Продемонстрируйте заказчику/начальнику готовую часть сайта. Демонстрация уже сделанной работы нередко облегчает вам переговоры по выделению дополнительного времени. Покажите, что вы работаете над проектом.
- Найдите стрелочника. Возьмите всю ответственность за опоздание на себя, но не забудьте указать различные внешние факторы, помешавшие выполнить заказ в срок.
- Срезайте углы. Это отвратительно и неприятно, но если вы действительно не укладываетесь, придётся срезать несколько углов и выдать заказчику рабочую версию продукта с подробным списком всех багов. Потом, во время тестирования или позже вы вернётесь к ним и исправите.
- WYSIWYG. При работе над web-проектами я предпочитаю писать html-код руками, ибо очень не доверяю графическим html-кодерам. Однако если ваш проект трещит, то стоит рассмотреть и такой вариант.
Резюме
- ПРОГРАММИРОВАНИЕ МЕТОДОМ "ВЫРЕЗАТЬ-ВСТАВИТЬ": НЕВЕРНЫЙ ПОДХОД. Обычно копирование чужого кода - не очень хорошая затея. Здорово будет изучить техники и алгоритмы чужого кода и при случае внедрить в свой проект. Или же использовать готовые библиотеки. Но ни в коем случае не просто слепо копировать чужой код.
- ОТСУТСТВИЕ В ПРОЕКТЕ ТЕХНИЧЕСКИХ ДИРЕКТИВ. Составление подобных директив необходимо при разработке любого проекта. Директивы позволяют вести проект организованно, в единых для всех стиле и документировании и таким образом, позволит избежать многих возможных "недопониманий" в будущем.
- ОТСУТСТВИЕ ЭКСПЕРТНОЙ ОЦЕНКИ ПРОГРАММЫ. Пусть кто-то обязательно просмотрит ваш код. Свежий взгляд на проект обязательно выявит такие вещи как неоптимальные SQL-запросы, неправильное применение синтаксиса языка, слишком сложные решения, "дыры" и другие недостатки.
- "ЛАТАНИЕ ДЫР". Серьёзные недостатки "патчами" не исправить. Если вы видите, что что-то спроектировано неправильно (или не совсем правильно), то в большинстве случаев намного лучше перепроектировать и переписать этот модуль. "И да воздастся вам" впоследствии.
- ИСКЛЮЧЕНИЕ КОНЕЧНОГО ПОЛЬЗОВАТЕЛЯ ИЗ ПРОЦЕССА РАЗРАБОТКИ. Никогда не делайте этого. Именно пользователи будут судить о пригодности вашего приложения, когда всё будет сказано и сделано. Привлечение пользователей в сам процесс разработки приложения опять же позволит избежать многих возможных "недопониманий".
- ОТСУТСТВИЕ ПЛАНА. Забудьте про "главное начать, дальше само пойдёт". Выделите время для тщательного планирования. Каждый план проекта должен обязательно включать в себя этапы Изучения Требований, Разработки и этап Тестирования.
- СОРВАННЫЕ СРОКИ. Выделяёте достаточно времени на реализацию проекта! В своём стремлении уложиться в нереальные сроки, вы только повышаете свои шансы допустить 20 предыдущих ошибок. Не позволяйте себе потеряться во времени.
Об авторе
Стерлинг Хьюз (Sterling Hughes) - независимый разработчик веб-сайтов, занимается созданием динамических веб-приложений для некоторых крупнейших мировых компаний. Участвовал в создании cURL and SWF расширений PHP с открытым исходным кодом. Его книга, "Настольная книга программиста PHP" (The PHP Developer's Cookbook), была издана в октябре 2000 года издательством "Sams publishing".