Professional Documents
Culture Documents
Basics
Source code management
Coding guidelines
Product planning and issue tracking
Tips and tricks
Basics
Copyright header and license notice
All source code files (mostly src/**/*.cs and test/**/*.cs) require this exact header (please do not
make any changes to it):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
External dependencies
This refers to dependencies on projects (i.e. NuGet packages) outside of the aspnet repo, and
especially outside of Microsoft. Because these repos are a core part of all ASP.NET apps, it is
important that we be careful and manage our dependencies properly. Adding an external
dependency requires approval from @eilon.
The current approved dependencies are:
CoreCLR
Roslyn
Json.NET
Re-linq
Ix-Async
Dependencies that are used only in test projects and build tools are not nearly as rigid, but still
require approval from @eilon.
Customer samples
Customer-facing sample projects will have their own repos. For example, the "MVC Music
Store" sample has its own repo with both the original MVC Music Store, and a copy of it that
has been modified to use the new features we are building. For repo-specific samples please
see the Repo-specific Samples section.
Repos
To create a new repo in the https://github.com/aspnet/ org, contact @eilon.
Universe repo
The https://github.com/aspnet/Universe repo is the "one repo to rule them all." It is a special
repo that has a build that will pull in and build projects from all the other repos. This is great to
have in your enlistment if you're making cross-repo changes, such as renaming a core API.
Branch strategy
In general:
has the code for the latest release on NuGet.org (e.g. alpha, beta, RC, RTM)
dev has the code that is being worked on but not yet released. This is the branch into
which devs normally submit pull requests and merge changes into.
release has the code that is being staged and stabilized for an upcoming release
master
Shortly before a release, the release branches are created from dev, and stabilization work
happens there. Post-release work continus in the dev branch. Once the release takes place, the
code is pushed from release to master.
Note that after running the build command the system will generate the following files:
/build/*.shade
CoreCLR:
#ifdef DNXCORE50
EntityFramework, which is not part of ASP.NET, will continue to use its existing naming
pattern.
Non-platform-specific projects, such as dependency injection, use the pattern
Microsoft.Extensions.<area>.<subarea>.
Build system
We are using a new system called KoreBuild, which is built using the sake build tools. The sake
project is available here: https://github.com/sakeproject/sake
Unit tests
We use xUnit.net for all unit testing.
Repo-specific Samples
Some repos will have their own sample projects that are used for testing purposes and
experimentation. Please ensure that these go in a samples/ sub-folder in the repo.
To have a sample project reference a project in src you'll need a global.json file in the root of
your repo. By default project-to-project references must be sibling folders. Using a global.json
file allows a solution to specify non-standard locations to locate references. The format of
global.json is as follows:
{
"projects": ["src"]
}
Coding guidelines
The content of the code that we write.
Cross-platform coding
Our frameworks should work on CoreCLR, which supports multiple operating systems. Don't
assume we only run (and develop) on Windows. Code should be sensitive to the differences
between OS's. Here are some specifics to consider.
Line breaks
Windows uses \r\n, OS X and Linux uses \n. When it is important, use Environment.NewLine instead
of hard-coding the line break.
Note: this may not always be possible or necessary.
Be aware that these line-endings may cause problems in code when using @"" text blocks with
line breaks.
Environment Variables
OS's use different variable names to represent similar settings. Code should consider these
differences.
For example, when looking for the user's home directory, on Windows the variable is
USERPROFILE but on most Linux systems it is HOME.
var homeDir = Environment.GetEnvironmentVariable("USERPROFILE")
?? Environment.GetEnvironmentVariable("HOME");
If two runtime assemblies need to share common helpers then we will use a "shared source"
solution with build-time only packages. Check out the
https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNet.Mvc.Common project and how
it is referenced from the project.json files of sibling projects.
If two runtime assemblies need to call each other's APIs, the APIs must be public. If we need it,
it is likely that our customers need it.
Null checking is required for parameters that cannot be null (big surprise!). To add null
checking to your code, declare this attribute in your assembly in any namespace (use the
JetBrains namespace to have ReSharper work):
using System;
namespace JetBrains.Annotations
{
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
internal sealed class NotNullAttribute : Attribute
{
}
}
The null checking code will be code-gen'ed at compile time into the method body.
Argument null checking in interface member definitions and abstract/virtual methods
If an interface member or abstract/virtual member contractually disallows nulls in its
parameters, annotate them with [NotNull]. The implementing method does not need the
annotations - the code-gen will automatically emit the null checking code on the
implementing method.
Argument null checking in chained constructors/methods
Null checks should be used on any public entry point where null is not allowed. This ensures
the contract of not-nullable is seen by all callers.
public class Banana
{
// Even though all this ctor does is chain the next ctor, it still must have NotNull annotations
public Banana([NotNull] string name, [NotNull] string variety)
: this(name, variety, string.Empty)
{
}
public Banana([NotNull] string name, [NotNull] string variety, [NotNull] string color)
{
...
}
}
When writing extension methods for an interface the sponsor type name must not start with
an I.
Doc comments
The person writing the code will write the doc comments. Public APIs only. No need for doc
comments on non-public types.
Note: Public means callable by a customer, so it includes protected APIs. However, some public
APIs might still be "for internal use only" but need to be public for technical reasons. We will
still have doc comments for these APIs but they will be documented as appropriate.
Assertions
Use Debug.Assert() to assert a condition in the code. Do not use Code Contracts (e.g.
Contract.Assert).
Please note that assertions are only for our own internal debugging purposes. They do not end
up in the released code, so to alert a developer of a condition use an exception.
PublicApiArgumentsShouldHaveNotNullAnnotation
Public_api_arguments_should_have_not_null_annotation
The crucial thing here is that the Act stage is exactly one statement. That one statement is
nothing more than a call to the one method that you are trying to test. Keeping that one
statement as simple as possible is also very important. For example, this is not ideal:
int result = myObj.CallSomeMethod(GetComplexParam1(), GetComplexParam2(), GetComplexParam3());
This style is not recommended because way too many things can go wrong in this one
statement. All the GetComplexParamN() calls can throw for a variety of reasons unrelated to the
test itself. It is thus unclear to someone running into a problem why the failure occurred.
The ideal pattern is to move the complex parameter building into the Arrange section:
// Arrange
P1 p1 = GetComplexParam1();
P2 p2 = GetComplexParam2();
P3 p3 = GetComplexParam3();
// Act
int result = myObj.CallSomeMethod(p1, p2, p3);
// Assert
Assert.AreEqual(1234, result);
Now the only reason the line with CallSomeMethod() can fail is if the method itself blew up. This is
especially important when you're using helpers such as ExceptionHelper, where the delegate you
pass into it must fail for exactly one reason.
To make writing unit tests easier it is recommended to compare the error message to the RESX
resource. However, comparing against a string literal is also permitted.
var ex = Assert.Throws<InvalidOperationException>(
() => fruitBasket.GetBananaById(1234));
Assert.Equal(
Strings.FormatInvalidBananaID(1234),
ex.Message);
Parallel tests
By default all unit test assemblies should run in parallel mode, which is the default. Unit tests
shouldn't depend on any shared state, and so should generally be runnable in parallel. If the
tests fail in parallel, the first thing to do is to figure out why; do not just disable parallel tests!
For functional tests it is reasonable to disable parallel tests.
Issue tracking
Bug management takes place in GitHub. Each repo has its own issue tracker. Bugs cannot be
moved between repos so make sure you open a bug in the right repo. If a bug is opened in the
wrong repo someone will have to manually copy it to the correct repo.
We use the HuBoard pattern for issue tags. Look at the numerical tags that SignalR uses for an
idea: https://github.com/SignalR/SignalR/issues
Then you can run projects more easily in VS, debug more easily, test more quickly, and also
write code and refactor more quickly.