четверг, 14 июля 2011 г.

Разработка больших систем. Часть 1. Внесение изменений.

Очень давно ничего сюда не писал.
А сейчас вот решил написать на другую тему...
Пожалуй, даже более близкую мне - про технологию разработки больших программных систем.

По роду своей работы, я постоянно поправляю молодых программистов, рассказываю им о допущенных ими ошибках, объясняю, как надо писать, чтобы такого не повторялось. В какой-то момент стал записывать эти пояснения. И вот сейчас решил опубликовать.
Кстати, буду благодарен за конструктивную критику. Особенно от тех, кто также профессионально занимается разработкой больших программных систем.

Начну с первой (исторически) части - как делать (и как НЕ делать) изменения в большой программной системе.



Внесение изменений

Как вносить изменения (или исправления) в сложную систему.

Неверный способ

1) Найти место для внесения изменения
2) Внести изменение
3) Проверить работает ли
4) Если нет, перейти к пункту 2

Почему этот способ не работает

Нет анализа исходной точки

В связи с тем, что нет анализа того, как система работает сейчас (до изменения), нет представления о том, как код изменения будет жить совместно с уже существующим кодом во всех возможных ситуациях.
Образный пример – мы имеем автомобиль, и он не ездит. При таком подходе, обычно рождаются решения вроде: приделать к автомобилю сбоку новый двигатель и зацепить цепной передачей за существующие колеса.

Пример из жизни: Было замечено, что некий продукт работает слишком медленно, если число отображаемых объектов превышает 6000. Разработчик не разобрался в существующем коде и приделал к нему кэш. При этом, поскольку кэш там уже был, получилось двойное кэширование, которое никак не повлияло на скорость работы, однако усложнило систему и внесло в нее ряд нетривиальных багов.

Когда анализ исходной точки не проводится, система начинает покрываться корявыми «наростами». А потом эти наросты – новыми наростами. В какой-то момент невозможно понять что система делает вообще.

Нет анализа конечных целей

Перед реализацией любого изменения необходимо четко ответить на вопрос – зачем оно делается. Ответы типа «так сказал заказчик» являются неверными. В случае нетривиальной задачи, этот ответ необходимо показать заказчику, чтобы убедиться, что он хотел именно этого.

Пример из жизни: Заказчиком была поставлена задача добавить к системе полнотекстовый поиск. Закзачик сделал акцент на том, что этот поиск позволит искать внутри описания объектов. При этом он не отметил, что поиск , работает в несколько раз быстрее, чем ранее применявшееся фильтрование в памяти, а также , что поиск может быть использован ВМЕСТО фильтрования, что повысит скорость работы системы в несколько раз. Пропуск фазы анализа конечной цели привел к тому, что полнотектовый поиск был приделан нарядус фильтрованием. И, хотя и позволил искать внутри описания объектов, никак не увеличил скорость работы. Именно это неверное решение привело к возникновению в будущем задачи про 6000 медленных объектов (см выше).

Нет гарантии, что изменение будет работать всегда

Изменение, внесенное согласно неверному процессу, как правило, проверяется только один раз и при идеальных условиях. Процесс не гарантирует, что оно будет работать в другом контексте (скажем, при переключении каких-то галочек, при изменении количества или типов объектов, если у пользователя изменятся права и т п.)

На самом деле, этот процесс гарантирует в точности обратное.

Образный пример – вы приносите в сервисный центр сломанный аудиоплэйер. Который включается через раз и иногда зависает. Ждете неделю. Потом вам его возвращают с диагнозом «неисправность не обнаружена». И все потому, что девочка-приемщик включила плэйер, подержала его включенным 2 минуты, и он за это время не завис.

Правильный способ внесения изменений

Вероятно, уже понятно, что неверный способ внесения изменений (также известный, как «запинывание») приводит к очень плохим результатам. Как минимум, все изменения , внесенные таким образом, потом вычищает и переписывает более опытный специалист, как максимум, проект убивается и становится не управляемым.
Теперь опишу правильный способ внесения изменений.

ДАО изменений

Вкратце, чтобы сделать изменение, вы должны точно знать что вы имеете сейчас (точка А), что хотите иметь в результате(точка Б) и как убедиться, что ваше изменение делает переход из точки А в точку Б и не делает ничего больше.

Процесс выглядит так:

1) Понять цели вносимого изменения
2) Описать, как должна работать система после внесения изменения
3) Понять, как система работает сейчас, какие аспекты она учитывает
4) Предложить метод перехода из состояния «сейчас» в состояние «надо»
5) Встать в позицию критика и объяснить, почему предложенный способ – полное говно
6) Вернуться в конструктивную позицию и перейти к пункту 4

На третьей-четвертой итерации вам не удастся выполнить пункт (5). Значит, пора переходить к реализации:

7) Реализовать выбранный метод, снабдив его комментариями, описывающими ваш ход мысли – почему такое изменение приведет к нужным результатам
8) Проверить реализацию с учетом всех аспектов, которые могут влиять на изменение (разные входные данные, секьюрити и т п). Здесь иногда надо написать юнит тест (если напрямую проверить не получается). А иногда, если требуется много проверок, можно написать инструкцию для тестера – включить в нее все действия, которые тестер должен выполнить.
9) Важно: если при проверках все идет не так, как было вами предсказано, НЕОБХОДИМО понять, почему это так. Ни в коем случае не списывать на случайность.
10) Задача считается сделанной, если вы видите, что переход в нужную точку состоялся и прекрасно понимаете как все работает после этого изменения.

Комментариев нет: