20800

Опыт выживания в проекте-камикадзе Д.Н. Топорков

Практическая работа

Информатика, кибернетика и программирование

Состояние после года разработки: директор по ИТ заказчика, курирующий этот проект уволен, генеральный директор компании-разработчика понижен акционерами до зама, главный архитектор лишился доверия начальства

Русский

2015-01-19

5.33 MB

2 чел.

Опыт выживания в проекте-камикадзе Д.Н. Топорков 

План:

- Безнадёжный проект

- Состояние дел (команда, процесс, код, заказчик, результат)

- Делаем из мухи слона (описание проекта)

- Спасение проекта (команда, процесс, код, заказчик, результат)

Название моего доклада было навеяно уже довольно старой (уже 13 лет), но до сих пор актуальной, книгой Эдварда Йордона "Путь камикадзе или как разработчику программного обеспечения выжить в безнадёжном проекте". Пересказывать не буду, но очень рекомендую к прочтению. Согласно Йордону под безнадёжным проектом (death march) понимается такой проект, параметры которого отклоняются от нормальных значений, по крайней мере, на 50%. При этом применительно к программным проектам он выделяет такие параметры как:

1) сроки (план проекта был сжат более чем наполовину по сравнению с нормальным расчётным планом);

2) количество разработчиков уменьшено более чем наполовину, по сравнению с действительно необходимым для проекта данного размера и масштаба;

3) бюджет, и связанные с ним ресурсы урезаны наполовину;

4) требования к функциям, возможностям, производительности и другим техническим характеристикам

вдвое превышают значения, которые они могли бы иметь в нормальных условиях.

Проект, про который я буду рассказывать, подходит по каждому из этих параметров, чтобы назваться безнадёжным:

а) ТЗ было согласовано через месяц, после истечения договорного срока проекта

б) на проект были набраны абсолютно новые люди + для этих людей используемые технологии были новыми

в) изначальная постановка была провальной (вот есть некий проект СОИ УР и нужно его "немного" доработать, соответственно и цена была как "доработки").

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

Состояние после года разработки: директор по ИТ заказчика, курирующий этот проект уволен, генеральный директор компании-разработчика понижен акционерами до зама, главный архитектор лишился доверия начальства, ведущий разработчик команды спился и был уволен. Ну и самое неприятное для лично меня: я был вызван к начальству и со словами "кроме тебя - некому" проект был поручен мне с задачей: вот тут есть список из 89 замечаний, но работы "всего" на 2 месяца - надо доделать, пройти комплексные испытания, опытную эксплуатацию и к Новому Году ввести проект в промышленную эксплуатацию. При этом проект уже был безнадёжным по всем параметрам Йордона. Не могу дать дельного совета как поступать в подобной ситуации: лучший вариант, наверное, не ввязываться в такую авантюру, несмотря на то, что программисты по натуре оптимисты. Но для наёмного работника такой вариант неприемлем и отказаться нет возможности. Поэтому заранее предупредив начальство, что 2 месяца это нереальный срок и навскидку работы минимум на полгода, приступил к работе.

Начал я с оценки состояние дел. К тому времени имелось в наличии:

1) команда: аналитик, разработчик (бывший С# разработчик, которого пересадили на java) и тестировщик (с абсолютно нулевым опытом тестирования).

2) процесс: налаженный continious integration (система контроля версий svn с одной веткой разработки, без ветвлений, билд-сервер Team City с одной сборочной конфигурацией, запускающий юнит-тесты при сборке (правда сборка занимает минут 15, но это переживаемо). Требования заказчика заводятся в TFS, назначается исполнитель, резолвится исполнителем, тестировщиком заводятся баги на разработчика, баг исправляется и резолвится на тестирощика для проверки. Среды разработки две: Eclipse и IntellyJ IDEA (не очень удобно, но, в принципе, терпимо). То есть в общих чертах всё как бы есть, единственное что непонятно из TFS какая задача сколько времени потребовала и куда был потрачен год разработки. Восстановить историю что и когда по коммитам в svn не представляется возможности, так как один из разработчиков в принципе не писал комменты к коммитам, а другой (который ведущий) лучше бы не писал, а то в описании одно, а в коммите до чёрта совсем непонятных изменений. Тестирование только ручное, только хардкор, только ПМИ.

3) код: если говорить одним словом, то хаос. Нет, внешне всё выглядело достаточно структурировано и логично - чётко выделены слои контроллеров, сервисов, дао и прикладных объектов. НО: куча кода относилось к "родительской" системе, которая была реализована немного для другой задачи и вообще была в каком-то роде демонстрационной системой (там использовался движок JBPM для добавления возможностей потенциального управления бизнес-процессами, использовался толстый клиент, а значит никакой визуальной части на сервере; толстый, потому как когда мы разрабатывали эту систему на дворе был 2008 год и несмотря на это заказчик в принципе не понимал, как система может быть в браузере - в браузере-же интернет!!!). Кроме того, когда проект успешно профукивал свои первые сроки сдачи, начальство догадалось до такой "гениальной" вещи, как время от времени "усиливать" проект разработчиками из других команд... В итоге выходило так, как в пословице "у семи нянек дитя без присмотра": разрозненные модули, иногда даже с дублирующимся кодам (копипаст - зло!) и, зачастую, с дублирующейся функциональностью... и НИ КАПЛИ ОТВЕТСТВЕННОСТИ... У каждого свой стиль, свой подход и своё казино со всем прилагающимся. Зато становится совершенно очевидно, почему Вавилонскую башню так и не достроили...

4) заказчик: сказать, что заказчик относился к нам негативно, это всё равно, что ничего не сказать, так как считали они нас полными чудаками: авантюристами, дилетантами и даже немного вредителями. И если взглянуть на тот результат, который им предоставляли, то с ними нельзя было не согласиться.

5) результат: чем дольше я работаю программистом, тем больше прихожу к мнению, что если код простой и понятный, то у него есть шанс быть работающим. Если код напоминает воплощение картин Сальвадора Дали - он в принципе работать не может.

Укажу только несколько вопиющих моментов:

- старт приложения на Jboss занимал примерно 10-15 минут. То есть стартовал сервер приложений и первые попытки обращения к системе можно было делать не ранее, чем через 15 минут. Для системы, в ТЗ которой было гордо написано 24/7, как вы понимаете, было неприемлемо.

- некоторые операции в системе занимали очень долгое время (импорт справочников - 25 минут, очистка устаревших данных - больше часа, закрытие ворот тоже около часа, импорт данных 3-4 часа). При этом база при выполнении этих процессов блокировалась, и система оказывалась в принципе недоступна. Учитывая, что часть этих операций отнюдь не одноразовые, а ежедневные, то было это также абсолютно неприемлемо.

- в ТЗ заказчик вставил вполне невинную с первого взгляда для неопытного аналитика фразу: "время реакции системы на действия пользователя не должно превышать 5 секунд". Вот такой вот вроде как логичный показатель назначения... Ага... Только на тот момент время отображения в системе данных могло занимать и секунду, и 5 и 30 и минуту и 5 минут... То есть ткнулся не в то место системы и подвис. А на дворе 2013 год. Суперкомпьютеры, гигагерцы, веб-технологии, мегаархитектуры... А тут интерфейс пользователя тормозит так, как даже в дос-е не тормозил...

В общем, не проект, а страшный сон программиста. 

Немного расскажу о проекте, или как из мухи сделать слона:

задача системы была очень проста, но ей не повезло, и она разрабатывалась на замену уже существующей и работающей системе. Система была старой, децентрализованной и не могла интегрироваться с новыми системами, разработанными за годы её работы. 

Ну и, естественно, что с самого своего рождения очень большое влияние оказал так называемый эффект второй системы: когда пишется вторая система начинает казаться, что знаешь, какой должна была быть первая и думаешь, что теперь-то уж точно получится именно то, что надо. А ещё надо сынтегрироваться с теми, и с теми, и с теми, и ну и так далее...

Прикладная задача: обеспечить возможность участникам рынка электроэнергии подать заявку на максимальное планируемое почасовое потребление регулятору рынка - компании Системный Оператор, чтобы потом передать эти данные в другую расчётную систему, которая рассчитает режим на следующий день.

То есть, чтобы оптовый потребитель подал 24 цифры своего объёма планируемого потребления электроэнергии. Всё просто и понятно. Дьявол в деталях: 

- цифр не всегда 24, для Востока подача заявок на потребление подаётся получасовками, то есть 48

- подавать эти цифры можно в строго регламентное время, называемое воротами

- тонкий клиент с поддержкой ВСЕХ основных браузеров начиная от IE 7, заканчивая Safari. (От IE 7, слава богу, удалось отмазаться в связи с окончанием поддержки Windows XP, но остались с позволения сказать браузеры IE 8, 9, 10, 11)

- безопасность: для пользователей СО доменная аутентификация, для участников рынка - по логину/паролю либо по сертификату ЭЦП. Причём при подаче заявки необходимо подписать цифры ЭЦП участника рынка, чтобы он потом не смог отмазаться, что и цифры не те, и отправлял не он. 

- гетерогенный транспорт: внешние пользователи (участники рынка) на https, внутренние, тоже https плюс КИТС (корпоративная информационная транспортная система) на базе WebSphere MQ

- расположение как в корпоративном сегменте сети, так и в так называемой демилитаризованной зоне сети, к которой имеют доступ участники рынка. Это означает как минимум 2 взаимодействующих сервера (внутренний и внешний) и необходимость взаимодействия между корпоративным и буферным сегментом через шлюз КИТС-2 (шлюз http-MQ)

- я уже упоминал интеграцию с различными системами: СБР, ЕСС, Модес-Терминал, ИСП (иерархическая система прогнозирования), Барс в части как предоставления данных, так и получения данных из этих систем.

- понятное дело, мониторинг, журналирование, отправка уведомлений по электронной почте, форматно-логический контроль, всевозможные типы отчётов (в csv, в xml, в Excel), кластеризация, балансировка нагрузки и прочего-прочего чего напридумывала богатая фантазия заказчика

- при этом отдельным требованием заказчика нужно было предоставить открытое API для всей функциональности системы и примеры его использования в исходных кодах.

Вот так из мухи делают слона. Простая с виду задача превращается в монстрообразную мегасистему, интегрирующуюся с набором таких же мегаситем. Вот схема итоговой архитектуры системы по приёму 24 цифр планируемого потребления: *ссылка на схему архитектуры в конце документа

Но вернёмся к спасению утопающих силами самих утопающих:

1) команда: в команду был добавлен ведущий разработчик с полномочиями руководителя проекта. Почему я говорю с полномочиями, потому как без подобных полномочий что-то изменить в проекте просто невозможно. Люди уже пали духом, и они негативно относятся к любым изменениям. Люди вообще в принципе не хотят что-то менять. В самом начале пришлось почти каждому доказывать свои полномочия, но это очень просто: вместе с полномочиями руководитель награждается ответственностью за проект, а так как ответственность за проект на себя брать никто не хотел, то полномочия мои принимались как меньшее из зол. Если момент с ответственностью интересен кому-то, то очень рекомендую посмотреть запись с доклада конференции DUMP этого года "Почему из нас не получилось хороших тимлидов" - там докладчики достаточно хорошо осветили тему ответственности.

2) процесс: к continious integration претензий у меня не было, а вот к управлению задачами - были. Изначально поняв, что за отведённые пару месяцев что-либо исправить физически нереально, я озаботился тем, чтобы каждый член команды чётко знал что, когда и как он должен делать. Всё очень просто: в используемой системе управления требованиями (а у нас это TFS) требования не назначаются на исполнителя, а создаётся задача, на реализацию либо требования целиком, либо, если выполнение занимает более 16 часов, несколько задач. Задачам присваивается приоритет, исполнитель и срок выполнения, оцениваемый исполнителем (!). Это важно, ибо оценки, спускаемые сверху обычно никак не соответствуют действительности. После выполнения всех подзадач у требования, оно резолвится и автоматически переназначается тестировщику. Если тестировщик закрывает требование - оно автоматически переназначается на аналитика. Если бага переоткрывается - автоматически переназначается на разработчика. В общем - всё автоматически и ничего личного. У каждого члена команды есть свой отчёт для ежедневной работы: у разработчика список задач на неделю, у тестировщика - список зарезолвленных требований и багов, у аналитика - список открытых и закрытых требований, по которым можно актуализировать документацию и обсуждать с заказчиком.

В итоге, все работы по проекту учтены и зафиксированы актуальные трудозатраты. Плюс разработчик учится давать оценки задачам. Плюс все участники команды и, что немаловажно, руководство в любой момент могут посмотреть ретроспективу, перспективу и актуальные данные процесса разработки и тестирования.

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

Проблемы можно разбить на несколько областей локализации: проблемы с базой, с реализацией и с интерфейсом. Как правило, проблемы приводили к падению производительности и требовали поиска решений по оптимизации на каждом из этих уровней. Кстати, одним из лучших пособий по тому, как следует работать с кодом в такой ситуации, когда система достаётся практически в унаследованном виде, я считаю книгу Майкла Физерса "Эффективная работа с унаследованным кодом", которую всем рекомендую.

Начнём с базы:

- Как я уже упоминал, некоторые операции с базой данных были очень и очень длительными. Операций таких было несколько, и они блокировали базу данных почти целиком. Поиск в интернете выдал обходное решение проблемы, подходящее только для MS SQL: поменять стратегию работы с транзакциями с дефолтной READ_COMMITED на SNAPSHOT_ISOLATION. То есть средствами БД гарантировать, что отдельные длительные операции не будут блокировать БД для других операций. Детально не буду на этом останавливаться: в интернете можно найти как плюсы, так и минусы подобного решения. Главное в этом решении - оно позволило нам выиграть время для оптимизации без переписывания всего, что тормозит.

- Работа со справочниками заслуживает отдельного рассказа. Как я уже упоминал импорт справочников занимал 25 минут. Анализ реализации показал, что при импорте сперва данные записывались во временные таблицы БД, а потом доставались из них запросами с целью восстановления связей между объектными сущностями. Я могу предположить, что ещё временные таблицы были нужны для того, чтобы гарантировать, что все поля справочников уложатся в соответствующие таблицы, то есть проходят все ограничения. Отказ от временных таблиц в пользу коллекций в памяти, плюс выполнение форматных проверок на этапе парсинга этих данных сократило время импорта справочников до 6,5 минут, при этом 5 минут занимает получение этих данных от веб-сервиса ЕСС. Справочники содержат иерархические и сложносвязанные объекты, причём зачастую нам нужно либо объекты определённого уровня иерархии начиная от указанного, но не дочерние, а внучатые или наоборот. Иногда также приходилось строить длинную связь между сложносвязанными не напрямую объектами. Это всё существенно замедляло работу со справочниками, особенно иерархические запросы. Тогда было принято решение о частичной денормализации базы: для связи иерархических объектов была добавлена таблица избыточных связей, где записывались идентификаторы объектов и их уровень в иерархии (для любого объекта были записи со всеми дочерними любого уровня). В итоге для любого объекта одной связью можно было выбрать дочерние или родительские нужного уровня. Работа запросов с деревом объектов была ускорена в разы за счёт одного запроса вместо нескольких. С введением дополнительных таблиц связей между напрямую несвязанными таблицами пусть и не дало ускорения в разы, но существенно облегчило написание запросов (вместо длинной цепочки связей достаточно сджойнить одну таблицу и сразу получить профит). Оправдание денормализации простое: данные справочников не редактируются в рамках нашей системы и один раз заполненные таблицы справочников и связей - они остаются неизменными и ситуация нарушения целостности невозможна в штатном случае. Для интересующихся могу отослать к статье на хабре "Денормализация БД. Зачем? Когда? Как?".

- В проекте исторически использовался ORM hibernate. Штука во всех отношениях хорошая, однако обнаружилось, что некоторые агрегированные запросы к базе выполнялись мягко сказать неоптимально. С помощью SQLProfiler-а мы находили места в коде, где один запрос к коде приводил к тысячам подзапросов к базе. Надо ли говорить, что это работало медленно? Мы нашли два обходных решения данной проблемы. Первое - это использование множественного мэппинга для доменных объектов, когда для одного доменного объекта существует несколько отражений на таблицы БД, но с разным набором полей (это замечательно работает, когда нужны объекты без вложенных коллекций, либо без объёмных полей). Второе решение посложнее: часть агрегирующих запросов к БД были переписаны с HQL на нативные запросы к таблицам БД. Это во-первых, ускорило выполнение запросов за счёт уменьшения количества таблиц в запросе, а во-вторых, упростило сами запросы для понимания (для знающего человека SQL всё-таки понятнее, чем объектные конструкции). Процедуры удаление старых данных и импорта данных из унаследованных систем также были переписаны на нативные запросы, плюс импорт был переписан на пакетную загрузку. В итоге эти задачи стали выполняться минуты вместо часов. В одном месте даже была использована хранимая процедура, чего обычно не рекомендуется делать при использовании ORM. Но вызвать хранимку с курсорами и временными таблицами оказалось проще, чем реализовывать её в коде - особенно если дело касается не бизнес-логики, а служебных задач типа очистки БД от ненужных объектов справочников в случае повторной их загрузки.

- Интересная проблема была обнаружена на стенде заказчика - NLB-кластере из двух машин и одной БД. В проекте было реализовано выполнение некоторых бизнес-задач по заданному расписанию. Так вот, узлы кластера друг про друга ничего не знают, у обоих настроен хрон на запуск джобов по расписанию и одни и те же задачи стартуют почти одновременно на обоих узлах кластера. Выполняются, параллельно загружают данные из внешних систем, и также параллельно пишут их в БД. В итоге в БД оказывались дубликаты данных, которые потом, как-то удалялсь прикрученным к коду костылём. Мало того, что это ещё и тормозило, наличие дубликатов и логики кода, рассчитанной на наличие дублирующихся данных, мягко сказать, напрягало. Решение было реализовано в виде блокировки на базе (база ведь единственное место с которым взаимодействуют оба узла кластера): при старте задач на обоих серверах первым делом вне транзакции выполнялось обновление одной и той же записи в одной и той же таблице БД. Это успешно выполнялось только на одной из машин кластера, а на другой приводило к блокировке. Затем выполняется чтение информации о том, не запущена ли уже подобная задача. Если запущена, то принимается решение о том, что другой узел кластера уже выполняет данную задачу и текущая задача прекращается. Если не запущена, то добавляется запись о том, что задача стартовала и уже в рамках обычной транзакции стартует бизнес-процесс. Второй узел, разблокировавшись, понимает, что задача уже запущена и завершается. Не очень, может быть, красивое решение, но оно работает - а это главное.

- Несколько слов о пользе кеша. Учитывая специфику проекта, данные планируемом потреблении на сегодняшний день и дальше в прошлое никогда не могут измениться. Исходя из этого была добавлена таблица статистики и архивные агрегированные значения вместо постоянного пересчёта из основных таблиц берутся из этого кеша. Пользователи остались в восторге, так как для актуальных данных они понимают, почему медленно считается, но для статистических всё летает и их это устраивает.

Переходим постепенно к проблемам реализации:

- Параллельно выполнялись работы по поиску проблем в других местах системы: после убыстрения работы с базой следующее узкое место было обнаружено при передаче большого объёма данных: подпись и сериализация объектов выполнялась дольше, чем их извлечение из базы - мы не укладывались в 5 секунд. Первое, что было сделано, это разделение загрузки данных: раньше для списка объектов грузились данные как списочного представления, так и детального по каждому объекту. Смысл в этом есть: загрузка выполняется один раз, а затем при работе с объектами списка заново данные с сервера не запрашиваются. Но есть и проблемы: если данные обновились - клиент об этом не узнает до обновления всего списка объектов. Но у нас возникла проблема с объёмом этих данных, поэтому мы были вынуждены разделить загрузку списка и загрузку отдельного объекта. Нас это спасло ровно до тех пор, пока не появились пользователи, у которых уже сам список был огромным и грузился дольше 5 секунд (при этом из базы тянулся за время, меньшее половины секунды) - анализ показал, что опять проблема с подписью. Вариант пэйджинга отпадал - заказчик не хотел. Фоновая загрузка по частям тоже - сроки поджимали. Поэтому было принято волевое решение сжимать те ответы на запросы, где возникли такие проблемы. С точки зрения подхода веб-сервисов, xml и прочего решение было в принципе неправильным: фактически это шаг назад - никакой валидации по форматному контролю на этапе обмена. В прикладные объекты были добавлены опциональные поля типа binary куда внутренний сервер записывал сериализованный в json и запакованный gzip-ом сам объект, остальные поля при этом очищались. На внешнем сервере этот объект распаковывался и подставлялся в виде ответа дальнейшей логике. Костыль? Да, но заветные 5 секунд мы обеспечили и в случаях, когда объектов в списке очень много.

- Были и логические ошибки реализации: при авторизации внешних пользователей по сертификату поиск выполнялся по полю Subject сертификата X.509. Видимо ведущий разработчик не стал читать стандарт X.509, где чёрным по белому написано, что уникальным является серийный номер сертификата в рамках удостоверяющего центра, его выдавшего. И надо же, оказалось, что у пользователей поле субъекта не меняется для разных сертификатов. Сертификаты разные, но выданы в разное время одному и тому же человеку. Всё, у нас полетела авторизация. По-быстрому пришлось переделать поиск на серийный номер, но и тут возникли проблемы: из используемого нами браузерного плагина КриптоПро (да и вообще в .net-овской реализации) серийный номер получался как бинарная строка, а в java серийный номер представлял собой число. Как оказалось, современные разработчики не все представляют собой как правильно сериализовать число в бинарное представление и первый байт .toString(16) может в итоге выдать отрицательное число (правильно .toByteArray() и уже потом побайтово String.format "%02X").

- К ошибкам реализации также отнесу просто начхательское отношение к обработке данных в циклах: это относится как к наличии в циклах запросов к БД, чего категорически лучше не делать (один раз запросить перед циклом, а потом использовать), так и просто использование вложенных конструкций for 3-4 уровня для поиска вместо предварительного формирования словарей и быстрого поиска по хэшу. Да, возможно, что по использованию памяти не очень оптимально, но зато выигрыш в производительности в разы в зависимости от ситуации. Поиск таких узких мест очень прост: последовательно и методично расставляем тайминги выполнения вызываемых функций сперва на верхнем уровне, затем вниз в том направлении, где тратится больше всего времени. Потом анализ кода, рефакторинг и удовольствие от ускорения реализации в 2-3 раза.

- Не очень радовали больше 200 туду, расставленных в коде. Частенько при исследовании очередной баги приходилось натыкаться на комментарий типа туду доделать или ещё интереснее туду тут бага. Кроме некоторого количества негативных эмоций это ничем не помогало. Сейчас правда таких туду тоже осталось порядка сотни, но это тот технический долг, с которым придётся ещё долго возиться и дальше.

Наконец, несколько проблем интерфейса:

- Для начала продолжение проблемы с большим количеством объектов. Для отображения интерфейса на клиентском браузере уволенным ведущим разработчиком был разработан свой джаваскриптовый движок для формирования html-представления json-данных, получаемых ajax-запросами с сервера. По сути - небольшая обёртка над функциями jquery оперирования с DOM-деревом html-представления. В принципе, оно даже работало, после соответствующего допиливания. Проблема вылезла на IE8: при большом количестве объектов несколько секунд уходило на построение дерева и большой таблицы данных. Причём на других браузерах построение занимало меньше секунды. Помогло только одно: дерево и большую таблицу пришлось явно формировать в виде html-строки, а затем одним методом аттачить её к DOM страницы. Плюс к этому обработчики навешивались не на каждый элемент, а на body через on-метод через селекторы классов. В итоге обработчик создаётся один, и за его вызов отвечает событийная модель браузера. В итоге время построения таких объектов на IE8 стало занимать меньше секунды.

- Следующая проблема возникла с плагином jquery file upload: если его использовать штатно с штатной визуальной шкуркой, то всё работает отлично. Попытки программного использования оканчивались неудачей на IE9. Везде работало, кроме IE9. Файл до сервера доходил, формировался ответ, но в js события ответа от сервера не возникало. Работало только при указании метатега X-UA-Compatible content="IE=EmulateIE8". Замечательно работало до появления IE11, в котором при таком указании не работало отображение графиков. Вот не работало и всё тут. Графики стабильно работали при указании "IE=edge", но при этом не работал file upload на IE9. На форуме jquery file upload проблема обсуждалась и работающего решения никто не указал, все решали проблему как-то по-другому. Анализ http-трафика, поведения IE9 и разнообразные эксперименты привели к следующему работающему решению: в ответе от сервера на file upload было явно выставлен заголовок X-UA-Compatible со значением IE=edge и всё заработало на всех версиях IE как с графиками, так и с закачкой файлов. Ну тут, я считаю, просто повезло.

- Отдельно можно рассказать про проблему кеширования браузером джаваскриптов и css-ок. После установки очередной версии у заказчика постоянно возникала ошибка с тем, что браузер считал, что нет необходимости обновлять javascript и пользовался закешированной версией. Учитывая, что вся логика по отображению была в джаваскриптах, приводило это фактически к нерабочему состоянию (логика сервера и клиента отличалась). Оставив безуспешные попытки научить всех пользователей чистить кэш браузера при выходе каждой новой версии, был найден такой выход, что атрибут src="js/scripts.js?ver=${display_version}" джава скриптов и href="css/styles.css?ver=${display_version}" стилей стал содержать параметр версии, который подставлялся в jsp-файлы при сборке maven-плагином maven-war-plugin. Тогда для каждой новой версии скрипты и стили стали закачиваться новые, а затем кешировались до выхода новой версии. То есть работать стало ровно так, как нужно.

Скажу несколько слов про тестирование: 

- Во-первых, юнит-тесты были, но, пусть я и не сторонник 100% покрытия юнит-тестами, но особо важные места, типа логики интеграции с внешней системой можно и нужно покрывать тестами. Ведь это так просто сделать: выделить максимально узкий интерфейс для обращения к внешней системе, затем подсунуть в свой тест то, что должно уходить во внешнюю систему и то, что должно приходить в ответ. Такие тесты очень и очень сильно помогают в случае, если внешняя система тоже изменяется. Никто же сразу не скажет, что что-то изменилось, просто валится ответ и всё. А запротоколируй ошибочный ответ от внешней системы и подставь в тест - сразу будет понятно, что изменилось. В общем, это и было сделано в части автоматизированного тестирования.

- В части ручного тестирования у тестировщика была проблема: то, что было в рамках нашей системы он тестировал, но протестировать взаимодействие из интегрирующихся с нами систем он никак не мог. Поэтому в этой части постоянно возникали проблемы. Что было сделано: для каждой внешней системы, которая взаимодействовала с нашей были реализованы тестовые утилиты: в виде консольных приложений на .net-е и в виде тестовых страничек html. В итоге тестировщик смог проверять взаимодействие с нашей системой почти от всех нужных систем. Время на разработку этих тестовых приложений многократно окупилось, когда с нас потребовали примеры использования API. Мы просто отправили исходники наши утилит и проблем с автоматизированным взаимодействием с нашей системы практически ни у кого из участников рынка не возникло.

4) заказчик: онлайновое оперативное общение с заказчиком, максимально быстрая реакция на ошибки, спокойное уважительное отношение, несмотря на неадекватное поведение и претензии. Вот, в общем-то, и всё, что потребовалось, чтобы исправить ситуацию с заказчиком. Надо сказать, что нам повезло: учитывая специфику отрасли - электроэнергетики, представители заказчика в основном попадались технически грамотные и даже если и умудрялись как-то грохнуть базу, то быстренько её восстанавливали из предварительно сделанного бэкапа, попингуй не считали ругательством и в особо сложных случаях даже пускали через удалённое администрирование типа Ammyy Admin. До этого с заказчиком общение было только через аналитика, но их формально-бюрократический подход хоть и полезен иногда, но нормальные человеческие отношения, когда тебе пишут, что тут одна форма очень долго открывается, и ты обещаешь, что к завтрашнему дню исправишь и на самом деле исправляешь - результат этого не заставил себя ждать. Технически грамотные ответы и уважение к проблемам пользователя завоевали главное - доверие заказчика. Когда начальник представителя заказчика устраивает нам взбучку по телефону, а сам подчинённый, с которым мы общаемся, потом пишет, что зря он так на нас - означает, что ситуацию с заказчиком удалось исправить в лучшую сторону.

5) результат: было проведено 2 этапа комплексных испытаний, 2 месяца опытной эксплуатации и со 2 июня этого года система запущена в промышленную эксплуатацию.

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

Большая часть решений были вообще найдены гуглом, остальные были подсказаны Капитаном Очевидность. Я также не могу сказать, что предложенный мной подход постепенных улучшений сможет спасти ваш проект - каждый проект уникален и ситуация в каждом случае разная. Цель моего рассказа сегодня показать, что не боги горшки обжигают и при грамотном инженерном подходе некоторые проекты-камикадзе можно привести к более или менее успешному результату.


 

А также другие работы, которые могут Вас заинтересовать

5750. Физиологические особенности лошади 134.5 KB
  На протяжении тысячелетий лошадь остаётся верным спутником и помощником человека. Трудно назвать другое животное, чьё значение для нас было бы столь велико. Уже более четырех десятилетий общая численность лошадей в мире остаётся стабильной...
5751. История фирмы Сименс 81.5 KB
  Биография основателя компании Сименс. 13 декабря 1816 г. в городе Ленте близ Ганновера родился Вернер Сименс - будущий инженер, изобретатель, ученый, промышленник, общественный деятель. Окончив с отличием гимназию в Любеке, затем артиллерийское инже...
5752. Цинкодефициты и значение цинка в жизни человека 71.5 KB
  О цинкодефицитах и не только. Все больше людей сейчас приходят к осознанию что прежде чем требовать от своего организма стабильно - эффективных результатов работы - необходимо обеспечить ему для этого максимально благоприятные условий...
5753. Монолитное перекрытие выполняемое по балочной схеме 74 KB
  На сегодняшний день из существующих технологий возведения зданий и сооружений наиболее перспективным является монолитное строительство. Это - возведение конструктивных элементов из бетоносодержащей смеси с использованием специальных...
5754. Конвейер ленточный крутонаклонлонный (угол наклона 600) 5.64 MB
  Высокопроизводительная работа современного предприятия невозможна без правильно организованных и надежно работающих средств промышленного транспорта. Например, на машиностроительном заводе получают и распределяют по цехам сотни тонн металла...
5757. Построение осесимметричного меридиального потенциального потока 246 KB
  Исходные данные к курсовой работе Большой радиус канала R= 0,35 м Малый радиус канала r=0,045 м Высота канала на входе b0=0,3 м Угол наклона конической части за малым радиусом ...
5758. Глобальные проблемы мировой цивилизации 123.5 KB
  Введение Глобальные проблемы нашей эпохи - закономерное следствие всей современной глобальной ситуации, сложившейся на земном шаре в последней трети XX века. Для правильного понимания происхождения, сущности и возможности их решения необходимо ...