Below are my personal guidelines for when I write C# code, or code in general really. It started as collaboration with a friend many moons ago, just so we'd have something to lean on when discussing pull requests to a project we were working on together. The guidelines were originally based on the .NET Runtime coding guidelines, anno 2018 or so, but has since been expanded to this monstrosity of definition and excruciating detail. It is formulated as if it were written by a group of people, since it is intended to be used in a project as either the basis or starting point for an official coding guidelines document. It could even be used as a provocation just to start a conversation about code and its quialities.

If you read this, and actually make it to the end, I hope to leave you either having confronted some of your own concepts of what makes code "good" or reinforced what you're already doing. Adding some context and factors that shores up the "why" in what do "this way or that".

Or failing that at least gave you fuel for your own righteous crusade and a break from our burning world at large.

Back to techtree.se

PS. If you feel like using this, please include some attribution or a link to the original, unadulterated, version. Not for my sake. But so that others, especially if included/published in a public setting, can see where this wellspring of complete and absolute correctness originated. It is my intention to keep all versions going forward available on this site - somehow. We'll see. - - - Cheers!

C# coding guidelines - turned manifesto of sorts

version 1.0.0 (2025-10-09)

This document outlines a set of guidelines regarding coding and coding style for C#. The guidelines are written in first person plural (we) to signal that this document is consensus driven and not dogma passed from up on high. It is a living document that is supposed to change with the times.

The collective preferences expressed in this document are defined and maintained by the suggestions of individual developers. Suggestions are to be discussed in the group, and when reaching consensus the suggestions are to be realized as additions to, alterations in, or removals from this document.

The guiding principles for this document are sustainability and stability. In this context sustainability refers to our aspiration to make it easier to write, and by extension read, code the way we prefer, even if the developer is less experienced or is coming from a different way of working. Stability means that our code is stable as written between machines and development environments. The settings of a given editor or the like should not impact how the code is presented. Some things may be enforced with .editorconfig files, others rely on the developers following the guidelines of this document. The end goal of these principles is to maintain readability and as far as possible keep our code self documenting.

Keywords

There are certain words that have different meanings when used in everyday speech depending on context. Context, intonation, and body language inform the intended meaning of the word as it's used. This is all but lost in text. To mitigate misunderstandings this chapter contains a list inspired in form by RFC2119. When the key words in the list are used as written, in all caps, they mean only what they are listed as meaning below. When not written in all caps, the key words are intended to have their respective colloquial meaning in the given context.

Keyword Meaning
MUST
REQUIRED
SHALL
These words mean that the definition is an absolute requirement.
MUST NOT
SHALL NOT
These phrases mean that the definition is an absolute prohibition.
SHOULD
RECOMMENDED
These words mean that the definition is the preferred one. There may exist valid reasons in certain circumstances to ignore the definition. Any deviations must be motivated by increased readability, maintainability, or similar reasoning.
SHOULD NOT
NOT RECOMMENDED
These phrases mean that the definition is undesirable. There may exist valid reasons in certain circumstances to ignore the definition. Any deviations must be motivated by increased readability, maintainability, or similar reasoning.
MAY
OPTIONAL
These words mean that a definition is truly optional.
PREFER This word means that the definition is the preferred one. There may exist valid reasons in certain circumstances to ignore the definition. Any deviations must be motivated by increased readability, maintainability, or similar reasoning. This word is considered a stronger proposition than SHOULD/RECOMMENDED and the full implications must be understood and carefully weighed before choosing a different course.
AVOID This word means that the definition is undesirable. There may exist valid reasons in certain circumstances to ignore the definition. Any deviations must be motivated by increased readability, maintainability, or similar reasoning. This word is considered a stronger proposition than SHOULD NOT/NOT RECOMMENDED and the full implications must be understood and carefully weighed before choosing a different course.

Project organization

General coding style

Naming

Class construction

Database interaction

Examples

Bracing

// Ok
if (contidion)
    DoThis();
else
    DoThat();

if (condition)
{
    DoThis();
}
else
{
    DoThat();
    ThenThis();
}

// Not ok
if (condition)
{
    DoThis();
    ThenThis();
}
else
    DoThat();

Implicit else

// Ok
if (condition)
    throw new Exception();

if (condition)
{
    return valueOne;
}
else if (condition)
{
    return valueTwo;
}
else
{
    var valueThree = Operation();
    return valueThree;
}

// Not ok
if (condition)
    return valueOne;
if (condition)
    return valueTwo;

var valueThree = Operation();
return valueThree;

Mutability

// Prefer
public class Class
{
    public int Integer { get; init; }
}

public class Class
{
    public int Integer { get; private set; }
    public string Text { get; }

    public Class(string text, /* ... */)
    {
        Text = text;
        DoThis(/* ... */);
    }

    public void DoThis(/* ... */)
    {
        /* ... */
        Integer = value;
    }
}

// Over
public class Class
{
    public int Integer { get; set; }
    public string Text { get; set; }
}

Value objects

// Prefer
public class EMail : ValueObject<EMail>
{
    public string Recipient { get; }
    public string Domain { get; }
    public string Address => Recipient + "@" + Domain;

    public EMail(string email)
    {
        /* ... */
    }
}

public class EMailRecipient
{
    public EMail Address { get; }
}

// Over
public class EMailRecipient
{
    public string Address { get; }
}

Boolean expressions

// Prefer
var b = DoThis() && DoThat();

// Over
var b = DoThis() && DoThat() ? true : false;

Method arguments

// Prefer
void DoThis(int arg0, int arg1, string arg2);
void DoThat(
    int arg0,
    int arg1,
    string arg2,
    Collection<string> arg3,
    Collection<int> arg4);

// Over
void DoThis(int arg0, int arg1, string arg2);
void DoThat(int arg0, int arg1, string arg2,
    Collection<string> arg3, Collection<int> arg4);
void DoOther(int arg0, int arg1, string arg2, Collection<string> arg3, Collection<int> arg4);

Call chain wrapping

// Prefer
var foo =
    collection
    .MethodA(/* ... */)
    .MethodB(/* ... */);

var bar = serviceInstance
    .MethodA(/* ... */)
    .Property;

return
    instanceObject
    .Property
    .MethodA(/* ... */);

Indexing

// Prefer
var e = array[^1];
var s = text[2..^4];

// Over
var e = array[array.Length - 1];
var s = text.Substring(2, text.Length - 4);

Pattern matching

// Prefer
return n is 1 or 2;

if (x is int i) /* ... */

if (x is not int i) /* ... */

if (y as string s) /* ... */

// Over
return n == 1 || n == 2;

if (x is int)
{
    var i = (int)x;
    /* ... */
}

if (!(x is int i)) /* ... */

var s = y as string;
if (s is not null) /* ... */

Variable declaration

// Prefer
if (int.TryParse(value, out var i)) /* ... */

var (name, age) = PersonAsTuple();
Console.WriteLine($"{name} is {age} years old.");

(int x, int y) = StartingPointAsTuple();
Console.WriteLine($"Starting point is {x} by {y}.");

// Over
int i;
if (int.TryParse(value, out i)) /* ... */

var person = PersonAsTuple();
Console.WriteLine($"{person.name} is {person.age} years old.");

(int x, int y) point = StartingPointAsTuple();
Console.WriteLine($"Starting point is {point.x} by {point.y}.");

null checking

// Prefer
func?.Invoke(args);

var z = x ?? y;

var s = o?.ToString();

if (x is null)
    return;

// NB: Prior to C# 9.0/.NET 6 the `not` key word is not available. Use
// `!(y is null)` instead.
if (y is not null)
    DoThis();

// Over
if (func != null)
    func(args);

var z = x != null ? x : y;
var z = x == null ? y : x;

var s = o != null ? o.ToString() : null;
var s = o == null ? null : o.ToString();

if (x == null)
    return;

if (object.ReferenceEquals(x, null))
    return;

if ((object)y != null)
    DoThis();

Calculating hash codes

// Prefer
public override int GetHashCode()
{
    return System.HashCode.Combine(
        PropertyA,
        PropertyB,
        PropertyC,
        PropertyD);
}

// Over
public override int GetHashCode()
{
    var hashCode = 336910899;
    hashCode = hashCode * -1521134295 + PropertyA.GetHashCode();
    hashCode = hashCode * -1521134295 + PropertyB.GetHashCode();
    hashCode = hashCode * -1521134295 + PropertyC.GetHashCode();
    hashCode = hashCode * -1521134295 + PropertyD.GetHashCode();
    return hashCode;
}

EF queries

// Prefer
var foo = context
    .Entity
    .Where(e => e.Property == value)
    .Select(e => e.OtherProperty);

// Over
var foo =
    from e in context.Entities
    where e.Property == value
    select e.OtherProperty;

© Tech Tree AB 2026