Feeds:
Posts
Comments

Build and Deployment Practices

Goals

  • Manage concurrent development efforts of the team effectively.
  • Manage product quality by rigorous unit and functional testing.
  • Maintain the ability to deploy any version of the application and related database schema and base-line data at any time to any environment.
  • Provide the ability to address defects identified in production while continuing to add new functionality slated for future releases.
  • Be able to respond to defects in User Acceptance Testing (UAT) or production without injecting new functionality into the deployed version of the application.
  • Keep the main code line (development branch) synchronized with any defect fixes made in a release branch.
  • Provide the ability to report on the status and progress of all development and deployment operations.

Principles

  • Source code is the source of truth (i.e. Golden).
  • Maintain fidelity between the source code and the delivered product.
  • Remove the chance for error everywhere in the process by using automation and implementing process controls where possible.
  • Make the management and tracking process simple and easy for developers (low friction).
  • Audit (log) all build and deployment activities.

Versioning

The continuous integration process increments the build label each time it builds the solution. We use the build version number to track the build, build metrics, integration test results, and deployments.

The version number uniquely identifies each build. When we have a build with new stories ready for testing, we select the build in TFS, set the build quality to “Ready for Initial Tests”, and the functional tester selects the build, creates the deployment, and starts functional testing the build. When the functional tester is finished testing the new stories and regression testing the application, the build quality is marked “Ready for Deployment” and can be deployed to production.

To facilitate identification and tracking of the application versions the following number schema is used:

{Major}.{Minor}.{Build}.{Revision}

e.g.: 1.0.123.0

e.g.: 1.1.343.0

e.g.: 1.1.343.1

The major version number designates the current release branch we are working on. The major version number is incremented each time we have a new release to production. The major version is tied to the release number we establish for a group of features within TFS.

The minor version number indicates the sub-release for the designated branch. We increment the minor version number when deploying production fixes made to a code branch that is already in production.

The build number indicates the specific build we are working with. We increment the build number each time we build the product. With continuous integration, we may build the product several times a day. Each build attempt will have a new build number associated with it.

The revision number indicates a ‘patch’ release. We don’t expect to need patch releases and will not use the revision number at this time.

Branching Strategy

We use a Branch for Release strategy where we create a new branch each time we release a version of the application to production. The release branch gives us the ability to fix production issues and re-release without adding additional functionality from the next sprint into the build.

Branching Model

Branching.png

Note: we don’t need to actually create the physical branch for the release when we release the application. We can create the branch from the label if we need to make changes and re-deploy the release branch during the next sprint. This allows us to only branch the source tree when absolutely needed.

Continuous Integration and Version Build Use Cases

1. Continuous Integration (CI) Build

The continuous integration build retrieves the latest version of the source code from the mainline (development) branch in source code, updates the version number in the assemblies, executes the full set of database scripts against the development (integration) database, compiles the source code, performs the static code analysis, executes the unit tests, and code coverage analysis. If all these steps execute without error, we consider the build successful and the build reports indicate success and all build metrics.

The continuous integration build ensures the solution compiles and unit tests execute properly. We want to ensure that all the code changes made by the developers on the team integrate properly. If changes break the build, we want to know as quickly as possible so the developer that checked in the breaking change can resolve the issue immediately. Since we won’t be deploying the CI build for testing, we won’t label the version in the source repository and we won’t update the version number in the App.Config file.

2. Test Version (New Version) Build

When we are ready to test a new version, we initiate the build of a new version of the application and prepare the application for deployment to the test environment by creating a ClickOnce deployment package. To do this we increment the version number, apply a label with that version number to the main (development) branch in the source repository, retrieve the source files, set the value of the version number in the App.config file, update the value of the database connection string in the App.config file, execute all database package scripts against the Test database, update the assembly version number for all assemblies, compile, analyze, and unit test all assemblies. With the assemblies created and tested, we then build the ClickOnce deployment package, modify the link on the ClickOnce deployment HTML page, and copy the deployment files to the deployment location (currently on the TFS01 server).

If the build succeeds, we have a new version of the application ready to be installed and tested. The tester can install the new version of the application on a Virtual Machine for testing, or directly on their workstation.

3. Production Version Build

Once a version of the application has been through a full suite of functional and user acceptance tests and is ready to be deployed to production, we build the specific version of the application and create a ClickOnce package for deployment to production.

To create the versioned build, the build master selects the labeled version to create, and initiates the build process. The build process retrieves the files tagged with the specified version number from the source repository, sets the version number and the connection string information in the App.config file, updates the assembly version number information, compiles the code, performs static analysis, executes the unit tests and code coverage analysis, creates the ClickOnce deployment packages, updates the ClickOnce HTML page link information, and copies the deployment pack to the deployment location.

BuildFlow.png

Using Team Build to manage the build process

Requirements of TFS

TFS’s Team Build requires the following:

· A unique name (number) for each build.

We must:

1.) Store the current increment number in a common location on the build or drop server.

2.) Increment the build number when the build kicks off.

3.) Label Source control with the build number generated.

4.) Override the Target in TFS – “BuildNumberOverrideTarget”.

5.) Remember every build number must be unique. (Build name must be unique.)

a. We may need to use something like “{BuildDefinitionName}_1.0.0.0″ to define a build that we do for CI and a build we do for Testing, Or Production.

Note: Custom Build Number Generator – uses file stored in build drop section, and DLL Placed in the Build Directory in Source Control – and referenced in the MS Build file on the server.

We Override the <Target Name=”AfterGet” /> target to get the assembly info files and update the version number of each assembly to match the build version number.

<ItemGroup>
  <AssemblyInfoFiles Include="$(SolutionRoot)\**\assemblyinfo.cs" />
</ItemGroup>
<Target Name="AfterGet">
  <!-- Update all the assembly info files with generated version info -->
  <Message Text="Modifying AssemblyInfo files under
                                         &quot;$(SolutionRoot)&quot;." /> 

  <Attrib Files="@(AssemblyInfoFiles)" Normal="true" />
  <FileUpdate Files="@(AssemblyInfoFiles)"
              Regex="AssemblyVersion\(&quot;.*&quot;\)\]"
              ReplacementText="AssemblyVersion(
                                &quot;$(VersionMajor).$(VersionMinor).
                                    $(VersionService).$(VersionBuild)
                                &quot;)]" />
  <FileUpdate Files="@(AssemblyInfoFiles)"
              Regex="AssemblyFileVersion\(&quot;.*&quot;\)\]"
              ReplacementText="AssemblyFileVersion(
              &quot;$(VersionMajor).$(VersionMinor).
                    $(VersionService).$(VersionBuild)
              &quot;)]" />
  <Message Text="AssemblyInfo files updated to version
              &quot;
              $(VersionMajor).$(VersionMinor).
              $(VersionService).$(VersionBuild)
              &quot;" />
</Target>

Custom Build Tasks

To control the build number versioning, we created a custom MSBuild Task: “IncrementBuildNumber” to retrieve the last build number from the build number file, increment the number and update the build number file with the newly created version number.

We include our custom task in the build type, by adding the following tag to the TFSBuild.proj file:

<UsingTask TaskName=”IncrementBuildNumber”

AssemblyFile=”$(TeamBuildRefPath)\CustomBuildTasks.dll”/>

To manage the increment the build number during the build process, we call the IncrementBuildNumber task in the “BuildNumberOverrideTarget” in the TFSBuild.proj file as shown below:

<PropertyGroup>

<VersionBuild>9999</VersionBuild>

</PropertyGroup>

<Target Name=”BuildNumberOverrideTarget”>

<!– Create a custom build number, matching the assembly version –>

<Message Text=”Loading last build number from file
&quot;$(DropLocation)\buildnumber.dat&quot;” />

<IncrementBuildNumber NumberFile=”$(DropLocation)\buildnumber.dat”>

<Output TaskParameter=”NextNumber” PropertyName=”VersionBuild” />

</IncrementBuildNumber>

<PropertyGroup>

<BuildNumber>$(BuildDefinitionName)_$(VersionBuild)</BuildNumber>

</PropertyGroup>

<Message Text=”Build number set to &quot;$(BuildNumber)&quot;” />

</Target>

Running all Unit Tests

Initially, TFS assumes that you want to use a .vsmdi – metadata – file to maintain a list of all unit tests to run in the build. This requires the developers to manage the updates to this file each time the add a unit test. When we create an integration build, we want to run all the unit tests in the solution, and we don’t want to have to rely on the developers updating the unit test metadata file each time they check in a new unit test. So, we use the Test Container feature of TFS 2008 that allows us specify a pattern for test assemblies. When the pattern specified, we tell the TFS build process to execute all tests within the solution, not just those specified in the unit test metadata. To use the test container, add the following to the TFSBuild.proj build definition file after the TestArguments section:

<TestContainer Include=”$(OutDir)\%2a%2a\%2a.Test.dll” />

Executing Code Coverage Analysis in the Build

To run code coverage analysis in Team system, a test run configuration file is required. The test run configuration file maintains a list of assemblies to instrument for code coverage analysis. Developers maintain the test run configuration file as needed when they add or remove assemblies from the solution.

To include Code Coverage Analysis in the build, we specify the location of the test run configuration file using the <RunConfigFile> tag in the TFSBuild.proj file. Note, this tag must be specified within the <PropertyGroup> tag.

<PropertyGroup>

<RunConfigFile>$(SolutionRoot)\Main\
BuildIntegrationTests.testrunconfig</RunConfigFile>

</PropertyGroup>

ClickOnce Deployment

We use ClickOnce deployment to manage the distribution of new versions of the application in all environments (Dev Test, Functional Test, and Production). Our build and deployment process creates a new ClickOnce deployment packages with each build. Each environment has a unique drop location and install web page, allowing our testers to install a test version of the application and production users to install a production version of the application.

The application deployments may differ both in version number as well as the database connection used. (Test uses the test database connection, and production uses the production database.)

To create the ClickOnce deployment packages, the build process must:

· Set the application and deployment properties to match the version and environment specified by the build type information.

· Create an application manifest file using the mage.exe utility.

The application manifest describes the application, including: the assemblies, the dependencies, the files that make up the application, the required permissions, and the location where updates are available.

· Create a deployment manifest file that describes how we deploy the application, again using the mage.exe utility.

The deployment manifest includes the location of the application manifest, the version of the application that clients should be running, and the update location where the application checks for updated versions – specified in the publish properties.

· Copy the deployment files and application files to the deployment location, i.e. the web server running on the TFS01 server. The deployment manifest specifies the deployment location.

Using MSBuild to generate the ClickOnce deployment

Buck Hodges describes this in detail and provides utilities to make this happen:

http://blogs.msdn.com/buckh/archive/2006/11/04/how-to-run-tests-without-test-metadata-files-and-test-lists-vsmdi-files.aspx

Switching from NAnt to MSBuild, we have had to port our custom tasks from NAnt to MSBuild. Since the design of MSBuild is so similar to NAnt, this is actually pretty easy to do.

In addition to our custom tasks, we use many tasks found in the NAntContrib project. Luckly, there is a similar project for MSBuild so we don’t have to re-implement all the these tasks as well.

The MSBuildContrib files can be found here: http://msbuildtasks.tigris.org/.

As stated on the Tigris page:

In order to use the tasks in this project, you need to import the MSBuild.Community.Tasks.Targets files. If you installed the project with the msi installer, you can use the following.

<Import Project=$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets/>

Editing the MSBuild File for Team Build

TFS Build uses the [Microsoft.TeamFoundation.Build.targets] file to define the set of build tasks and targets needed for the FTS Build process. This file is typically found on the TFS build server in the directory: [C:\Program Files\MSBuild\Microsoft\VisualStudio\TeamBuild].

This file is shared by all builds that run on the build server so use care when editing this file to add new targets or modify existing targets.

Working with the TFSBuild.proj file

Overriding BuildNumberOverrideTarget

The base targets file has an existing target that provides for implementing your own build version number scheme.

</p> <p>&lt;!-- Override this target to execute a task for customizing the build number and/or drop location. Make sure that the task outputs properties with the names 'BuildNumber' and/or 'DropLocation'. --&gt; <br/>&lt;Target Name="BuildNumberOverrideTarget" /&gt;</p> <p>

Replace the oringal target with the following xmal block to call our custom build numer version mechanisim.

</p>
<pre class="xml">
&lt;PropertyGroup&gt;
  &lt;VersionMajor&gt;1&lt;/VersionMajor&gt;
  &lt;VersionMinor&gt;0&lt;/VersionMinor&gt;
  &lt;VersionService&gt;0&lt;/VersionService&gt;
  &lt;VersionBuild&gt;0&lt;/VersionBuild&gt;
&lt;/PropertyGroup&gt;
&lt;Target Name="BuildNumberOverrideTarget"&gt;
  &lt;Message Text="Loading last build number from build version file &amp;quot;$(DropLocation)\buildnumber.dat&amp;quot;" /&gt;
  &lt;IncrementingNumber NumberFile="$(DropLocation)\buildnumber.dat"&gt;
    &lt;Output TaskParameter="NextNumber" PropertyName="VersionBuild" /&gt;
  &lt;/IncrementingNumber&gt;
  &lt;PropertyGroup&gt;
    &lt;BuildNumber&gt;$(BuildDefinitionName)_$(VersionMajor).$(VersionMinor).$(VersionBuild).$(VersionService)&lt;/BuildNumber&gt;
  &lt;/PropertyGroup&gt;
  &lt;Message Text="Build number set to &amp;quot;$(BuildNumber)&amp;quot;" /&gt;
&lt;/Target&gt;
</pre>
 <p>

This is much more involved that it need be.  Why not just have a ‘delete’ menu item and ask ‘are you realy, really sure you want to delete this work item’?

Anyway, here is how you do it:

1.) Get and Install TFS Powertools, if you haven’t already done so.  (Get it Here)

2.) Open a command window and navigate to the directory where tfpt.exe is located. (Usually found [C:\Program Files\Microsoft Team Foundation Server 2008 Power Tools] directory)

3.) Type: tfpt destroywi /server:<serverName> /workitemid:<workItemNumber>

4.) Answer ‘Y’ to the prompt.

When are we Done?

Measuring Progress

Measuring the progress of an agile project is simple – the working, tested, and deployed unit of software shows the exact progress of the system.  Gone are the days of having 80% of your tasks 90% done for most of the project.  There are no partially implemented features, there are no “almost there” project estimates.  What’s done is done, what’s not done is not done. Even if a feature has 99% of the code, unit tests, user interface, and documentation created – if it is not releasable, then it’s not considered done.

Agreeing what Done means for your team

  1. All code for feature(s) is complete, commented, and peer reviewed.
  2. All Code Analysis rules pass, there are no errors or warnings.  This assumes the code builds without errors or warnings.
  3. Unit (Integration) tests for the code modules (business objects) are complete.  Boundary conditions are tested, and all ’standard’ business object tests pass.
  4. 100% of the unit testable code is covered.  Note: In practice we can only get about 97%-98% code coverage when creating integration tests that access a ‘live’ database.
  5. Code meets development standards and naming conventions.  (This is validated via a peer review process.)
  6. Deployed to system test environment and passed system tests
  7. Code / UI / Module passes functional testing (see functional testing check list) and passes user acceptance testing.
  8. Design documents, including domain/design model elements are up to date, any user documentation require is created and checked into the team site on share point.
  9. Task work items are updated, the remaining time is set to zero, and the task is marked as closed.
  10. All time spent on tasks is updated on the time tracking spreadsheet. (On the team share point site.)

Release Planning

Release planning with user stories follows the process outlined in the flow chart.

The goal of any release is working software with some set of functionality that is valuable to the product owner.

1.) Determine what it means to be ‘ready to release’.  The release criteria are typically a combination of prioritized functionality (user stories), quality attributes, and schedule.  This is often feature driven or date driven based on what are the most important attributes for the business (Scope, Schedule, Resources, Cost).

The release criteria are directly related to what is most important to the project. (Financial Goals, Market Conditions, etc.)

Story Point Estimating

2.) Estimating users stories is done using the ‘Story Points’ by using a group estimating technique such as playing ‘planning poker’.  All estimates are done using a relative size.

Planning Poker

The planning poker process:

- Start with a deck of cards that have numbers associated with the estimating sequence we are using. (Fibonacci series: 1,2,3,5,8,13, then larger numbers such as 50, 100)

- All team member get together, including the product owner.

- The mediator (often then scrum master) take story card from the stack and reads the description.

- The Developers ask clarifying questions about the story to the product owner.

- Each person on the team selects a card with an estimate (in story points).

- Everyone turns their estimate card over.

- The high and low estimates are explained.  What is the estimator thinking.  What information do they have to lead them to a high or low estimate.

- The team discusses the details, and if needed, selects a new estimate to present to the team.

- The agreed upon estimate (again, in story points) is written on the story card.

Iteration Length

3.) Selecting a iteration length is easy for scrum – use 30 days.  You can also use 15 days, or 45 days, or whatever length you like.  But once you select the lenght it is best to keep it consistent through the project.

Prioritize Stories

4.) Prioritize User Stories.

Estimate Velocity

5.) Estimate Velocity.

Select Stories for Iteration

6.) Select a set of stories that you think – based on the estimated velocity and story point estimates will fit into a release.  Or use these estimates to come up with an estimated release date that will drive the schedule.

7.) Continue to do this until you have a set of stories that meet the release criteria.

Next, on to sprint planning.

Use brainstorming (divergent thinking) to generate creative ideas.   Divergent thinking is hard.  It is too easy to filter, criticize, and reject creative ideas.  When attempting to analyze problems divergent thinking is needed throughout the process.

After generating the ideas, be open to the ideas from the beginning of the analysis process to the end.  Here are for key points to encourage divergent thinking:

1.) The more ideas the better.

2.) Build one idea upon another.

3.) Wild ideas are o.k.

4.) Don’t evaulate ideas while brainstorming.

Our analysis of a problem, our conclusions, and actions are guided by the statement of the problem.  Also, the statement of a problem depends on our perspective and self-interest driven by our biases and mindset.  Often the first statement of the problem isn’t correct or tends to confuse a statement of observed symptoms with the actual problem.

If a problem is not defined or stated well, our analysis goes off into the weeds and we can waste a lot of time trying to solve the wrong problem or coming up with a solution that doesn’t fit.  If we don’t know what problem we are tying to solve, it becomes difficult to come up with a good solution.

By putting some work up front in the definition and correct statement of the problem, we can start our analysis pointed in the right direction and working on the real problem.  The work is required because it is difficult to overcome our mindset and biases – we have to overcome our natural tendencies that can lead us astray.  (Patterning, focusing, seeking explanations, looking for supporting evidence, etc.  See The Thinkers Toolkit and Pragmatic Thinking for a more detailed discussion of these items and to find the citations for the original psychological studies that illustrate these problems.)

Okay, it’s a good thing to make sure we are solving the right problem, but how do we do this?  Problem restatement.

Goal:

The goal of the exercie is to open our thinking by brainstorming and restating the problem in as many different ways we can.  We want our thinking to diverge and throw out as many different ways of looking at the problem as possible without evaluating each statment – just say it, record it, and move on to the next one, we will evaluate each one when we are done.  We want to change and broden our perspective – looking for the themes of the problem at a high level and identifying the core issues.  We want to gain the broadest perspective we can.

Note:  As we restate the problem,  we may identify that there is more than one problem.  Keep going, getting to the essence of the problem allows us to have time during the analysis and decision making steps.

Technique:

Capture all the problem statments you can during this exerciese and write them down.  We then evualuate the problem statement and select the one that best defines the problem.  We write this down as the agreeded upon problem statement as we move into the analysis and solution definition phase.

Remember the point of the exercise is to broaden our perspective and thinking on the problem not to solve it.

Pitfalls in problem definition:

The Thinkers Toolkit lists a number of ‘pitfalls’ in problem definition:

1.) No focus – defintion is too vague or broad.

If the statement is too broad or generic, it doesn’t really define the problem.

2.) Focus is misdirected – definition is too narrow.

This assumes the root cause of the problem from the start.  The defintion of the problem is colored by the mindset and biases and we don’t know if it the real problem or the first thing that came to mind.

3.) Statement is assumption driven.

When assumptions are made, there problem is often too narrowly defined.  If the assumption is not valid, the problem statment can misdirect the focus of the analysis.

4.) The statement is solution driven.

When the problem is stated such that it assumes a solution, the problem is defined too narrowly.  If the the assumed solution is inappropriate, the problem statement misdirects the analytic focus.

To avoid these issues and drive to a more useful problem statement use the following techniques:  (Again, these are taken from The Thinkers Toolkit)

Techniques to avoid pitfalls of problem statement:

1.) Paraphrase: Restate the problem using different words without losing the original meaning.

2.) 180 degrees: Turn the problem on it head.

3.) Broaden the focus: Restate the problem in a larger context.

Narrow yes or now questions don’t lead to much analysis – broaden the problem statement such that it encourages consideration of alternatives.

4.) Ask “Why?”: Ask why of the initial problem statement.  Then formulate a new problem statement based on the answer.  Then ask “why?” again, and restate the problem based on the answer one again.  Continue asking “why?” until the ‘real’ problem emerges.

Additional Tips and Techniques:

1. Make the problem statement simple.

2. Make the problem statement positive.

3. Make the problem statement using active voice. (actor-action-object)

Pros Cons and Fixes

The Pros, Cons, and Fixes method is one of many structured analysis tools that we use on nearly a daily basis.  We often introduce this technique to clients as we try to make the decision making on projects explicit, structured, and at least somewhat rigorous – rather than the intuitive (aka knee jerk) analysis and decision making techniques that are so commonly used.

The technique is outlined below:

  1. State the problem or the issue as simply and clearly as possible.  Write it down on the white board so everyone is clear exactly what the problem is.  (Problem definition and restatement are also important techniques we will discuss in a future post.)
  2. Identify a set of alternative solutions to the problem.  (A good way to generate this list is by a divergent thinking exercise such as brainstorming or mind mapping.)  Select a set of potential alternatives to consider.
  3. List all the pros of each alternative.
  4. List all of the cons of each alternative.
  5. Review and consolidate the cons.  Merge those statements that mean basically the same thing, eliminate duplications, and develop the final list.
  6. List the fixes that neutralize as many cons as possible.  This is a list of solutions (i.e. fixes) or factual statements that either turn the Con into a Pro or eliminate the Con all together.  This includes statements like: “If we purchase a $10,000 piece of equipment this con will no longer be an issue.”  or “If we add auditing to the system, we don’t have to worry about losing information that is deleted.” [This is a very valuable part of the exercise - don't skip it.]
  7. Compare the pros and the unalterable (fixable) cons for each of the selected options.
  8. Select the alternative where the pros out weigh the cons.

This technique and many others are fully described in The Thinker’s Toolkit.

Older Posts »