Версионность кода и отслеживание зависимостей

alekciy

Новичок
Какие методики/алгоритмы лучше применить для поддержания версионности кода? Как я понимаю готового решения нет и нужно писать свое, но хотелось бы подумать для начала, как наиболее оптимальнее это выполнить. Пока возникает такой вариант.

Начальные условия: два класса (модуль или какая другая программная единица кода, в общем некая сущность), пусть X и Y, два разработчика. Класс Y (назовем его класс-клиент) связан с X (назовем его класс-сервер) через метод m(), т.е. класс Y знаем, чьим клиентом он является. При этом X совершенно не знает, кто использует его метод m, но у него есть публичное свойство в котором содержится версия. При этом глобально принята семантическая схема именования версий ("Семантическое управление версиями 1.0.0-rc.1").

PHP:
<?php

class X
{
	public static
		$version		= '0.1.0';

	public function m()
	{
		return array(
			'id'	=> 1,
		);
	}
}

class Y
{
	public static
		$varsion		= '0.1.0';
		
	public function n()
	{
		$X = new X();
		$var = $X->m();
		// что-то делаем с $var
	}
}
Ситуация: Изменилась сигнатура (поэтому увеличился номер мажорной версии) метода m и в id теперь не целое, а массив:
PHP:
<?php

class X
{
	public static
		$version		= '1.0.0';

	public function m()
	{
		return array(
			'id'	=> array(),
		);
	}
}
Соответственно разработчику класса Y нужно изменить метод n().

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

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

Алгоритм изменения кода:
1) В классе увеличили номер будущей версии, но сам код ни как не изменяем еще.
2) Вызываем метод класса-реестра сообщая ему о нашем намерение.
3) Реестр посмотрел, кто подписан на данный класс и уведомил все заинтересованных лиц.
4) Авторы классов-клиентов посмотрели, касаются ли их изменения (может сменился метод который они не используют) и если касаются, то вносят изменения в свой код (по сути вводят новую ветку исполнения кода [реализовать можно банальным if ($current_version = будующая_версия) {} elseif ($current_version = текущая_версия) {}] параллельно с текущей, что бы когда будущая версия кода-сервера стала текущей выполнялась эта ветвь).
5) Изменяем класс-сервер, сменяем номера версий, уведомляем реестр о новой текущей версии.
Ни чего не сломалось, потому как связанный код под новые условия уже должен быть изменен.

Цели:
а) в пределах двух версий (текущей и будущей) быть уверенным в работоспособности системы состоящей из кода разных разработчиков (что не исключает полезности этого и для одного разработчика);
б) иметь возможность в любой момент времени узнать, какие части будут затронуты при изменении одной из компонент, это позволит более точнее определять фронт работ.

Мысли? Соображения? Дополнения?

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

флоппик

promotor fidei
Команда форума
Партнер клуба
Добавляем глобально класс-реестр который хранит связи между классами и версии, а так же информацию в духе email каждого автора класса. Каждый класс содержит в себе не только номер текущей версии, но и будущей планируемой и предыдущей. Класс-клиент всегда знает, чьим клиентом он является и с какой версией класса-сервера работает.
Это разумней вливать в Dependency Injection, чем держать отдельный реестр для этого.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
DI от Симфони есть как отдельный компонент — http://components.symfony-project.org/dependency-injection/
Я его пробовал, да, но в итоге концептуально отошел к мнению, что подобные вещи как зависимости, версионность и.т.п. должны скорее решатся на уровне документации/соглашений, чем технически. Всегда есть исключения, конечно.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Кроме того, можно зависимость от версии чужого кода реализовывать на уровне версионности своего кода, через git submodules например — это мне как-то ближе.
 

zerkms

TDD infected
Команда форума
Модульные/функциональные/приёмочные тесты ftw
 

alekciy

Новичок
Кроме того, можно зависимость от версии чужого кода реализовывать на уровне версионности своего кода, через git submodules например — это мне как-то ближе.
Т.е. смена кода полным куском? Думал в этом сторону, т.к. проще реализуется. Но применимо не всегда. Для случая поддержания кода в режиме двух версий (будущая-текущая) не подходит.
 
alekciy, разве это не будет минорное изменение в X, т.е. будет версия 0.1.1, так как было введено в продукт новой функциональности, ведущей к программной несовместимости с старой версией (именно несовместимость на уровне данных, а не интерфейса (API))?
Насчет твоей проблемы, мне попадался на глаза подобный менеджер, что называется composer (http://getcomposer.org/), но я сильно не вникал.
 

alekciy

Новичок
alekciy, разве это не будет минорное изменение в X, т.е. будет версия 0.1.1, так как было введено в продукт новой функциональности, ведущей к программной несовместимости с старой версией (именно несовместимость на уровне данных, а не интерфейса (API))?
Нет, это именно мажорное. Потому как это изменение именно на уровне API. Код клиентов класса Х раньше от метода m ждат целое, теперь API изменилось и стал выдаваться массив. То, что само API не изменилось по набору входных данных вовсе не означает, что API не изменилось. Если новая версия перестает поддерживать старую схему работы, то увеличиваем именно мажорную версию. И исходя из этого, а так же при условии следования семантической схемы именования версий, код классов-клиентов может сразу понять, что возможно его работа может оказаться нарушенной.

К примеру, если бы было обратное изменение, т.е. с array на int, то в коде классов-клиентов тут же бы все слетело в foreach и прочих операторах работы с циклами ибо в них бы стали передавать целое, а не массив.

Минорным (т.е. 0.2.0) изменение было бы в случае добавления нового метода и отсутствии изменения в m методе. В этом случае уже созданное API фактически затронуто ни как бы не было и все классы-клиенты исходя из семантики поняли бы это и знали, что ни чего в своем коде изменять не нужно.
 

alekciy

Новичок
В каком смысле не ответят? А на какой вопрос они ответят?
В прямом. При наличии информации о связях через тот же реестр можно еще до внесения изменений в какой либо код сразу узнать кого это затронет. А над тестами нужно еще думать и писать. Кроме того тесты, такой же код подверженный ошибкам и неточностям реализации. Ситуация корректного прохождения теста и падение кода выкаченного на продакшен вполне возможна.
 

zerkms

TDD infected
Команда форума
В прямом. При наличии информации о связях через тот же реестр можно еще до внесения изменений в какой либо код сразу узнать кого это затронет. А над тестами нужно еще думать и писать. Кроме того тесты, такой же код подверженный ошибкам и неточностям реализации. Ситуация корректного прохождения теста и падение кода выкаченного на продакшен вполне возможна.
На чём основывается всё написанное вами? Вы хоть когда-нибудь практиковали тесты (любые)?
 

alekciy

Новичок
На чём основывается всё написанное вами? Вы хоть когда-нибудь практиковали тесты (любые)?
Практиковал. Функциональные. Видимо не постиг особый дзен.

Мне просто не ясно, каким образом тест для класса-клиента может заранее узнать об изменении API. В случае реестра все заинтересованные лица могут получить нотификацию еще до того, как какой либо кода в классе-сервере будет написан. Каким образом в этом может помочь тесты? Я вижу только один вариант, автор класса-клента пишет тест в котором используется новое API и подсовывает его для тестов классов-клиентов, при запуске тестов глобально в рамках всего проекта тесты классов-клиентов валятся ибо ожидают другую сигнатуру.
 

zerkms

TDD infected
Команда форума
Практиковал. Функциональные. Видимо не постиг особый дзен.

Мне просто не ясно, каким образом тест для класса-клиента может заранее узнать об изменении API. В случае реестра все заинтересованные лица могут получить нотификацию еще до того, как какой либо кода в классе-сервере будет написан. Каким образом в этом может помочь тесты? Я вижу только один вариант, автор класса-клента пишет тест в котором используется новое API и подсовывает его для тестов классов-клиентов, при запуске тестов глобально в рамках всего проекта тесты классов-клиентов валятся ибо ожидают другую сигнатуру.
Эм... Тест ни о чём не должен узнавать. Он должен ломаться, если всё плохо, и не ломаться, если всё хорошо.

Какого рода "нотификацию" получат заинтересованные лица в случае реализации вами "реестра"? "Что-то" изменилось? Отличная просто информативность. После этого вы сядете и вручную проделаете то же самое, что тесты делают автоматически, т.е. проверите, что всё работает как ожидается, или работает как не ожидается. Вот только в этом случае вы успускаете, что ошибиться (забыть что-то сделать) у вас шансов на пару порядков больше, чем у автоматического теста.
 

alekciy

Новичок
Эм... Тест ни о чём не должен узнавать. Он должен ломаться, если всё плохо, и не ломаться, если всё хорошо.

Какого рода "нотификацию" получат заинтересованные лица в случае реализации вами "реестра"? "Что-то" изменилось? Отличная просто информативность.
Что что-то еще собирается измениться.

Но в целом куда все это клониться понятно. Не стоит смешивать нотификацию о планах разработки и работой конкретного кода и проверки его работоспособности.

Значит как я понял предложный вариант тестирования вполне легитимный? Но с ним так же есть аспект версионности. Т.е. получается, что нужно иметь тесты под каждую версию с тем, что бы можно было узнать, на каких версиях валиться код клиентов? Или версионость для теста разумнее делать на уровне СУВ? В общий бранч камитим новый тест, запускаем на бранче тесты, они валятся, ответственные разбираются в чем дело, изменяют свой код в этом бранче, дописывают тесты, еще раз прогоняем тесты этого бранча, если все нормально, мерджим в транк?
 

AmdY

Пью пиво
Команда форума
alekciy
вы разделяете правки в коде и тесте? все правки должны быть в одном бранче. у нас это деже 1 коммит на один бранч. соотвественно версионность - это замержденные бранчи. помимо этого есть ещё кустомизация, которая для клиентов. там всё основанно на наследованнии через файловую систему - если есть кустомный файл - грузим его, иначе коре. с версионностью там даже не нужно загоняться.
 

alekciy

Новичок
alekciy
вы разделяете правки в коде и тесте? все правки должны быть в одном бранче. у нас это деже 1 коммит на один бранч. соотвественно версионность - это замержденные бранчи. помимо этого есть ещё кустомизация, которая для клиентов. там всё основанно на наследованнии через файловую систему - если есть кустомный файл - грузим его, иначе коре. с версионностью там даже не нужно загоняться.
Сейчас каждый работает в своем бранче. Да, по ходу работа в рамках одного упростит дело. А для связанного кода вне репозитория тесты по ходу самый разумный вариант контроля апдейтов.
 

zerkms

TDD infected
Команда форума
Что что-то еще собирается измениться.

Но в целом куда все это клониться понятно. Не стоит смешивать нотификацию о планах разработки и работой конкретного кода и проверки его работоспособности.

Значит как я понял предложный вариант тестирования вполне легитимный? Но с ним так же есть аспект версионности. Т.е. получается, что нужно иметь тесты под каждую версию с тем, что бы можно было узнать, на каких версиях валиться код клиентов? Или версионость для теста разумнее делать на уровне СУВ? В общий бранч камитим новый тест, запускаем на бранче тесты, они валятся, ответственные разбираются в чем дело, изменяют свой код в этом бранче, дописывают тесты, еще раз прогоняем тесты этого бранча, если все нормально, мерджим в транк?
Если вы предоставляете одновременно несколько версий интерфейса, то, очевидно, под каждый из них должен быть свой набор тестов.
Цель тестов не узнать "на каких версиях валиться код клиентов", а удостовериться, что код ведёт себя так, как ожидается.

По поводу что и куда мерджить - это уже в рамках текущей темы оффтоп имхо. Вариантов ведения веток и разработки не одна, каждый разрабатывает так, как удобно
 
Сверху