Visual Studio 2010 Best Practices
上QQ阅读APP看书,第一时间看更新

Organizing for source code control

Despite having Visual Studio, working with an SCC system doesn't come without some planning and caveats. We will discuss them next.

Organizing directory structures

Generally, when you're working with SCC, the source code in the repository is in a state that builds (for example, a commit could "break the build"). This, of course, assumes that everything the build needs to run is actually in source code control, and referenced in the same structure that it is in source control. One of the biggest issues with users of SCC is that what the user has on their local system builds fine, but if someone else clones or fetches everything from SCC it doesn't build.

The biggest culprit for build errors like this has been that some dependencies, like libraries or frameworks, are missing. To a certain extent, there are dependencies we are not capable of storing in SCC. You can't store whether a development environment works in Windows, Linux, or some other operating system. For example, fetching code from a repository that works on Windows won't compile well at all on a Linux computer. Everything required to build should be included in SCC.

When working with a team, it's important for each computer to abide by the baseline in order to work effectively on the team. Things to consider when deciding what should be in the baseline rather than stored in SCC are as follows:

  • OS type
  • OS version
  • Frameworks (Java, .NET, and so on)
  • Framework versions
  • Developer tools (Visual Studio, SVN, Git, and so on)

SCC systems basically just copy files. Anything that only needs to be copied from one place to another, is a dependency of the source code in some way, or can be modified and tracked over time is a good candidate for placing in SCC rather than the workstation baseline.

Context: When working with teams of developers with multiple computers.

Practice: Define a minimalistic baseline that every computer connecting to SCC must adhere to.

Once you define what the workstation baseline is, it's important to keep things, such as third-party libraries, in SCC so that anyone fetching from the repository is able to build on any workstation that adheres to the baseline. It's preferred to do this in a directory, within the source directory src. lib is a common directory for such dependencies. Having a src\lib relationship helps when building within Visual Studio. It's more common to have lib and src at the same level, especially when the build occurs outside of Visual Studio.

Context: When taking dependencies on third-party libraries or frameworks.

Practice: Keep dependencies within the source code directory of the project.

Generally, you also have other files you may want to keep in SCC that aren't really source code but do change over time and would be aided by tracking revisions and history. It's common to have a directory at the same level as the source code to contain such files. It's common for there to be a doc directory at the same level as src. For example, the directory structure may look like the following screenshot:

Organizing directory structures

Or maybe like the following screenshot:

Organizing directory structures

When creating projects and solutions with Visual Studio you, of course, are not given the option of naming the source directory. The name of the solution is used for the source code directory name. It's fine to accept that and rename the directory afterwards.

Context: When creating a solution in Visual Studio.

Practice: Do not name the solution src to get all of the source code within a src directory.

Some SCC systems actually require a little more work when it comes to directory structure. Systems like Subversion keep branches and tags within the directory structure. Without some preplanning it's easy to create a Subversion repository that is difficult to manage because these directories were not taken into account.

It sounds simpler to create a directory to hold a Visual Studio solution and its projects than to simply create a repository and start checking these files into the root. Unfortunately, when it comes time to tag or branch you'll be asked where you want to store that information. That information must be somewhere within the repository. If you haven't accounted for it and you've put your source in the root of the repository (that is the SLN file) you'll be forced to put the branches and the tags in the same directory.

"Yeah, so?" you're probably thinking. Well, let's say you work on this project for several years and you've created 20 or so branches and tagged about 50 times. This means you have 71 copies of your source code in the root of the repository (if using SVN). Whenever you ask the SCC to check-out or export you're going to get all 71 versions of that code, every time! Believe me when I say, that's only mildly better than sticking bamboo under your finger nails.

Fortunately, the creators of Subversion have defined a set of recommended directories to create when you create a Subversion repository. By creating a branches, tags, and trunk directory in the root, you can avoid these hassles. The source code would initially be placed in trunk, and branches and tags of that source would go in the branches and tags directories respectively. You can now check-out or export from https://servername/svn/ProjectName/ trunk, and avoid having to fetch multiple copies of the source code every time you get the latest version of the source.

For example, in a popular Subversion server distribution VisualSVN, the project ProjectName may appear like the following screenshot in the repository hierarchy:

Organizing directory structures

Solution structure

For the most part, the out of the box solution structure (or out of the wizard, in this case) is perfectly acceptable.

When working with SCC systems that integrate into Visual Studio, what is retrieved and updated in SCC is largely defined by what is in a solution. For example, by selecting File | Source Control | Open from Source Control, Visual Studio actually only retrieves the files that the solution knows about. This has been a problem with teams and build errors when getting the "new guy" up to speed.

Of course, this generally means that we need to also have all of the files that are required to build somewhere in the solution (despite Visual Studio being perfectly happy to find them only in the local filesystem). In much the same way as we did with our directory structure, we can accommodate that by having a similar folder structure in Visual Studio. You can use the same naming as we did with directories to make it easier to remember and correlate the files to directories that match the solution folders (since solution folders are not stored as a filesystem directory). You can add a solution folder to a solution by right-clicking the solution in Solution Explorer and selecting Add | New Solution Folder. For example, if we added a lib solution folder, and added a third-party library to it in our solution, our Solution Explorer may look the following screenshot:

Solution structure

Context: When working with a SCC that is integrated into Visual Studio.

Practice: Ensure that all files that are in SCC are also in the Visual Studio solution or project.

Continuous integration

As we'll detail shortly, what you have locally on your computer, your ability to build, and what is in SCC may not be in sync. It is quite feasible that you may check something on your computer (forgetting a single file, for example) that will create something in SCC that will not build (should anyone else fetch that particular point in the repository's history). To minimize this, it's important to be able to automate the build early and make use of continuous integration tools. Continuous integration tools can be configured to point at your repository, check for new changesets, fetch all source code, and perform a build automatically. This allows detection of broken builds almost as quickly as they occur.

Context: When working on a team with SCC.

Practice: Ensure build failures are detected early by using continuous integration tools.

Continuous integration tools are much more powerful than simply fetching from SCC and performing an automated build. The process can include labeling/tagging, reporting, deployment, emailing success/failure, and so on.