Представьте себе такую ситуацию:
- У нас есть внутренняя утилита (скажем, редактор уровней). Время от времени выходят новые версии.
- Информация хранится в XML или другом древовидном расширяемом формате. При этом мы, как светлые джедаи, следим за совместимостью «снизу вверх», и более поздние версии открывают все ранние проекты.
- Утилитой пользуются люди нетехнические и не подверженные «синдрому раннего подражателя». Им не обязательно иметь свежайшую версию редактора; главное — стабильную и одинаковую в рамках одного проекта.
- Время от времени появляются новые функции, в XML для этих функций появляются новые блоки. При этом возможна такая ситуация: пусть над одним проектом работают двое. У одного программа новые блоки поддерживает, у другого — нет. Второй, сохранив проект, просто потеряет эти блоки.
- Иногда приходится «перекраивать» формат хранения данных. Например, раньше был один тайлсет — теперь целая куча. Старой версии, скорее всего, «снесёт крышу», и она перед загрузкой должна предупреждать: «Это сейв от новой версии и, скорее всего, несовместимый. Продолжить загрузку?»
- Аналогичные подтверждения нужны, если открываем новой версией старый проект. Утилита всё-таки внутренняя, и не стоит консервировать в ней поддержку старых сейвов на запись.
- Поскольку утилита внутренняя, она на правах «it’s beta than nothing», и единственный гарант её безглючности — наш профессионализм. Иногда появляются «чёрные» версии, которые портят данные. Как предупредить дизайнера, что в его версию вкралась противная ошибка?
- Нужно обойтись без централизованного сервера обновлений.
Это простенькое решение я придумал в 2005 году, работая на мобильных играх, чтобы хоть как-то справиться с зоопарком версий.
В каждом документе храним четыре поля.
- BuildSaved — та версия, которой мы сохранили проект.
- BuildSupported — наименьшая версия, которая гарантированно и полностью откроет этот сейв.
- BuildRequired — наименьшая версия, которая хотя бы частично, но без ошибок откроет этот сейв.
- BlackBuilds — все версии, которые портят данные.
При этом BuildSaved >= BuildSupported >= BuildRequired. Номера версий я задавал жёстко, в исходниках. Появилось в XML новое поле — заменяй BuildSupported на текущую версию. Перекроил формат хранения — заменяй сразу BuildSupported и BuildRequired. BlackBuilds определял умозрительно, по чейнджлогам — от той версии, в которой появилась ошибочная функциональность, до той, в которой заметили и исправили.
Открывая документ, мы делаем такие проверки.
- Если текущая версия программы ниже BuildRequired, сообщаем: «Это документ от новой версии и, скорее всего, несовместимый. Продолжить загрузку?»
- Если версия ниже BuildSupported, сообщаем: «В документе могут быть новые поля, которые при пересохранении потеряются».
- Если версия — одна из BlackBuilds, сообщаем: «Ваша версия портит документы. Ничего не сохраняйте, обновитесь до менее опасной версии».
- Если BuildSaved — одна из «чёрных» версий, хранящихся в EXE-файле, выводим: «Документ сохранён опасной версией. В документе могут быть ошибки. Пусть тот, кто сохранил документ, немедленно обновится».
- Наконец, если обнаружен устаревший механизм хранения данных, выводим: «Прежде чем пересохранять, сделайте резервную копию». А ещё лучше — сделать резервную копию автоматически (предложил dzhe). Тут уже никаких голых заявлений на основе одной цифры BuildRequired: мы при загрузке сами видим, устаревший формат или нет.