1. Applicability
and Scope
The recommendations and guidance presented in this document
apply to Team Foundation Server Version Control, Visual SourceSafe, and
Subversion as of the publication date. Follow the project structure guidelines for
Visual SourceSafe projects since these will soon be migrated to Subversion or
Team Foundation Server.
2. Purpose
and Sources
This document attempts to minimize the focus on using
particular products and is aimed at providing guidance and justification for
the recommended repository, project, and solution structures based on scenarios
that occur in software projects in any organization and the best practices
promoted by the IT industry and Software Configuration Management (SCM) in
general. I aim to show you how to
accomplish specific goals as they relate to SCM and the decisions and concepts
involved.
This document draws upon the combined published experience
of developers using a variety of version control systems (VCS). The recommendations for the core version
control structure (BTT) are published and promoted primarily by Subversion
users. In itself, this represents the
combined experience of millions of developers worldwide over the past 21 years
on a variety of projects of all sizes. Each
VCS vendor, of course, has their own guidance and recommendations on this
subject with various slants to highlight the capabilities of their own systems.
This document also includes project structure guidance that,
while not specifically related to any type of version control, certainly
enables some of the version control tasks to be performed cleanly and simply. The project structure guidance itself draws
upon sources both related and unrelated to version control and software
configuration management (SCM). This
guidance incorporates the theory and practical advice of a multitude of IT disciplines,
project models and recommendations from developers and vendors on all platforms.
This document will be followed by a set of focused
"how-to’s" demonstrating each of the scenarios and tasks presented using the
products and tools currently in use.
3. Words
of Warning
Good version control systems are effectively time
machines. You can delete things to your
heart’s content and remain confident that you can always get it back at any
time if you should need it. The view
provided by most version control systems shows you a directory structure that
looks very similar to a file system. It
is important to realize that the view you’re shown is only the structure as it
exists at a particular point in time (typically the present, unless you tell it
to show you the system as of some prior date or change).
The fact that a good version control system will faithfully
store anything you put in there and never lose it makes it fairly important
that you never store anything that you don’t want to keep a record of for all
time. Private passwords, credit card or
bank account numbers, personal data and any form of PHI should never placed in
version control. Although it can easily
be deleted from the current view, it will always remain available in the
system’s historical views; accessible to anyone with appropriate access.
If the version control system is used to maintain source
code, be sure that you only store the source code information and dependent
binaries that are necessary to build your software solution. For example, do not store the ISO image of
the Visual Studio installation DVD or the installation packages for other
tools. This would simply be a waste of
space, as these are readily available from other sources and, if placed in
version control, can never be truly removed.
With that said, please do
delete anything and everything that you no longer have a use for. The default, current view of the system
should only show you what is relevant and supported or in active development now.
Do not leave dead or retired branches in the current view any longer
than necessary. If you ever have need of
them again, they will still be there.
"Destroy" supported in TFS 2008
Astute readers may be quick to point out that the "Destroy" capability,
the ability to permanently remove an item from version control, is available in
Team Foundation Server 2008. This is a
double-edged sword.
The ability to completely eradicate all traces of data is certainly
important from a security perspective (PHI, or Personal Health Information, for
example) and from a repository maintenance perspective (CD/DVD images or SQL
databases accidentally committed to version control, for example), but must be
used very sparingly and with extreme caution.
Keep in mind that two of the main purposes of version control are to
allow people to share information and to keep a history of all changes to that
information. The ability to destroy an item
defeats both of these purposes.
The ability to destroy can have drastic unintended consequences:
Consider first, a file that contains a piece of PHI that must be removed
from the system, regardless of consequence.
This file has itself been changed numerous times, branched to six
different releases and merged several times with otherwise ‘safe’
information. You decide to "Destroy" the
file where the PHI first appeared. What
happens to the other variations, both before and after the change that incorporated
the PHI? What happens to the history of
that file?
Consider also, the situation where a release is branched to the wrong place. Instead of moving the new branch, it is
branched again to the correct location, and instead of deleting the errant branch,
you decide to use "Destroy" to remove it permanently. What happens to your history of the correct
branch…is that gone? Does the correct
branch even exist since the basis for that branch was destroyed?
4. Version
Control Models
There are two primary version control models in use; the
Lock/Edit/Release model and the Copy/Modify/Merge model. For a description and comparison of the
benefits and drawbacks of each, visit Versioning Models.
The first is the Lock/Edit/Release model in which a user
must request exclusive access to a file in order to edit it, and then remember
to commit the change (and release the lock) to allow others to modify the file
in the same manner. This is the model
used by default in Visual SourceSafe and all work on individual files needs to
be serialized (one user after another).
The second model is the Copy/Modify/Merge model, in which
the user can edit any file at will after retrieving it from the server, but
must resolve any potential conflicts introduced by other users when committing
changes back to the server. This is the
model used by default in TFS and allows work from multiple tasks to progress in
parallel.
Users migrating from Visual SourceSafe should specifically
be aware of these changes. While in most
version control systems the term ‘checkout’ refers simply to the act of
creating a working copy from a specific URI and version on the server; in TFS,
this action means ‘make writable’ and retains the terms ‘Get Latest’ and ‘Get…’
in order to perform the checkout. When
you perform a ‘Check Out’ in TFS, it will not
retrieve any changes from the server (it only switches the read-only attribute
on the file) and by default, it will not
prevent other users from checking in their changes.
While TFS can be modified to work more like VSS, including
exclusive locking semantics, do not
set these options. You will reduce or
eliminate many of the benefits of parallel development and revert to a
serialized, unproductive working style.
One argument recently heard in favor of the
Lock/Edit/Release model is "Our team is diverse enough in our efforts and not
as tightly integrated as other teams. We
also are going to be growing again this year.
So, we don’t feel there is enough day-to-day collaboration and
communication to allow multiple individuals to work on the same file…call it a
fail-safe, if you will." This argument
is flawed. Many diverse and globally
distributed users use the Copy/Modify/Merge model daily (and simply would not
be able to work using the Lock/Edit/Release model). Take a look at any given Open Source Software
(OSS) project as an example. All code on
CodePlex, Google Code, SourceForge.net, and many other public and private
repositories are developed using this distributed model by groups of developers
who may even be unaware of each others’ existence in time and space. A very few concrete examples of highly
successful projects developed in this fashion include the Microsoft Enterprise
Library, NUnit, and the ASP.NET AJAX Controls.
We will enforce the multiple check-out option at the
Enterprise level and remove Team Project Administrators’ ability to adjust
these options if that becomes necessary.
The necessary level of collaboration to support large and geographically
dispersed teams is already enforced through Changeset Comments Policy.
5. Log
Messages and Changeset Comments
"All version control systems have to solve the same
fundamental problem: how will the system allow users to share information, but
prevent them from accidentally stepping on each other's feet? It's all too easy
for users to accidentally overwrite each other's changes in the repository." – Versioning
Models
The Copy/Modify/Merge model enables parallel development,
and is required in order to use modern programming tools effectively. For example, refactoring tools (built into
Visual Studio 2005) can modify large numbers of files with a single
change...this would not be possible in the Lock/Edit/Release model if any of
those files were locked (exclusively checked out) by another user. Even the simple act of adding a new file to
your project modifies at least two files (the project file and the file being
added) and potentially several more (designers, code-behinds, etc.).
The drawback of the Copy/Modify/Merge model, however, is
that people must communicate what
they’re doing. Good version control
systems provide the ability to enforce this communication by rejecting any
attempted commits unless they are accompanied with a message describing the
changes made or associated with a request for those changes.
Team Foundation Server can go one step further, if you
enable this option, and require that every set of changes be associated with a
requested Work Item. This implies that any change made to any business project would need to be preceded by a request for
that specific change and those requests and their associated changes can be
traced back to a business or technical requirement. These change requests would have to be itemized
in detail by a project manager or tech/team lead before they could be worked on
by developers.
Implementing Work Item tracking would require an extreme
level of discipline among every team member and places an additional and
unwanted burden on project managers and team leads that is not present in
current development models. As a result,
we feel that this implementation is simply unfeasible at this time. (As a side note, this is the default implementation
used in the MSF for CMMI process template for TFS.)
In lieu of requiring that every change be associated with a
request, we use and recommend an implementation where every change must be
associated with a description of that change.
This is known in TFS as the Changeset Comments Policy.
Every commit of any
set of changes in version control must
be associated with a comment describing why
the changes were made. This is a requirement
and must be understood and communicated clearly to all members of the team.
Unfortunately, TFS also gives individual users the option to
override this policy with each commit, but you must still specify the reason
why you are overriding the policy in order to perform the commit. Do not
attempt to override the policy and place the description of your changes in the
Policy Override Reason. This is an
inappropriate use of both the comment and the policy override.
The comment itself must describe why the changes were made
and/or the intent or goal in making those changes. Comments such as "sdf", "added changes",
"modified function", "daily check-in", etc. will be rejected and
must be corrected by the developer or, failing that, their team lead or Team
Project Administrator. Consider
what the comment will mean next week, or in three months, or two years from
now. Next to the comments before and
after the current set of changes, will it sufficiently convey to you or your
readers what changed and why? Examples
of good comments include "Corrected spelling errors in documentation", "Added
unit test project to solution", "Changed web.config to enable impersonation",
and "Removed deprecated BaseDAO.cs file – it is no longer needed or used in the
project".
The Enterprise Architecture team reviews the comments for
all commits to all Team Projects and may mark specific comments as bogus or
vague and uninformative, specified in the wrong location (Policy Override
Reason), or both.
When such issues are detected, and for every such issue detected, the Enterprise Architecture team will
respond with an e-mail describing the correct use of these features and copy
all Team Project Administrators for your project to inform them that this
information and policy should be redistributed to your team members and that it
is their responsibility to ensure that this practice is understood and
consistently applied.
When such messages are received, we suggest having the
original committer update their comment as soon as possible while their changes
are still fresh in their mind. If the
developer cannot be reached or does not respond in a timely manner, then the
responsibility falls on the team lead or Team Project Administrator to make
these updates, and this will often involve a ‘Diff’ of the existing changeset
with the previous changeset to determine what changed and why in order to
provide an appropriate comment.
Descriptive comments are required, and informative and
goal-oriented comments preferred. This
is the only means at our disposal to determine at a glance why changes have occurred on the project without
digging into the source files themselves.
If you cannot describe why you’re doing something, then you probably
shouldn’t be doing it (undo your changes instead of committing them).
6. Common
Terms
URI (n.)
The term URI (Uniform Resource Identifier) is used here to
refer to the path to an item in a version control system. Version control URI’s start with a dollar
sign character followed by a slash, followed by zero or more path components
separated by slashes, as in ‘$/Enterprise Architecture/trunk/ Application
Blocks/v2.0/Src’.
Branch (n., v.)
A branch is any independent line of development. In this document, the noun ‘branch’ is used
to refer to one or more paths in version control that share a common history
(see cheap copy). The verb ‘branch’ is
used to refer to the process of creating a cheap copy of a software solution at
a different path in version control.
BTT (n.)
The BTT structure refers to the /branches,
/tags
and /trunk
root directories commonly used in enterprise version control systems. Within TFS the BTT structure is created
inside each team’s Team Project.
Cheap Copy (n.)
A cheap copy is created when the version control server
creates a new URI and points the contents of that URI to an existing URI and
version. The end result is that no
actual data is copied, but from a user’s perspective, there now exists two
complete source trees containing the same data.
When a modification is made to Tree A (currently at version 534),
it begins to diverge from the source in Tree B, since the source in Tree B is
based on version 534. The next version
on either tree would be 536 and above.
When modifications are made to Tree B, it will diverge from Tree A in
the same manner.
Commit (n., v.)
A commit refers to the process of sending a set of changes to
the server. In a system that supports
atomic commits, either all of the changes are successfully relayed to the
server, or none of them are. TFS
represents such a system.
A clean commit
implies that the set of changes are all somehow related. For example, a ‘Rename…’ refactoring may
modify several – even hundreds – of individual files across several projects,
and be considered a clean commit when those changes are sent to the server with
the comment ‘Renamed Foo to Bar’.
A dirty commit
implies that there is no meaningful relationship in a set of changes. In the example above, if the developer also
added a new class to the project and failed to mention this in the comment (or,
as often happens, forgot what changes were made), this constitutes a dirty commit. Dirty commits should be avoided if possible
because they make it difficult to track which changes accomplished which goals.
Conflict (n.)
A conflict occurs when the system does not have enough
information to perform an automatic merge successfully. For example, if both the source and target of
a merge operation contained changes to the same line of code and each change
was different, a conflict would occur and the system would prompt the user to
decide how to handle the situation.
Merge (n., v.)
A merge refers to the process of incorporating a group of
committed changes to a different URI from the branch where the changes
occurred. The source of the merge is the
URI containing the changes that should be incorporated, and the target of the
merge is the URI containing the branch that you wish to update. When a merge is performed, all changes made
to the target branch are pending until the changes are committed; this allows
you to examine the results of the merge before making them permanent.
Working Copy
The working copy refers to the structure and files of a
project present on a user’s hard drive that are associated with equivalent
items stored in version control. Changes
in the working copy are not reflected on the server (or made available to other
users) until these changes are committed.
Shelve
Changes in the working copy can be shared with other users
by shelving the changes, instead of committing. Shelve can be used for code
review purposes.
7. Core
Version Control Structure using BTT
The main line of development is defined in /trunk,
although this is not where typical development occurs. The trunk should always contain the most
stable current release(s) of your software solutions. Some organizations insist that the trunk remain
in a perpetually deployable state and limit write access to the trunk to select
individuals responsible for preparing a release.
Offshoots from the main line of development, including new
versions that have not been deployed, are defined in /branches. Typical development occurs in one or more
branches in this sub-tree. There are two
fundamentally different kinds of branches based upon the intended use of the
branch; feature branches and version branches.
Feature branches are used to isolate potentially breaking
changes without unnecessarily affecting developers working in other
branches. The purpose of a feature
branch is to introduce and stabilize a new feature (or feature set) and then
merge the changes into a more stable branch.
As such, feature branches tend to be short-lived and are deleted after
the feature branch is merged.
Version branches are used to introduce a new line of
development while maintaining the existing version. The purpose of a version branch is to add/change/enhance/retire
functionality for the next release.
Bug-fixes and changes to the existing version are rolled forward to this
branch, and the version branch eventually becomes the current release and
promoted to the /trunk. The source of this branch – the original URI –
is eventually retired and then deleted.
The purpose of the /tags URI is to
contain ‘snapshots’ of your projects at various points in time that are deemed
interesting from a business perspective.
Typically, these ‘points in time’ will coincide with a release, but this
is not their only use. The important
thing to remember about tags is that they should be considered and treated as
read-only once they’re created. To make
any change at all to a tag branch defeats the purpose of the tag. In fact, if an automated build system is in place,
it should be the only entity with sufficient privileges to create a tag branch,
while everyone else has read-only privileges.
In this scenario, when a release is actually ready to be executed, the
build system would create a tag from the trunk, build the tag, and then perform
the deployment (if automated) or turn the results of the build over to a
release engineer for manual deployment.
Note that the value of the BTT structure rests in adherence
to its intended use and the semantics attached to each name in the
structure. If a release is performed
from sources in the /branches sub-tree, without first promoting it
to the /trunk,
or if the /trunk itself is broken for several days on
end while a major refactoring effort is underway, or if any change at all is
made to a /tags sub-tree; then the value of this
structure is significantly reduced.
While some of this can be enforced electronically (such as the correct
use of /tags
in the automated build example above), much of this relies on social contracts
that cannot be enforced through software.
The ongoing feasibility of this structure is highly
dependent on the ability to successfully relocate a project without making
changes to the project (e.g., to fix up reference paths, virtual directories, etc.). The recommended project structure addresses
this need by making each business project completely self-contained, requiring
only the necessary framework and compilers in order to build and test the
resulting application.
Please note that Visual Studio 2005 SP1 Web
Application and Web Service Application projects will require special handling
in this regard because of their dependency on IIS virtual directories, while
Web Site and Web Service projects do not.
8. Core
Project Structure
The core project structure as it is defined here is what you
should expect to see as shown in the /trunk sub-tree in
the BTT structure described above. The
variations in this structure as they are used in the /branches
sub-tree are noted in the branching tasks and scenarios.
The core project
structure defines at least two folders in a parent-child relationship. The parent folder holds the name of the
business project, while one or more child folders hold the names of the
targeted releases. The parent folder is
referred to in this document as the ‘project folder’, and child folders are
referred to as ‘release folders’. The project
folder will remain until the business project is retired (at which time it can
be deleted), while new release folders will be added or removed as necessary to
coincide with planned releases. At any
point in time, one of these release folders exists for each such release ‘in
the wild’ (in some public environment, even if that environment is internal).
It will be the release folders in this structure that are
themselves the source and target of branch and merge operations. The graphic to the left shows a naïve view of
what this structure might look like using a product that should be familiar to
all readers.
Using the example shown, let’s assume that Office 2003 (v11)
just shipped, Service Pack 1 (v11SP1) was already being ‘dogfooded’ internally
(equivalent to our TEST environment), and Service Pack 2 (v11SP2) was in active
development.
At that point, all the v11 directories would be present as
well as the v11SP1 directories (since it was released internally), but v11SP2
would only exist in the /branches sub-tree. Additionally, the RibbonToolbar project would
not even have been created yet because the business is still finalizing the
Office 2007 (v12) requirements and correlating user feedback – development
hasn’t begun on this version.
Developers would be working in their respective /branches
either on v11SP2 or responding to defect reports on v11SP1 and Quick Fix
Engineering (QFE) requests on v11.
Before each internal release of v11SP1, the changes would be merged into
their corresponding folders in /trunk,
stabilized, tagged in /tags, and released.
As v11SP1 readies for the CTP and Early Adopter Program
release (equivalent to our QA), v11SP2 is released internally and finally makes
it into /trunk. In addition, the v12 folders are created in /branches
by branching /trunk/[Project Name]/v11SP2 (as the most
current release) to /branches/[Project Name]/v12. Since v12 will incorporate a new technology
called a Ribbon Toolbar, a new project is created for it in /branches
and development on Office 2007 begins in earnest.
Some important takeaways from this model are:
- Active development
occurs only in /branches. The /trunk is
reserved for integration and stabilization purposes (the target of merges
from various sources), and /tags are
created for each actual release.
- Each business project
shown is independently testable. Any
release of ‘CommandBar’, even though it represents a common library (or
set of libraries) used by multiple products, doesn’t need the full-blown
‘Excel’ or ‘Word’ programs to be tested.
It only needs a simple test harness that exercises the API that
‘Excel’ and ‘Word’ are going to expect.
- The structure is
organized to the extent that automated tools can be provided to show a
list of business projects and, once selected, their target releases, to be
built, analyzed, tested or deployed at any time.
- The actual software
projects defined in these release directories are moved around…a lot
(branching, merging, branch promotion to trunk and then tags, etc.). These projects can be checked out by
anyone with appropriate access to any location on their machine and built
and worked on locally. In short,
everything inside one of the release directories is and needs to be
self-contained.
- A cycle exists between /branches
and /trunk.
Features and fixes are worked in /branches and
then branched to or merged into /trunk for
release. The branch in /trunk
is then branched back to /branches for
the next feature set or round of fixes.
- Clean-up is
important. Since each release
folder essentially represents a complete copy of the prior release with
some changes applied, it should be a regular practice to simply delete
your local copy of any release you’re not currently working on, and then check
it out again from the server when you need it. You would not want to check out the entire /trunk
– or worse, the entire repository ($/), since
you would receive a complete copy of every release of every project
currently in any environment.
9. Extended
Project Structure
Within each release folder, the actual components of a software
solution are defined in a particular structure that enables this self-contained
model. Fortunately, with appropriate
branching, this structure need only be defined once for each business project
(and even that can be automated or stored as a ‘template’ branch to reduce the
possibility of human error).
We make the assumption that you’re using Visual Studio 2005
to create your software. If the software
uses older technologies (especially the Visual Studio 6.0 suite of development
tools), then other guidelines may apply as these tools have a tendency to put
things where they want them.
The release folder contains, at the very least, a "Src"
directory that is intended to be the root of your Visual Studio solution. The Visual Studio *.sln file itself is
defined in this directory. The purpose
of the "Src" folder is to contain all of the source code assets for your software solution, including all of the
Visual Studio projects and files that are part of the solution.
If your solution uses any external file references that are not part of the application proper, such
as the Application Blocks, third-party libraries, or common libraries from
other project folders, these should be kept in a folder named "Lib" defined at
the same level as "Src" in the release folder.
The purpose of the "Lib" folder is to contain all of the external file references that are used
by various Visual Studio projects in your solution along with their
dependencies.
If there is a need to store developer-to-developer
documentation that is specific to the business project or release, this
documentation should be kept in a folder named "Doc" defined in the release
folder. The location of this folder
simply serves as a convenient place for developers to create and read documentation
about the software under development that will be versioned along with the software
itself.
Following these
recommendations, the release folder and its contents should appear something
like this:
Within the "Doc" and "Lib" folders, you may define any
additional internal structure that is appropriate for your application. Additional guidance for the structure of your
"Src" folder is provided in the Solution Structure section. Do not rename these folders or reinterpret
their meaning or purpose. Doing so will
prevent automated tools from accessing them or using them appropriately based
upon this naming convention.
Each Visual Studio project that references a binary
dependency in the "Lib" folder (or one of its subfolders) does so using a
relative path. This structure guarantees
that anyone can check out the release folder to any location on their hard
drive and immediately perform a build.
The relative paths in the Visual Studio projects will remain valid no
matter where the release folder may be located.
It is at this point that many developers start wondering
whether they can create a ‘shared’ library folder that can be referenced by
several projects, especially when the dependencies of one solution are the
output of another that is shared by several different business projects. On the face of it, this does make sense –
after all, common libraries = common location.
If other developers have the same structure on their machines, it should
work, right? …right? The following graphics
show variations of this theme in use today:
Do not reference
shared libraries from mapped drives or shared folders outside of the release
folder. This is exactly the situation
that should be avoided. There are
several reasons for this warning:
- Version management –
Let’s say that, using the graphic above, someone got Solution A up and
running and all (known) bugs finally worked out. They get the latest sources on Solution
A and rebuild in preparation of adding new functionality and suddenly
everything breaks. There are no
changes in the history of Solution A from the time it worked perfectly, so
what’s wrong? If this model were
used, then it is quite possible that someone updated Common Lib
incorrectly…you just don’t know. Note that if something does ‘suddenly’
break, you’re very lucky. Typical
issues like these don’t manifest themselves so readily, until they’re in
production and some piece of functionality that was previously tested
thoroughly is exercised by an end-user with the ‘unknown change’ in its
place. If something breaks, you
should be able to track it back to a change in the solution itself, or to
a change in environment (Add/Remove Programs, virtual directory deleted, hardware/network
issues, etc.).
- SCM practices and Audit
compliance – When Solution A (again, from the graphic above) is tagged and
deployed, the only information available about the external reference that
was deployed with it is the relative path where it existed at the time of
deployment. Which version of
CommonUtility.dll was deployed along with it? It is difficult to be sure, since Common
Lib was not included in the tag.
- Portability – Each
software solution should exist as a self-contained entity. Any time you refer to an item outside of
the solution root, you introduce a dependency that must be replicated to
every machine using the solution and you lose the ability to branch and
tag effectively. If projects in the
solution reference libraries from a directory shared among your team, then
these references may be suddenly
broken when you create a branch or tag, and will certainly be broken when another user who is not aware of this
dependency retrieves the solution or, even if aware, retrieves the
solution at a different local path.
- Unintended dependency
explosion – Each reference to a compiled library in your application may
be dependent on other references, which are dependent on others, and so
on. Unless these dependencies are
‘in your face’ and explicitly managed, by the time you’re ready to
actually deploy, you may be copying hundreds of assemblies to your deployment
folder just to satisfy a couple of project references. Each of these dependencies is a tight
coupling and a change in any one of them may inadvertently break your
application. What’s worse, these
dependencies may share a common dependency at different versions. For example, LibraryA references
LibraryB and LibraryC; LibraryB references version 1 of LibraryD, and
LibraryC references version 2 of LibraryD.
This will only be made apparent to the developer if they manage
these references explicitly. If it
is not made apparent and the code is deployed, things won’t work as
expected, because there can’t be two different versions of LibraryD in the
same directory.
Unfortunately, the shared library model, as attractive as it
may seem, becomes progressively more difficult to manage effectively over
time. The self-contained model, though
it requires a little more up-front work, becomes progressively easier to
manage. The Enterprise Architecture team
is updating the Reference Application with these guidelines, and to make this
explicit dependency management even easier, there is now an ‘UpdateExternalDependencies.cmd’
batch script in the "Lib" directory to pull in the latest version of the
Application Block references (and their references, etc.). The dependency management is still explicit,
but with a mouse-click we can now test the Reference Application with the
latest changes in the Application Blocks.
It is entirely appropriate to copy only the required shared
libraries from a mapped drive or common folder shared by your team members into
the "Lib" directory, especially when these components are maintained outside
your group. The key difference is that
this drive or common folder is not under version control (and should not be
under version control). For example, if
the Infragistics controls were purchased by your team, then a common directory
would be set up and shared with your team from which the purchased version
could be obtained. In order to be used
in your project, the specific DLL’s you need would be copied to your release’s
"Lib" directory and referenced from that location rather than the common
location. If a newer version was
obtained and placed in the common folder, it would not affect your application
until you were specifically ready for it, giving you the opportunity to build
and test with the newer version and resolve any issues that may arise from the
upgrade.
There is one appropriate use (and place) for shared
libraries in .NET…the Global Assembly Cache (GAC). The GAC shell extension (Fusion.dll) goes to
great lengths to hide the complexity of effectively managing multiple versions
of shared libraries, but for an illustration of the complexity involved, you
may use the following registry scripts to enable or disable this shell
extension. The GAC is viewable at
%windir%\assembly (C:\WINDOWS\assembly).
10.
Solution Structure
The contents of the "Src" directory are further organized
roughly by Visual Studio project types.
The "Src" directory itself contains the Visual Studio solution file and
other Solution Items, as shown in Visual Studio’s Solution Explorer.
The recommendations in this section are based largely upon
the needs expressed by build engineers (those responsible for building and
ultimately deploying the software) and enable automated tools and humans alike to
easily recognize deployable and non-deployable source code projects.
As such, the names and semantics provided should be used as
they are, but please consult with Enterprise Architecture to determine if new
‘project types’ should be added to this structure.
It should be pointed out that individual elements of this structure
should only be created if there is a need.
That is to say, do not create any of these folders unless it contains one
or more Visual Studio projects that match that project type; do not create the
entire structure and then leave parts of it empty.
A description of each
folder in this structure and its purpose follows, but the major project types
identified thus far can be expressed using the following terms: "Data",
"Hosts", "Installers", "Libraries", and "Tests". A graphic depiction of a solution containing
all of these project types is shown at right.
In your own projects, you may have only "Hosts" and "Libraries", or
"Hosts", "Libraries", and "Tests", or just "Data" by itself. Other combinations exist and you should use the
combination that is appropriate for your application.
I.
Data Folder
The "Data" folder is intended to contain database projects
and batch scripts used to create and initialize databases (i.e., database
‘source code'). This includes the Visual
Studio "Database Project" type and the new "SQL Server 2000" and "SQL Server
2005" project types introduced with Visual Studio 2005 for Database
Professionals. These projects typically
contain SQL scripts, database schemas, and other files required to create,
initialize, load, and change relational databases. There are advantages to using these project
types in order to store database configuration scripts in version control. The Visual Studio 2005 for Database
Professionals edition even extends the ‘Rename…’ refactoring support to database
object names and allows the database schema itself to be versioned along with
the rest of your solution.
The scripts found in these projects are typically used
during deployment to ensure that database changes are reflected in version
control (this can easily be accomplished by having SQL Enterprise Manager or
the SQL Server Management Studio script the database prior to each release and moving
the resulting scripts into one of these projects). These scripts may also be handed over to a
DBA to affect database changes in other environments.
II.
Hosts Folder
The "Hosts" folder contains your solution’s *.EXE and Web
Site/Web Application projects. Each
Visual Studio project in "Hosts" represents a deployable project and is, in
fact, deployed during a release. Each
host is independently "runnable" (or browsable, in the case of Web Site and Web
Application projects), and can be set as the "Startup Project" in Visual
Studio.
Each business project and release folder that will be
deployed should have at least one "Hosts" project defined, and multi-tier
applications typically have at least two hosts (Web tier and App/WebService
tier) and potentially many more. Keep in
mind that neither the solution nor the business project itself is deployed,
only "Hosts" are deployed. This implies
that a business project may encompass multiple ‘applications’ with shared core
functionality and a solution’s "Hosts" projects are not limited to a single
tier.
The term "Hosts" is used instead of an alternative such as
"Webs" or "Exes" to indicate that these projects should act as ‘hosts’ for the
application functionality defined in other project types (specifically
"Libraries"). No project in "Hosts"
should ever be referenced by another project (other than possibly "Tests"), and
any functionality defined in a host that is needed by other Visual Studio
projects should be moved into a re-usable library.
III.
Installers Folder
The "Installers" folder is used to contain and define the
installer(s) for your software solution, if the business project requires this. These projects include Visual Studio "Setup
Project", "Merge Module Project", "CAB Project", "Web Setup Project", "Setup
Wizard", and "Smart Device CAB Project" project types as well as other project
types used for this purpose (such as WiX, or Windows Installer XML, project
types).
IV.
Libraries Folder
The "Libraries" folder contains your solution’s Visual
Studio Class Library projects (and related project types, such as Windows
Control Library, Web Control Library, WCF Service Library and Windows Workflow
Library projects). This folder and its
contents comprise the majority of your application’s functionality and this
folder typically contains the largest number of individual projects in your
solution.
Any Visual Studio projects defined in the "Libraries" folder
are not themselves deployable, but rather expose functionality and services
that are consumed by other libraries or by projects in the "Hosts" folder. For example, there would be little value in
deploying the Application Blocks, in and of themselves, to a server in any
environment without a host (such as your application) that exercises their
functionality.
V.
Tests Folder
The "Tests" folder contains your solution’s Visual Studio
Test Projects along with unit and integration tests. These projects are libraries, but are treated
differently because they are typically not deployed and not subject to the same
analytics (Code Analysis, Code Coverage and Performance Profiling) as other
project types in your solution.
Testing guidance is outside the scope of this document, but
we strongly suggest that you
differentiate the test projects in this folder as containing either unit tests
or integration tests by suffixing the project name with ‘.Tests’ and
‘.Integration’, respectively. These
terms are defined as follows:
Unit Tests – Programmer-defined tests that exercise the
functionality of a class or method in
isolation. Unit tests require only
the code under test, the runtime and framework on which it depends, and a test
harness. Specifically, unit tests do not require any other products to be
installed or available (such as databases, performance counters, event logs, or
external services), or any type of network access.
Integration Tests – Programmer-defined tests that exercise
the functionality of a collaborative group of classes or components. Integration tests may require the additional
setup and use of external services and software in order to function.
As an example of the differences between these types of
tests, let us assume we wish to test a method that writes a row to the
database. The method’s signature is as
follows:
internal int AddCustomer(DbCommand command, string firstName, string lastName);
The unit test project would define a MockDbCommand
to pass in place of the DbCommand and used to test various scenarios.
The first set of unit tests would make sure that this method
throws the appropriate ArgumentNullException
if any of the parameters
were specified as null.
The second set of unit tests would make sure that the
appropriate DbCommand.ExecuteNonQuery();
command was
called inside the method and that it is populated with the appropriate DbParameter
objects filled in with the values of firstName and lastName.
The next set of unit tests would determine whether the
appropriate action was taken in the face of various possible errors that can
occur during the call to DbCommand.ExecuteNonQuery();
by simulating various error conditions such as DbException
(encompassing SqlException
, OleDbException
,
and OracleException
),
IOException
,
and others.
The final set of unit tests would determine that the return
value from this method represented the correct value (in this case, let’s say
that it is supposed to be the new Customer ID, and not the number of rows
affected). Our MockDbCommand.ExecuteNonQuery();
method will return the value of 1 (as the number of rows affected), but we add
an output DbParameter
to our MockDbCommand
named "@CustomerID
"
with the value of 5. With the test, we
verify that the return value from the method under test (AddCustomer
) is indeed 5.
In contrast, the integration tests for this method would
pass a real SqlCommand
to this method initialized with a
real connection to a real database.
The first integration test would pass a valid firstName
and lastName
to the method, and then read the row in the database indicated by the return
value to ensure that the row was added successfully (e.g., "
SELECT FirstName,
LastName FROM Customers WHERE CustomerID = @CustomerI
D"). This verifies the success scenario. Notice that this verifies the expected
interaction of the
AddCustomer
method along with
SqlCommand
,
SqlConnection
,
SqlParameter
,
and any other objects in the chain, right down to the database server itself
and the network required to communicate with it.
The next set of integration tests would verify the expected
behavior of various failure scenarios (for example, by modifying the SqlCommand
’s
Connection
property to point to an invalid or non-existent database, or attempting to add
the same customer twice and causing a unique key violation).
Both types of tests are required to fully specify the
expected behavior of the system under test.
VI.
Visual Studio Project Layout
One or more Visual Studio project folders will exist in each
of the folders described above; one folder per project. Do not nest these internal project folders. If additional organization is required, use
the ‘Solution Folders’ feature in the Visual Studio Solution Explorer to
represent this additional organizational structure. Solution Folders do not map physically to
folders on your hard drive or in version control, and simply exist as a
convenient organizational mechanism within a Visual Studio solution. We do recommend that you mimic the physical
layout described above using Solution Folders, but you may add to this as you
see fit.
To illustrate, let’s examine a claims processing pipeline
software solution as a scenario. This
hypothetical solution incorporates a Windows thick client for paper-based claims
entry, Web Services for providers to submit claims electronically along with a
Web application for manual entry, as well as a Web application used internally to
provide statistical management information, a Mobile web application used to
provide alerts and notifications to Blackberry devices for application health
monitoring, and Windows and Web services to provide back-end processing and
batch processing capabilities.
Such a
system might have the following projects and structure. Note that this solution represents a fairly
complex set of applications, and additional *.sln files would likely be present
in the "Src" folder that reference only those projects necessary for the
current development effort in order to provide a more focused view.
11.
Windows MAX_PATH Limitations and Team Build
The Windows® operating systems have historically (through
Vista™, at least) had a limit on the number of characters that can be
represented in any file system path.
This limit, set at 260 characters, is generally well within reason, but
as folders and files may themselves have long, descriptive names, you may run
into this limitation, especially during build scenarios.
The version control provider itself does not have these same
limitations, so it is quite possible to have deeply nested folders under
version control, each with long, descriptive names. This would result in items in source control
that cannot be checked out to a local machine in the same structure and is a
situation to be avoided.
Team Build also uses the concept of a ‘Build Type’ to define
various build options and solutions to be built together. By default, when the build server checks out
items to be built, it will follow the same structure that is used in source
control (starting at the Team Project root), but the root of the check out will
be ‘F:\MyBuilds\[Team Project Name]\[Build Type Name]\Sources\’ (where [Team
Project Name] and [Build Type Name] corresponds to your Team Project and Build
Type names, respectively). This has the
effect of appending 22 + Team Project Name length + Build Type Name length characters
to the version control URI length, and limiting useful paths to well under 238
characters. For example, the Enterprise
Architecture group defines a build type of AB (Application Blocks), limiting
the maximum useful path of any item in version control to under 212 characters. Longer Team Project and Build Type names only
exacerbate this problem.
These issues and limitations are mentioned just to raise
awareness. Individual projects within a
Visual Studio solution have in the past been created with descriptive names
such as ApplicationBlocks.UIPPersistence and/or
Microsoft.Practices.EnterpriseLibrary.Configuration; creating both a folder and
file (*.csproj) of that name, making these paths correspond to
ApplicationBlocks.UIPPersistence\ ApplicationBlocks.UIPPersistence.csproj and
Microsoft.Practices.EnterpriseLibrary.Configuration\Microsoft.Practices.EnterpriseLibrary.Configuration.csproj,
respectively. It may be prudent to
create the project with a shortened name (such as UIPPersistence and Common.Configuration
for the examples above), and then rename the project and set the root namespace
and assembly name properties explicitly.
12.
Scenarios
Your team has just landed a new project (or is converting a
VB6/ASP/COM application) and a home for development needs to be created in
version control. The project’s name is Medical
Records Request Tracking and, for brevity, is known among the team as simply
MRRTracking. The first planned release
of this application is the 8.2 release (Feb. 2008). Your Team Project name in TFS is ‘Provider
Services’.
Using your version control tools, you would add a new project
folder named ‘MRRTracking’ to the $/Provider Services/branches
URI to make the $/ProviderServices/branches/MRRTracking
URI. To that folder, you would add your
release folder named ‘v8.2’ to make the $/ProviderServices/branches/MRRTracking/v8.2
URI.
At this point, commit your changes with the comment ‘Created
Medical Records Request Tracking project and release folder’. This notifies any subscribers what these
folders are for and the project they will contain and will serve as a reminder
for anyone viewing the history of this project.
Notice that the abbreviated form ‘MRRTracking’ was not used in the comment.
Now that the release folder has been created, go ahead and
add the Doc and Lib folders and place within the Doc folder a simple text file
named ‘Glossary.txt’ containing the following line:
MRRTracking = Medical Record Request Tracking
This can serve as the project glossary and should be the
first place new contractors, developers, and reviewers look when they’re
unfamiliar with a term. Understand that
this code will be looked at by
someone who is not familiar with your project (reviewers, at the very least),
and in the case of off-shore contractors, may even be developed by someone who
is unfamiliar with culture and terminology in general or the project itself may
be handed to a different team entirely.
The acronym CIM will mean Common Information Model to developers
familiar with Windows Management Instrumentation (WMI), it will mean Corporate
Information Management to others.
Distinctions like this must be made in an easily accessible location
available to all team members. It makes
sense to streamline communications with seasoned team members who were ‘in on
the joke’, but corporate and team jargon is nothing short of gobbledygook to
others. Providing the means for
self-help in making sense of it is an extremely valuable productivity gain that
prevents miscommunication and constant interruption of other team members.
The following URI’s should now be in an ‘added’ state (not
yet committed):
$/Provider Services/branches/MRRTracking/v8.2/Doc
$/Provider Services/branches/MRRTracking/v8.2/Doc/Glossary.txt
$/Provider Services/branches/MRRTracking/v8.2/Lib
Commit your changes with the comment ‘Added project glossary
and Lib’ or, for a ‘cleaner’ commit, commit only the folder changes with the
comment ‘Added Doc and Lib folders’ and then the addition of Glossary.txt
(still a pending ‘add’) with the comment ‘Added project glossary’.
Next, in the release folder, create a new, blank solution
named ‘Src’ in Visual Studio (this will create the ‘Src’ directory) and
immediately rename the solution. You may
use either MedicalRecordRequestTracking or MRRTracking for the solution name;
whatever will be understood more clearly, first considering new developers and
contractors who may be brought into the project at some future date, and then
considering those who will be viewing it on a daily basis. These considerations are prioritized because
you will likely want to get any new staff up to speed quickly, rather than
spending hours or days ‘giving them the tour’ or worse, giving them no
information at all.
After renaming the solution, you may add any of the Solution
Folders you may need for this project.
Keep in mind that Solution Folders do not map to physical folders; you
will still need to add the appropriate solution structure folders described
earlier as you add projects into those folders.
Add/commit the initialized solution to version control with
the comment ‘Created initial project structure’. Such a solution may have the following URI’s
(many are not shown, such as the individual *.csproj files and AssemblyInfo.cs
files and Properties folders):
$/ProviderServices/branches/MRRTracking/v8.2/Src
$/ProviderServices/branches/MRRTracking/v8.2/Src/MedicalRecordsRequestTracking.sln
$/ProviderServices/branches/MRRTracking/v8.2/Src/Hosts/Web
$/ProviderServices/branches/MRRTracking/v8.2/Src/Libraries/Controllers
$/ProviderServices/branches/MRRTracking/v8.2/Src/Libraries/ClientServices
$/ProviderServices/branches/MRRTracking/v8.2/Src/Libraries/Common
$/ProviderServices/branches/MRRTracking/v8.2/Src/Libraries/Core
II. Scenario #2 – Existing Project, Migration to TFS
Your team has an existing project in one of server environments or a thick client application in the hands of users or testers. The source for this application
is currently in VSS and you want to move it to TFS. The existing application in the ‘public view’ (testers, end-users) is the 1.0 release, but work has already begun on the 2.0
release. You have been following the prior EA guidelines and created a ‘Branch’ in VSS for these releases, but the 2.0
release has had a few critical bug-fixes applied and off-cycle turns. The project name is Turn Center Wizard, but people on the team call it TCW. Your
Team Project name in TFS is ‘Enterprise Architecture’.
First, inform your team that you will be moving the 2.0
release to TFS and have everyone get to a point where they can check in their
changes and the project will compile.
Additionally, ensure that everyone who will be working on the project
has the appropriate Team Foundation Server tools installed.
Delete any existing 2.0 release directory from your hard
drive and perform a "Get Latest…" action on this branch from VSS.
Open the 2.0 release (still bound to VSS), and first make
sure that it compiles. If it doesn’t,
then make any changes that are necessary while it is still bound to VSS until
you can get a good build. After checking
in all changes made, contact your VSS administrator (typically the EA team) to
set all access on the project to ‘read-only’ and undo any remaining checked out
files. This step will prevent any
changes from being made to the project in VSS until and after it is migrated. After the VSS administrator has completed
this task, perform another "Get Latest…" action (just in case anyone else made
edits before the project was set to read-only), and re-verify that you have a
good build.
The final act before closing this solution and saying
good-bye to VSS is to unbind the solution from VSS. Use the "File|Source Control >|Change
Source Control…" menu option to open the "Change Source Control" dialog and
"Unbind" each project and the solution.
Close the open solution, saving any changes, and set your Source Control
plug-in to Team Foundation Server.
Using your version control tools, add a new project folder
named ‘Turn Center Wizard’ to the $/Enterprise Architecture/branches
URI to make the $/EnterpriseArchitecture/branches/Turn Center Wizard
URI. To that folder, you would add the
release folder named ‘v2.0’ to make the $/EnterpriseArchitecture/branches/Turn
Center Wizard/v2.0 URI.
Next, create the ‘new’ project structure folders (Doc, Lib,
Src) to get the following URI’s:
$/Enterprise Architecture/branches/Turn Center Wizard/v2.0/Doc
$/Enterprise Architecture/branches/Turn Center Wizard/v2.0/Lib
$/Enterprise Architecture/branches/Turn Center Wizard/v2.0/Src
All of the folders created so far are still in a pending
‘added’ state, but are present in your working copy. Copy the solution and project folders from
your 2.0 VSS working folder into your Src folder in the TFS working copy.
Next, create the solution structure folders in your Src
directory and drag or move each project folder into its corresponding solution
structure folder (e.g., *.exe and Web applications into "Hosts", libraries into
"Libraries", any "TestCases" projects into "Tests", etc.).
Use Notepad or some other plain-text editor to open the
solution file itself and correct the location where it will look for each of
the project files.
Save your changes and then
open the solution to verify that it can find each of the projects. Do not
attempt to build at this time. When all
of the projects are represented (and loaded) in Solution Explorer, right-click
on the solution and choose the menu option "Add Solution to Source
Control…" This registers the solution
file and project structure as pending ‘adds’ with the version control provider.
If you open the ‘References’
node of any given project, you will probably see a number of broken
references. These are the external
binary references used by your project that should be placed in the "Lib"
directory.
Copy each external reference
and its dependencies into the "Lib" folder, and update each project to retrieve
the appropriate reference from that folder.
You may also wish to place
the VBScript file below into your "Lib" folder and modify any Web Application,
AJAX Web Application, and Web Service Projects to execute it prior to building
the project. This will ensure that the
appropriate IIS directories and virtual directories are created for the project
and pointed to your project’s root folder.
This script belongs in the "Lib" folder because it is an external file
reference (albeit a build reference, and not a project reference), and it is
not part of your solution’s source code.
The necessary projects can be
updated by right-clicking on the project and choosing the ‘Unload Project’
context menu item. After the project is
grayed out (unloaded), then right-click on the unloaded project and choose the
‘Edit …’ context menu item.
This will show the actual
MSBuild project file used for the project.
Toward the bottom of the file you will see an area that has been
commented out (shown below).
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
Uncomment this section and replace the BeforeBuild target with the following (beware line breaks in the Exec command…there are none in this file fragment):
<Target Name="BeforeBuild" DependsOnTargets="GetFrameworkPaths">
<Exec Command="cscript.exe "..\..\..\Lib\EnsureIISVDir.vbs" "$(MSBuildProjectFullPath)" "$(FrameworkDir)" />
</Target>
No matter where the project is located on your hard drive or what IIS virtual directory you use, you can now build and run it within IIS. The above
script also sets the Framework version in IIS to the current version being used by Visual Studio.
Now, you just need to build your project to verify that all required assemblies and scripts have been placed in the Lib folder. Correct any
errors that may occur, and then commit the whole project with the comment "Migration of Turn Center Wizard from VSS".
III. Scenario #3 – Clean First Release
Your team manages the Medical Records Request Tracking project, known among team members as MRRTracking. You are the Build Engineer for that project,
and your Team Project name in TFS is ‘Provider Services’. It’s mid-February, 2008, and the project’s requirements have been met for the 8.2 release and the code review came back
clean on Changeset 738. All developers on the project have their code checked in and are over at TGIFriday’s having
beers and congratulating each other on a job well done. You only need to complete this one final task before you can join them.
The project itself is new and has never been released
before, so no directory exists for it in the /trunk. This is the 8.2 release, and the next release
is scheduled for 8.4.
Using your version control tools, navigate to the current development
folder $/ProviderServices/branches/MRRTracking/v8.2
and perform a branch operation. The UI
for your version control provider will ask which Changeset you want to branch
(defaulting to the most recent) and where you want to branch it to.
Explicitly set the Changeset to 738, as the version that was
reviewed, and set the target branch to $/ProviderServices/trunk/MRRTracking/v8.2. If you have the opportunity to switch to or
create a local working copy out of the target branch, then set that option and
execute the branching operation.
A copy of the software solution as it existed in Changeset
738 will be created on your hard drive and in version control at the target
branch URI. Open the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder and verify that it compiles. (If
it doesn’t, delete the branch and call some of those developers back into the
office to fix their code.)
Commit the branch to version control with the comment
‘Initial 8.2 release of Medical Records Request Tracking’.
Next, select the release folder at $/ProviderServices/trunk/MRRTracking/v8.2
and perform another branch operation.
Again, the UI will ask which Changeset you want to branch (defaulting to
the most recent) and where you want to branch it to.
This time, leave the default Changeset (latest version), and
set the target branch to $/ProviderServices/branches/MRRTracking/v8.4. If you do not need to work on this branch,
then do not set the option to switch to or create a local working copy of
it. Now execute the branching operation.
A copy of the 8.2 release will be created in version control
at $/ProviderServices/branches/MRRTracking/v8.4.
Commit the newly created branch with the comment
‘Initialized 8.4 development from r738’ and blast out an e-mail to the entire
team informing them that all 8.4 functionality should be developed in this
branch and only bug-fixes should be worked on in the 8.2 branch.
If you have an automated build system in place, instruct the
build system to build and tag the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder (and optionally, if it also performs the deployment, where to deploy
each Hosts project it finds). If this is
the case, then you’re done, and can head over to TGIFriday’s and join the
team. If not, then continue reading for
the manual steps.
IV.
Scenario #4 – Dirty First Release
Your team manages the Medical Records Request Tracking
project, known among team members as MRRTracking. You are the Build Engineer for that project,
and your Team Project name in TFS is ‘Provider Services’. It’s mid-February, and the project’s
requirements have been met for the 8.2 release and the code review came back
clean on Changeset 738. The developers have
noticed and have been busy fixing a few bugs that they found after the code
review and refactoring some of the code that they still want to get into the
8.2 release...no work has started on the 8.4 release and no branch yet exists
for it. The latest Changeset is 813 and
this is the version you have been instructed to release. All changes have been committed by all team
members.
The project itself is new and has never been released
before, so no directory exists for it in the /trunk.
Using your version control tools, navigate to the current development
folder $/ProviderServices/branches/MRRTracking/v8.2
and perform a branch operation. The UI
for your version control provider will ask which Changeset you want to branch
(defaulting to the most recent) and where you want to branch it to.
Explicitly set the Changeset to 813, and set the target
branch to $/ProviderServices/trunk/MRRTracking/v8.2. If you have the opportunity to switch to or
create a local working copy out of the target branch, then set that option and
execute the branching operation.
A copy of the software solution as it existed in Changeset
813 will be created on your hard drive and in version control at the target
branch URI. Open the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder and verify that it compiles. If
it does not, then delete the branch and have the developers fix their code.
Commit the branch to version control with the comment
‘Initial 8.2 release of Medical Records Request Tracking – Reviewed at r738’. This indicates to any onlookers that the code
being released is not necessarily the code that was reviewed (this will also
CYA when auditors ask why the EA team reviewed 738 and you released 813).
Next, select the release folder at $/ProviderServices/trunk/MRRTracking/v8.2
and perform another branch operation.
Again, the UI will ask which Changeset you want to branch (defaulting to
the most recent) and where you want to branch it to.
This time, leave the default Changeset (latest version), and
set the target branch to $/ProviderServices/branches/MRRTracking/v8.4. If you do not need to work on this branch,
then do not set the option to switch to or create a local working copy of
it. Now execute the branching operation.
A copy of the 8.2 release will be created in version control
at $/ProviderServices/branches/MRRTracking/v8.4.
Commit the newly created branch with the comment
‘Initializing 8.4 development from r813‘ and blast out an e-mail to the entire
team informing them that all 8.4 functionality should be developed in this
branch and only bug-fixes should be worked on in the 8.2 branch.
If you have an automated build system in place, instruct the
build system to build and tag the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder (and optionally, if it also performs the deployment, where to deploy
each Hosts project it finds).
V.
Scenario #5 – Truly Dirty First Release
Your team manages the Medical Records Request Tracking
project, known among team members as MRRTracking. You are the Build Engineer for that project,
and your Team Project name in TFS is ‘Provider Services’. It’s mid-February, 2008, and the project’s
requirements have been met for the 8.2 release and the code review came back
clean on Changeset 738. The developers
have noticed and have been busy fixing a few bugs that they found after the
code review and refactoring some of the code that they still want to get into
the 8.2 release. Some developers have
even begun implementing 8.4 features and have committed changes for that
release in the 8.2 folders. The latest
Changeset is 906. All changes have been
committed by all team members.
The project itself is new and has never been released
before, so no directory exists for it in the /trunk.
Using your version control tools, navigate to the current development
folder $/ProviderServices/branches/MRRTracking/v8.2
and perform a branch operation. The UI
for your version control provider will ask which Changeset you want to branch
(defaulting to the most recent) and where you want to branch it to.
Explicitly set the Changeset to 738, as the most recently
reviewed, and set the target branch to $/ProviderServices/trunk/MRRTracking/v8.2. If you have the opportunity to switch to or
create a local working copy out of the target branch, then set that option and
execute the branching operation.
A copy of the software solution as it existed in Changeset
738 will be created on your hard drive and in version control at the target
branch URI. Open the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder and verify that it compiles. If
it does not, then delete the branch and have the developers fix their code, as
well as have it resubmitted for review in the hopes of a clean first release.
Commit the branch to version control with the comment ‘Preparing
8.2 release of Medical Records Request Tracking – Reviewed at r738’. This indicates to any onlookers that the code
being released is not necessarily the code that was reviewed.
Next, select the development folder at $/ProviderServices/branches/MRRTracking/v8.2
and perform a ‘Merge…’ operation. The UI
for your version control provider will ask for the source and target branches
of the merge and whether you want to merge all changes or only selected
changes. At this point, select the
option to merge only selected changes, and set the target of the merge to your $/ProviderServices/trunk/MRRTracking/v8.2
release folder.
The merge UI will show you all of the changes that have
taken place on the project since the branched version (738). At this point, you can cherry-pick the
changes that need to be in the 8.2 release.
See the screenshot below for an example of this UI. Note the value of good comments and the value
of clean commits in making your decision (what should be in the 8.2 release vs.
what should be in the 8.4 release).
Once you have selected the Changeset(s) to merge, execute
the merge. This will update the files in
your local working copy with the specified changes. Open the solution at the target branch,
verify that it builds correctly, and then commit the changes with the comment
‘Merged changes r###-r###), where ### are the starting and ending Changesets
being merged.
Repeat the merge process from your 8.2 development folder to
your 8.2 release folder in the /trunk for any
non-contiguous set of changes that need to be in the 8.2 release.
Once you are satisfied that the 8.2 release is ready and all
changes specific to it have been incorporated (while excluding any changes
specific to 8.4), take note of the changeset number (let’s say that it is 916
at this point) and select the release folder at $/ProviderServices/trunk/MRRTracking/v8.2
and perform another branch operation.
Again, the UI will ask which Changeset you want to branch (defaulting to
the most recent) and where you want to branch it to.
This time, leave the default Changeset (latest version, 916),
and set the target branch to $/ProviderServices/branches/MRRTracking/v8.4. This time, do set the option to switch to or
create a local working copy of the new branch and execute the branching
operation.
A copy of the ‘real’ 8.2 release will be created in version
control at $/ProviderServices/branches/MRRTracking/v8.4.
Commit the newly created branch with the comment
‘Initializing 8.4 development from r916‘ and blast out an e-mail to the entire
team informing them that all 8.4 functionality should be developed in this
branch and only bug-fixes should be worked on in the 8.2 branch.
At this point, the 8.2 development branch still contains
changes that are specific to 8.4, so we need to get them merged into the 8.4
development branch and revert the existing 8.2 development branch to match what
is currently in the /trunk.
Select the development branch at $/ProviderServices/branches/MRRTracking/v8.2
and perform another ‘Merge…’ operation.
This time, the target of the merge is $/ProviderServices/branches/MRRTracking/v8.4,
but we still want to select specific changesets to merge.
Because TFS tracks merges, the UI should show you only those changes that were not previously merged
into the 8.2 release. Select all of the
relevant changesets and execute the merge.
This will update the files in your local working copy of the
8.4 development branch with the changes made in 8.2 that were specific to
8.4. Commit the changes with the comment
‘Merged r###,r###-r###,r###’, where ### is the changeset number of each
changeset, or starting and ending changeset numbers of contiguous changesets,
that were included in the merge.
Now that the 8.2 release folder in /trunk
contains what it should, and the 8.4 development folder in /branches
contains what it should, we can set the 8.2 development folder in /branches
to match the release.
In a good version control system, you can’t "Undo", but you
can always overwrite. We will use that
fact to undo the changes made in 8.2 that were specific to 8.4.
Select the 8.2 release folder at $/ProviderServices/trunk/MRRTracking/v8.2
and perform yet another ‘Merge…’ operation.
Set the target of the merge to be the $/ProviderServices/branches/MRRTracking/v8.2
development folder, then select all changes and execute the merge.
After the merge operation completes, the 8.2 development
folder now matches, directory for directory, file for file, line for line, the
content in the 8.2 release folder, effectively undoing those changes that were
specific to 8.4. Commit these changes with
the comment ‘Merged from r916 to undo 8.4-specific changes’.
If you have an automated build system in place, instruct the
build system to build and tag the solution in the $/ProviderServices/trunk/MRRTracking/v8.2/Src
folder (and optionally, if it also performs the deployment, where to deploy
each Hosts project it finds).
VI.
Scenario #5 – Initial Development
Your team manages the Medical Records Request Tracking
project, known among team members as MRRTracking. You are a developer on that project, and your
Team Project name in TFS is ‘Provider Services’. The project has been defined in version
control at $/ProviderServices/branches/MRRTracking/v8.2
and the solution has been created in the corresponding "Src" folder.
In order to work on this project, you will first need to
obtain a copy of it from the server.
Using your source control tools, navigate to the $/ProviderServices/branches/MRRTracking/v8.2
development folder and check out the project from TFS by performing a ‘Get
Latest’ action.
This will create your local working copy of the project’s
sources and dependencies at some location on your hard drive. You may now open the solution file either
from Windows Explorer or from within Source Explorer. Continue reading the Day to day Development
scenario for additional tasks.
VII.
Scenario #6 – Day to day Development
Your team manages the Medical Record Request Tracking
project, known among team members as MRRTracking. You are a developer on that project, and your
Team Project name in TFS is ‘Provider Services’. The project has been defined in version
control at $/ProviderServices/branches/MRRTracking/v8.2
and the solution has been created in the corresponding "Src" folder. You already have a local working copy of the
project and you need to make some changes or implement new functionality. You currently have no uncommitted changes in
your working copy.
Using your source control tools, update your local sources
with any changes that may be present on the server ("Get Latest" in TFS), and
then open the solution either from Windows Explorer, the IDE’s Most Recently
Used list, or the Source Explorer.
The very first action that should be taken after opening the
solution with a recent source update is to perform a local build. This action guarantees that you will be aware
of the project’s current state and not waste time adding functionality or
features to a non-functional project.
The local build, or compilation step, serves as your ‘smoke test’ (a
term borrowed from the electrical engineering discipline, where a component is
‘tested’ by plugging it in…if it starts to smoke, then something is obviously
wrong, if not, then it may not work, but nothing is obviously broken).
If the project does not compile, contact the most recent
contributor to the project. You can find
this information and the name or e-mail address of the person you need to
contact by viewing the history for the branch using your source control
tools. You may see output similar to the
following screenshot:
Given the user ID in the user column, you can determine which
user made the change by running the following command (replacing sxd7278 with
the user ID of interest):
Once you know who the most recent contributor is, call or
send them a message asking them if they have completed their commits and
informing them that the project does not compile. This may simply be a situation where the
other contributor is in the middle of a multi-step task and has not yet committed
all parts of the task, or it may be that the contributor unwittingly introduced
a dependency that is only available on their machine.
If the contributor is reachable, then work through any
issues with them until the project is stable and perform another update of your
sources and build.
In many cases, it may not be possible to reach out to the
contributor immediately. To name only a
few reasons; time of day, time zone differences, meetings, transfers and
terminations can all affect the availability of the most recent
contributor. In these cases, instead of
stopping your work, you must get the most recent functional version of the
project before making your changes.
13.
Additional Material
Please read the following material for additional sources
and justifications for the guidance presented in this document. Software
Configuration Management (General)
[1]
"Lib" folders commonly contain subfolders that correspond to particular
third-party vendors, such as Infragistics, Oracle, Log4Net, etc. This sub-structure makes it simpler to update
each individual vendor’s library set when new versions are acquired.