Re: Developing frameworks and applications in tandem and managing dependencies

John Elliot <[email protected]> Tue, 31 Jul 2012 20:36:37 +1000
Newsgroups gmane.comp.windows.off-topic
Message-ID <[email protected]>
Below is a long thinking-out-loud kind of a discussion. tl;dr: only use
svn externals definitions to point at immutable release tags or at
pegged revision numbers.

On 2012-07-31 19:23, Anna-Jayne Metcalfe wrote:
>> > Are you using Subversion as your version control system?
> I'm afraid not. The scheme we use should work with any SCC that supports
> shared files or projects, though.

Subversion has a feature called svn externals which might be like what
you refer to as 'shared projects' but I'm not sure... (What SCC system
are you using btw?) You can use an svn externals definition to link in
directories from projects in other Subversion repositories (or in the
same repository).

The problem with svn externals is this. Say I'm developing app-a and I
have it in my svn repository at repo/app-a/trunk. I can checkout trunk
to a working copy directory say wc/app-a. I might then have some
libraries that app-a uses and I'll put them in a subdirectory
wc/app-a/lib. I can add an svn externals property on the wc/app-a/lib
directory that says to use repo/myframework/trunk and to put that in the
wc/app-a/lib/myframework directory. Then I'll be working on the 'trunk'
of app-a and the 'trunk' of myframework. I think 'trunk' is analogous to
what you referred to as your 'Dev' branch.

This is a great set up at first because it gives me exactly what I want.
The ability to edit files in wc/app-a for the purposes of developing my
app's functionality and also the ability to directly edit the files in
wc/app-a/lib/myframwork. In both cases edits to any of those files would
be checked back in to the 'trunk' (Dev branch) of the respective projects.

The problem happens when I create a 'tag' (what I think you are calling
a 'Release' branch) of app-a after I do a release of it. What happens
then is that a snapshot of repo/app-a/trunk gets made in
repo/app-a/tags/version/0.1.0. So if I want the definitive code for
version 0.1.0 I can look in the svn repository at
repo/app-a/tags/version/0.1.0. After I've done such a release I might
make repo/app-a/trunk then be the 0.2 version which hasn't yet been
released. The problem is that when I tagged v0.1.0 in the repository the
svn externals definition for the library that was being used by app-a
0.1.0 still has repo/app-a/tags/version/0.1.0/lib/myframework pointing
at the trunk of myframework at repo/myframework/trunk.

So if at some point in the future I want the code for the app-a 0.1.0
release I might try to checkout repo/app-a/tags/version/0.1.0 into a
working copy somewhere, say wc/app-a-0.1.0. That will give me the
application code for app-a as it was at version 0.1.0 but the svn
externals definition on repo/app-a/tags/version/0.1.0/lib will still
have an svn externals property saying that the relevant version of the
myframework code is at repo/myframework/trunk. So I'll get the released
code of app-a and the latest myframework code, not the myframework code
that was used when app-a 0.1.0 was released.

It would seem that a viable solution to the problem would be that prior
to releasing app-a the myframework library was released first. Releasing
myframework would mean that the development branch of myframework in
repo/myframework/trunk got tagged as a release in
repo/myframework/tags/version/0.1.0 and then after that the svn
externals definition on repo/app-a/trunk/lib got updated to point at
repo/myframework/tags/version/0.1.0 rather than repo/myframework/trunk.

Then if I was to checkout the release of app-a v0.1.0 to wc/app-a-0.1.0
the wc/app-a-0.1.0/lib/myframework directory would contain not the
latest version of the myframework code but the code as it was for
myframework v0.1.0.

The above just discusses 'tags' and not branches, and with branches
things get more complicated still. Say for instance I released v0.1.0 of
app-a and then v0.2.0 of app-a which is not backwards compatible with
v0.1.0. Some of my customers are running v0.1.0 and some v0.2.0. Then I
discover a security vulnerability (hey, could happen) that affects both
v0.1.0 and v0.2.0 of app-a. Let's say I'm still offering support for
both the 0.1 and 0.2 versions, so I need to create v0.1.1 and v0.2.1
that patch the security vulnerability. I then discover that in addition
to needing 'tags' to snapshot my codebase at a particular point in time
I also need 'branches' in which I can continue development of minor
versions. So I might have repo/app-a/branches/0.1 and
repo/app-a/branches/0.2 while v0.3 is being developed in
repo/app-a/trunk. There remains the question of what to do with the svn
externals definition in repo/app-a/branches/0.1/lib for the myframework
library. Should I point that externals definition to the trunk of
myframework (repo/myframework/trunk), to a minor version branch of
myframework (repo/myframework/branch/0.1), or to a release tag of
myframework (repo/myframework/tags/version/0.1.0)?

It all starts to seem to get too hard and a copy and paste approach
seems like it might be the best bet. As a yard stick I look around at
what other successful projects do and it seems to me that most people
take the practical approach of just copying and pasting in a particular
version of a framework or library. Then if they need new functionality
from the framework or library they just overwrite their copied and
pasted version with an updated version of the framework or library.

So having had this discussion (and thanks to those who have played along
while I thought out loud) I think I've decided what will work for me.

I will develop a library/framework called myframework and keep it in my
svn repository in repo/myframework. As part of the myframework project
will be unit tests which run over various features and a reference
application which utilise the features of the application. So I'll be
able to run the reference application and develop framework features
with respect to the reference application. When I'm happy that the
development version of myframework in repo/trunk/myframework is release
worthy I will do a "release" of the software. A "release" means that
repo/myframework/trunk gets branched to repo/myframework/branches/0.1
(where backwards compatible fixes can be done if necessary in
maintenance mode) and gets tagged to repo/myframework/tags/0.1.0 (where
an immutable copy of the entire library is kept forever.

I will then develop any number of applications that use myframework. For
example repo/app-a and repo/app-b. Because app-a and app-b depend on
myframework they will have svn externals definitions in their lib
subdirectories so that both repo/app-a/lib/myframework and
repo/app-b/lib/myframework are references to some immutable release tag
of myframework, for instance repo/myframework/tags/version/0.1.0.

Then if an application I'm developing calls for a new feature of
myframework to be developed I will develop the feature along with unit
tests and reference implementation in repo/myframework/trunk. Then when
I'm happy that the new feature is functional I will do a new release of
myframework say to repo/myframework/tags/version/0.2.0 and then I will
update the svn externals properties in app-a or app-b as necessary to
point to the latest development tag.

In short what I think I've decided having had this discussion is that
when using svn externals definitions I will always either peg a revision
number or set the externals to an immutable tag.

This means that when I'm working on app-a and I find that I'm missing a
feature that I would like in myframework I can't just edit the source
code in wc/app-a/lib/myframework because that code is an immutable
snapshot of a particular version of myframework. Rather I will have to
do the development in wc/myframework, then release myframework, then
update the svn externals definition for wc/app-a/lib/myframework to
point at the latest useful release.

This is a little more labour intensive than I would have liked, but I
think it's a reasonable and robust enough process to pursue. Such a
process would certainly be an improvement on the disaster I have at the
moment wherein all of my svn externals definitions point to trunk and my
software spends more of its time broken than functional.