A while ago I had a discussion with a colleague on what the structure of our code repository ought to be. At the time, I couldn’t find the words to explain why I prefer one strategy over another, so I’ve given it some more thought.
In this case we’re talking about several Visual Studio Solutions (.sln) and their C++ projects (.vcxproj). If I were to choose a directory structure, my considerations would be as follows.
Ownership and containment
A solution contains projects that it may or may not own. I expect a solution like MyApplication.sln to contain a project MyApplication.vcsproj. The solution owns this project, the main project. The project could have dependencies on other projects of course. These dependencies are either specific for this project, or generic and reusable. The solution contains all projects that the projects it owns directly or indirectly depend on. I.e. My Applicaiton depends on MyApplicationLib, which in turn depends on GenericLib. The solutoin contains all three projects. It may seem obvious, but it’s quite common to find a project that depends on a library that is not included in the solution, causing a linker error unless you built this additional dependency manually beforehand. Not pretty.
The solution also owns projects that are specific to MyApplication. In this case, the solution owns MyApplication, MyApplicationLib and MyApplicationUnitTests. Generic, reusable projects that other projects may use as well, aren’t owned by this solution. So in this case the solution does not own GenericLib.
In my view, a project has exactly 1 owner. This owners also owns the test projects. When you make changes to a project, this has to happen from the solution that owns that project, since this contains all information (test projects, sample applications) to make this change carefully.
Conversely, a solution never contains the test projects of projects it does not own. When project GenericLib has its own solution, that also holds GenericLibUnitTests, it’s not necessary to include GenericLibUnitTests in the MyApplication solution as well.
Including the test projects for GenericLib in MyApplcation as well is an unnecessary redundancy that will only increase the build and test duration of MyApplication.
A project contains several files. The project contains those and only those files it owns, i.e. all files the project needs to build and use it.
In my view, like a project, a file has exactly 1 owner. It is therefore only present in 1 project. When a file is owned by multiple projects, this violates the boundaries between projects. In such a case there are two options: Merge both projects, because they apparently do not stand alone, or split off the offending files into a new project of their own, that both original projects now depend on.
- A project is owned by exactly 1 solution.
- When a solution owns a project, this solution also owns the test projects for this project.
- A solution contains all and only those test projects it owns.
- A file is owned by exactly 1 project.
- A solution contains all projects it owns.
- A solution contains all projects that the projects it owns depend on.
- A solution contains no other projects than those mentioned above.
- A project contains all and only those files it owns.
The structure on disk should, in my opinion, express aforementioned relationships:
- The source code files are present in a project as they are on disk. That way, looking for a file on disk is simple. This is one of the reasons I’m not a big fan of ‘filters’ in Visual Studio and prefer to enable ‘Show All Files’.
- The directory that contains a project and the directories below, contains all and only those source code files the project owns.
- The directory that contains a solution, contains all and only those projects the solution owns.
Besides, I prefer to place each project in a folder with the same name as the project, and each solution in a folder with the same name as well.
Now for a last question: Where to put the documentation? At the moment, I have a slight preference for placing documentation in a separate tree, next to the source tree, following the same directory structure as the solutions and projects.
Both developers as well as those for whom source code isn’t directly relevant (users, management, consultants, developers developing a product that depends on your project), should be enabled to find documentation quickly and easily. For both manually written as well as autogenerated documentation I believe the following holds: Someone who has no reason to look at code, does not want to go through a code base. Instead, (s)he will want to search in terms of high-level concepts (information on a module, feature or application). As software engineer, however, I prefer having documentation in the same structure as my code. This illustrates that not the code dictates the structure, but the people: Everyone who should be able to find something in the code base or documentation should be supported as much as possible by the directory structure.
Documentation could be placed in a wiki or on an intranet, of course. There, as well, I would be inclined to stick to the same directory structure as in the code base.
This is how I would do it and why. Most of these choices are implicitly made for you when you create a new solution in Visual Studio. However, when you’re reorganizing existing code into projects and solutions, I’d prefer working towards and consistently applying such a structure, even though this is not always easy to do. This way every developer, especially someone who is new on your team, will know which structure to maintain in solutions and projects and ownership and containment are expressed as part of your solution and directory structure.