Xpoint
   [напомнить пароль]

Подход к хранению повторно используемых библиотек

Метки: [без меток]
2006-03-22 18:28:54 [обр] Дмитрий Котеров(15/912)[досье]

Рассмотрим создание нескольких сайтов (предположим, на PHP) с похожей архитектурой. Весь код можно условно разделить на 3 большие группы:

  1. Универсальный неизменный. Например, библиотеки PEAR, сам PHP и т.д.
  2. Универсальный развивающийся. Например, код CMS/CMF, который от проекта к проекту неизменно дорабатывается и совершенствуется (как правило, без потери совместимости), универсальные библиотеки и т.д.
  3. Специфичный для сайта. Это контроллеры и шаблоны.

Возникает вопрос, как правильнее всего это хранить, если проектов несколько, чтобы обеспечить максимальную пользу от повторного использования кода. (1) хранится в единственном экземпляре на сервере и общий для всех проектов. Наоборот, (3) для каждого проекта свой собственный и не совпадающий от проекта к проекту практически ни в чем. Здесь проблем нет.

Вся загвоздка в (2) (универсальном изменяющемся, например, CMF), потому что:

  1. Хранить отдельную и независимую копию (2) в каждом проекте плохо. Изменения, внесенные в CMF одного проекта (например, багфикс), трудно будет внедрить в другой. К тому же, если идет активная работа над одним проектам, а второй на время "замораживается", чем дальше, тем сложнее будет в нем "догнать" версии библиотек.
  2. Хранить одну-единственную копию (2) в отдельном месте еще хуже. Раз в код вносятся изменения, вполне может возникнуть ситуация, когда добавляется новый баг, который в текущем проекте не воспроизводится, но все остальные проекты из-за него приходят в негодность. Естественно, когда правится код, то тестируется он в активном проекте (в том, где этот код используется), и требование тестировать заодно и все остальные проекты совершенно невыполнимо.

Я несколько раз видел реализацию варианта (б), когда библиотеки хранятся в отдельном CVS-модуле, выкачиваемом в каждом проекте. Соответственно, это вариант (б), и в случае, если библиотека чуть-чуть попортится, падают сразу все проекты при следующем обновлении.

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

Но ни (а), ни (б) не устраивает. Вопрос: как эту проблему решают участники форума?

спустя 53 минуты [обр] Николай Бубело(0/113)[досье]
Использую вариант a) (при этом он меня совершенно не устраивает:)
Вариант b) для меня неприемлем из-за описанных выше проблем с совместимостью.
спустя 15 часов [обр] Дмитрий Котеров(15/912)[досье]

В принципе, есть одна безумная идея, но вряд ли она будет поддерживаться в средах разработки. Хранить библиотеки в одном CVS-модуле, но под каждый проект выделять в нем собственную ветку (по имени проекта), и выкачивать только эту ветку. При этом в активном проекте использовать ветку HEAD, в остальные - делать MERGE по мере необходимости (из HEAD и в HEAD, если багфикс где-то еще). При этом на диске все эти ветки должны лежать внутри каждого из проектов (но вот так, кажется, как раз и нельзя: когда часть директорий берется из одного CVS-модуля, а часть - из другого).

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

спустя 58 секунд [обр] Андрей Пахомов(0/310)[досье]
на данный момент использую вариант а) но только из-за нехватки времени, поскольку не устраивает. А вообще применялся подход из XP: для каждой библиотеки писался набор тестов, которые максимально полно тестируют все библиотеки и методы, которые и запускаются каждый раз, прежде чем текущая версия станет применятся в проекте. Для построения тестов использовался PHPUnit
спустя 1 час 1 минуту [обр] Дмитрий Котеров(15/912)[досье]
Тесты - это хорошо. Но бывают же случаи, когда их недостаточно, либо они вообще невозможны. Например, если библиотека использует коннект к СУБД, тестировать ее будет очень проблематично. Библиотеки (2) потому и (2), а не (1), что они "недостаточно хорошие" в плане универсальности.
спустя 1 час 7 минут [обр] Андрей Пахомов(0/310)[досье]
Если честно, то таких случаев очень мало. Поскольку в моем случае БД - это Postgres, то делать кучу различных инсертов,апдейтов и делитов в одной транзкации и потом делать ей rollback не представляет проблем, то отладка на какой то тестовой БД классов, ответственных за работу с БД - вполне реализуемо. Единственно, что это в ряде случаев достаточно трудоемко, и в условиях напряженного графика работы этим заниматься не всегда получается, но зато это окупает себя с лихвой. Плюс все таки в классах, реализующих высокоуровневые операции и бизнес-логику (а именно для них и сложно строить тесты, потому как именно для них и приходится эмулировать достаточно сложные действия пользователя) ошибок гораздо меньше, потому как при нормальном построении приложения там достаточно все прозрачно и мощных усилий их отладка не требует, проще руками протестить, чем ваять сложные системы тестов. А воспомогательные классы более низкого уровня автоматическими системами тестов обрабатываются на раз. На самом деле когда начинаешь делать эти тесты, начинаешь испытывать настоящую уверенность в своих действиях, потому как тесты сами по себе отрабатывают быстро и их можно запускать часто, а результатом является то, что вы реально видите последствия своих правок сразу. Тем более что нормально написанные тесты заставляют писать классы с качественной обработкой ошибок и приводят к тому, что многие вещи о которых изначально не задумывался начинают учитываться. В моем случае это приводило к тому, что ошибкоустойчивость получившихся библиотек повышалась настолько, что реальные приложения практически не имели шансов вызвать ошибку. Если же это происходило - анализировалась сложившаяся ситуация и писался еще один тест:) В приницпе то альтернатив все равно нет - хранение вариантов в различных ветках репрозитория - это полумера, реальный ответ на то, будет данный класс нормально в реальном приложении может дать только тест, либо ручной, либо автоматический, ИМХО третьего не дано.
спустя 10 часов [обр] VIG(38/839)[досье]

Могу, в качестве иллюстрации, обозначить, как это делается у нас на фирме. Правда, не по отношению к сайтам, а в release engineering, но суть дела от этого не меняется.

  1. Имеется дерево проектов и их версий, которое весьма активно развивается, причем по многим веткам и подветкам (версиям) одновременно.
  1. В каждом проекте имеется ветка latest, в которую только и можно вносить изменения без ограничений (не считая, конечно, личных веток разработчиков). Ветка latest не используется ни для чего, кроме разработки и тестирования. Во все остальные ветки отдельные изменения портируются после того, как они прошли тестирование в latest (по решению ответственных лиц в каждом отдельном случае для каждой отдельной ветки: в основном, по принципу "работает — не трогай!"). Иногда, правда, достаточно редко, делаются багфиксы специфичные для конкретной ветки конкретного проекта.
  1. Используется достаточно мощный софт репозитория, который, в частности, включает довольно интеллектуальные операции integrate и backport. Плюс, естественно, весьма жесткая дисциплина разработчиков, направленная на то, чтобы этот софт работал правильно в подавляющем большинстве случаев.
  1. Довольно большой отдел QA (Quality Assurance), который, кстати, собственно тестированием не занимается, а занимается тем, что пишет и развивает систему автоматического тестирования продукта, через которую, без человеческого участия, прогоняются все билды (кроме личных) перед тем, как они хоть куда-нибудь попадают. Сейчас в ней, насколько я знаю, около 200 тысяч тестов и тестиков.
  1. Каждый разработчик обязан сопроводить каждое изменение указаниями на то, как его следует тестировать.

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

спустя 16 дней [обр] Дмитрий Котеров(15/912)[досье]
В общем, получается, что хороших решений нет... Причем не потому, что отсутствует метод (метод я предложил выше, в виде хранения в выделенной директории данных из другой CVS-ветки; пока что я не вижу, чем он плох), а потому, что его никто не реализовал.
спустя 8 месяцев [обр] Дмитрий Котеров(15/912)[досье]

Появилась интересная информация.

Во-первых, в Subversion есть svn:externals, который работает и в Eclipse (Subclipse) тоже. С его помощью можно часть svn-дерева брать из другого репозитория (и другой папки), а также - коммитить в другое место. Недостаток здесь такой, что "примонтировать" можно только директории. Отдельные файлы подключить по svn:externals невозможно. Еще недостаток: в Subclipse крайне примитивный merge для svn. Считайте, что его и нет, та же командная строка. Работа с CVS в Eclipse не в пример удобнее.

Во-вторых, если проект на CVS, то можно общие библиотеки дополнительно хранить в Subversion. Т.е. для файлов и директорий библиотек будут служебные директории CVS и .svn одновременно. Таким образом, в CVS хранится текущий и гарантированно рабочий "снимок", а .svn позволяет при необходимости синхронизировать (утилитами командной строки) изменения в библиотеках между проектами. Криво, конечно, но зато можно таким образом "расшарить" между проектами и один-единственный файл, а не всю директорию целиком (для этого надо лишь скопировать .svn-директорию для папки, содержащей файл, и вручную подредактировать .svn/entries, чтобы убрать все лишнее).

Powered by POEM™ Engine Copyright © 2002-2005