A Sensible .NET Project Directory Structure

A few years ago, I was doing some research on .NET project layouts. I was trying to figure out where people were putting their files that were not source control in their source control hierarchy. I looked at several .NET open source libraries and didn’t see too much of a trend other than having .sln files in the root and projects in a src directory. I was hoping to see a consistent structure for where to put build scripts, build tools, docs, and things of that nature since a lot of projects have these as well.

I have spent most of my career working on line of business applications for large companies. In my experience there isn’t really a standard for directory structures. Everyone does it differently. It often times seems as if it wasn’t even a consideration. I usually just see the root of a repository as a dumping ground for anything and everything.

A few years ago I discovered a member of the Asp.NET team at Microsoft created a Gist on GitHub that addressed some of these concerns, and I have adopted a similar version to this for my personal projects. You can find David Fowler’s original Gist here.

tl;dr skip to the final directory structure

David’s Gist starts with his directory structure layout.

$/
  artifacts/
  build/
  docs/
  lib/
  packages/
  samples/
  src/
  tests/
  .editorconfig
  .gitignore
  .gitattributes
  build.cmd
  build.sh
  LICENSE
  NuGet.Config
  README.md
  {solution}.sln

He goes on to document the purpose of each directory as follows.

  • src – Main projects (the product code)
  • tests – Test projects
  • docs – Documentation stuff, markdown files, help files etc.
  • samples (optional) – Sample projects
  • lib – Things that can NEVER exist in a nuget package
  • artifacts – Build outputs go here. Doing a build.cmd/build.sh generates artifacts here (nupkgs, dlls, pdbs, etc.)
  • packages – NuGet packages
  • build – Build customizations (custom msbuild files/psake/fake/albacore/etc) scripts
  • build.cmd – Bootstrap the build for windows
  • build.sh – Bootstrap the build for *nix
  • global.json – ASP.NET vNext only

There are several comments on this Gist that I’ll leave for you to review.

Changes to David’s Gist

Since the time I started working with this structure I have made a few changes to the names of directories and or their use cases.

From src to source

I use almost this exact structure in my projects, however I make a few modifications. The first change I make is to rename "src" to its full name "source". In the current year we tend to use longer more correct names for things. The term src in my opinion comes from tradition, one which I choose not to carry forward.

lib is Dead

I personally don’t ever use "lib" folders, if I were to it would be called "libraries", and would be in a very rare case. In the age of package managers there should be no reason to have any lib directories. If you work on a project that uses the bin directory as the only place to reference binary dependencies, then please create a lib or libraries folder and move those files there. You need to be able to clean your bin folder and have a successful compilation.

global.json is no more

The global.json file was at one point used for .NET core projects. It is no longer in use so don’t include it in your project set up.

The build directory

I use the build directory as the temporary output of a binaries, instead of a place to store build scripts, tools and dependencies. Most tools will have their own directories at the root level, and they will be pulled in via nuget or paket. The build directory is used in place of default bin directory that visual studio likes to place compiled binaries.

About build.cmd

I typically use build.bat instead of build.cmd. The ".bat" extension comes from DOS as the ".cmd" extension was introduced in Windows NT and tells the program to be executed by "command.exe" There is almost no difference. You could alternatively use a build.ps1 if you need/want powershell to run your builds.

Changing the Nuget.Config location

If you are going to be using nuget as a part of your project. Then I would suggest creating a ".nuget" directory in the root of your directory structure. In here you would likely want the nuget.exe commandline tool as well. Having your nuget.config in the same directory as the nuget.config file will force the nuget.exe to use that configuration file.

Add Build Server/Framework Scripts

I would suggest putting build scripts, not tools in the root directory. If you are using any of the MAKE like tools those scripts can live in the root. Put any build server specific files in the root as well. Also any files associated with docker should be in the root. You might think this adds clutter, but often times these files need access to files in sub-directories and it gets confusing having to use "../" to backup n number of directories to get back to the root to references a sub-directory.

My Updated Gist

$/
  .vscode/
  .fake/
  .nuget/
    nuget.exe
    nuget.config
  .paket/
    paket.bootstrapper.exe
    paket.targets
  artifacts/
  build/
  docs/
  lib/
  packages/
  samples/
  source/
  tests/
  .editorconfig
  .gitignore
  .gitattributes
  build.cmd | build.bat
  build.fsx | build.csx | build.ps1
  build.sh
  LICENSE
  packet.dependencies
  pakcet.lock
  appveyor.yml
  travis.yml
  README.md
  {solution}.sln
  • source – Main projects (the product code)
  • tests – Test projects
  • docs – Documentation stuff, markdown files, help files etc.
  • samples (optional) – Sample projects
  • libraries – Things that can NEVER exist in a nuget package
  • artifacts – deployable build artificats (npkg, exe, dlls, pdbs) does not include intermediate build output
  • packages – NuGet packages
  • build – Build output (not build scripts)
  • build.cmd – Bootstrap the build for windows
  • build.sh – Bootstrap the build for *nix

.gitignore

[Oo]bj/
[Bb]in/
.nuget/
.fake/
_ReSharper.*
packages/
artifacts/
*.user
*.suo
*.userprefs
*DS_Store
*.sln.ide

Why does any of this matter?

There are several benefits from having a consistent directory structure for your project. The biggest benefit is that you don’t have to think about how to organize your directory structure when you start a new project. It also makes it easy for anyone to know where to look for what files. Often times I look at source code on the [GitHub}(https://github.com) website. If the projects are nicely organized it doesn’t take long to find the code that I’m looking for. I typically can look for the "src" or "source" directory and have a good idea of what I’m going to find. Also if there is a "tests" directory in the root then I can easily find the tests. Imagine if a large majority of projects followed similar conventions. If there was a build.cmd, build.bat, and/or build.sh file in every solution then it would be pretty simple to know how to build every application. We use conventions all the time in software to make our lives easier. If we all try to adhere to at least some standards we can all play nicer together.

Spread The Word
Adam.Wright
 

Adam Wright is a technologist specializing in software development using Microsoft technologies.

Click Here to Leave a Comment Below 0 comments