Skip to content

Coding Conventions

Jeff Lill edited this page Apr 23, 2019 · 4 revisions

This topic describes and discusses the Neon coding standards.

We're trying to accomplish these things:

  • Readability: Try to think about the poor developer three years on (might even be you) who gets woken up in the middle of the night with a problem in production and he/she needs read and understand a bunch of code to fix the problem. This also applies to new developers being assigned to old code.

  • Teamwork: We want our code to look like it was developed by a team, not a random collection of developers. Armies and sport teams have uniforms, we have our source code.

  • Craftsmanship: Being developers, we write code; being craftsman, we write clean, easy to understand, and well documented code.

Microsoft C# Coding Conventions

The Neon projects will use a modified form of the standard Microsoft C# coding conventions detailed here:

    Microsoft C# Coding Conventions

Here are the highlights:

  • Use the default Visual Studio code editor settings (smart indenting, 4 character space indents (not TABs)

  • Limits lines to one declaration or statement per line (exceptions noted below)

  • Statements that extend across multiple lines should indent lines after the first.

  • Use parentheses in expressions where evaluation precedence is complicated.

  • Comments should generally appear on lines by themselves (I do occasionally put these after variable declarations or state for compactness).

  • Use string interpolation instead of string.Format():

var name = $"{last}, {first}";

// Rather than:

var name = string.Format("{0}, {1}", last, first);
  • Favor declaring variables using var rather than specifying a type when immediately assigning a value. This generally makes code easier to read by reducing bloat. This is specially important for generic types which can easy become very long declarations.

Additional Neon Conventions

This section discusses additional coding standards for the Neon development team.

Source Files

In general, each source file should be dedicated to specifying or implementing a single type. It's easy to be tempted to group together small and semi-related type definitions into a single file and I've done this, but in the end I generally end up splitting these up into separate files so they'll be easier to find in the Visual Studio solution explorer. Separate files also makes it easier to interpret at a glance what changed in the source control history.

All C# source files should begin with the following header:

//-----------------------------------------------------------------------------
// FILE:        MyFile.cs
// CONTRIBUTOR: Joe Bloe, Jane Doe
// COPYRIGHT:   Copyright (c) 2016-2019 by neonFORGE, LLC.  All rights reserved.

Do something similar for other files, e.g. scripts:

#------------------------------------------------------------------------------
# FILE:        MyFile.cs
# CONTRIBUTOR: Joe Bloe, Jane Doe
# COPYRIGHT:   Copyright (c) 2016-2019 by neonFORGE, LLC.  All rights reserved.
  • FILE: specifies the file name (I know, its somewhat duplicative but it's a somewhat common convention and I've been doing this since forever and like the the look).

  • CONTRIBUTOR: This is intended to describe who created and put significant work into the file so that future developers can know who to talk to about issues and changes. This should be full names separated by commas. (In theory, one could get this from source control history, but that can be easier said than done when there thousands of commits and it's not uncommon to relocate source over long timescales and and lose the history).

  • COPYRIGHT: This is VERY important. This is where we assert that these files are owned by neonFORGE. I generally put this on most every file, even scripts.

  • Historical Note: The line with dashes ends at column 80 in honor of the old 80x24 character terminals we old timers had to develop on until the mid-90s. Before then we couldn't actually develop Windows apps on Windows, we had to use terminals on IBM OS/2.

Naming Conventions

In general, type, method, property and variable names should use descriptive names rather than very short or cute abbreviations (with some exceptions noted further below). Names will generally use one of two naming conventions: PascalCase or CamelCase. Both of these conventions combine multiple words into a single identifier using capitalization (not underscores). PascalCase identifiers start with an upper case letter whereas CamelCase identifiers use lower case. Here's how these conventions apply to different language elements:

  • types: All types should use PascalCase naming conventions. Try to choose a name that actually hints at what the type does and is easy to use in a sentence. Type names will generally be nouns.

  • methods: All type methods must use PascalCase. This applies to public, private, and internal methods.

  • variables: Local method variables should use camel case and be relatively descriptive. Iteration variables like i, j, and k in for statements are OK and I often use item` when using a for loop to enumerate a collection. Abbreviations are also fine for lambda parameter names for simple LINQ query where clauses but it's often better to use more description names for complex queries.

  • constants: public and internal constants must use PascalCase. private constants may use PascalCase or CamelCase.

  • fields: These are variables tied to a type. In general, most fields will be private in which case they must use CamelCase. In very rare situations where a field is defined as public or internal, you must use PascalCase.

NOTE: We're not going to prefix member fields with underscores (_). This is a very old convention from C++ days and is still somewhat common but I just don't like it. Use this. instead:

// DO THIS:

public MyType
{
    private int value;

    public MyType(int value)
    {
        this.value = value;
    }
}

// NOT THIS:

public MyType
{
    private int _value;

    public MyType(int value)
    {
        _value = value;
    }
}
  • Properties: should always use PascalCase (even for private properties).

Other Conventions

  • if, else, for, while and using statements should always use braces { } around sub-statements, even if there's just one statement. These are much easier to see than indentation and is a very common convention (and I believe modern languages such as GO enforce this).
// DO THIS:

if (thisIsTrue)
{
    DoThis();
}
else
{
    DoThat();
}

while (a < b)
{
   Console.WriteLine(a++);
}

foreach (var item in items)
{
    Console.WriteLine(item);
}

using (var connection = new Connection())
{
    using (var writer = new StreamWriter("C:\\MyFile.txt"))
    {
        writer.WriteLine(connection.Status);
    }
}

// NOT THIS:

if (thisIsTrue)
    DoThis();
else
    DoThat();

while (a < b)
   Console.WriteLine(a++);

foreach (var item in items)
{
    Console.WriteLine(item);
}

using (var connection = new Connection())
using (var writer = new StreamWriter("C:\\MyFile.txt"))
    writer.WriteLine(connection.Status);
  • Assertions and Argument checking: The System.Diagnostics.Contracts namespace defines the static Covenant class (which is actually implemented in Neon.Common (see this. This class has methods for asserting the correctness of parameter values and program state within your code. You should use these when you need to verify that method arguments are valid, especially for libraries with general use methods. Here are some examples:
using System.Diagnostics.Contracts;

public class MyClass
{
    public string MakeUppercase(string value)
    {
        Covenant.Requres<ArgumentNullException>(value != null);

        return value.ToUpper();
    }

    public int Divide(double numerator, double denominator)
    {
        Covenant.Requres<ArgumentException>(deniminator != 0.0);

        return numerator / denominator;
    }
}

* Use signed integers rather than unsigned unless you **really** need an unsigned integer.  Signed integers are generally easier to work with because most standard library methods use unsigned values, so using signed integers may save you from a lot of casting.

* All type, method, constant, property, and field declarations should explicitly specify one of the access modifiers: `public`, `private`, or `internal`.  Although `private` is the default we want to explicitly specify this for readability.

* **struct:** Be really sure that you actually need to use a `struct` when defining a type rather than `class`.  `struct` can be more efficient in some situations but they often introduce unexpected behaviors and more often than not, I end up reverting back to `class` the few times I go down this road.

* **using statements:** These should be organized at the top of your files into groups of related imports:

  * **System.*** namespaces should be specified first (in sorted order).
  * **Microsoft.*** namespace should be specified next (if required).
  * Followed by any remaining namespace imports.  I generally group multiple related using statements together followed by a blank line.  When there's multiple single unrelated using statements I mostly group them together sorted, but not always.  You may need to manually fix these because Visual Studio code helpers often insert using statements in somewhat unpredictable orders.
  * We'll **allow unused using statements**.  Some teams and linters don't allow this, but sometimes removing these ends up getting in the way of writing new code.

// DO THIS:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

using Microsoft.AspNetCore; using Microsoft.AspNetCore.Mvc;

using Neon.Common; using Neon.Service;

// NOT THIS:

using System; using System.Collections.Generic; using System.Linq; using Neon.Common; using Microsoft.AspNetCore; using System.Text; using Neon.Service; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc;


* **using statements #2:** Use `using` statements to avoid prefixing identifiers with namespaces in your code (there are rare situation when you need to using namespace prefixes to avoid name conflicts).

// DO THIS:

using System; using System.Diagnostics;

public MyClass { public void DoSomething() { Debug.WriteLine("DID IT!"); } }

// NOT THIS:

public MyClass { public void DoSomething() { System.Diagnostics.Debug.WriteLine("DID IT!"); } }


* **Element Comments:** I'm a big fan of code comments and I'll generally write formatted comments for types etc, before I actually write any actual code.  The comment is essentially the specification for the type and by the time I've finished writing the comment I'll have a really good handle on how the implementation will go.  Please use all of the C# comment formatting goodies.  This only takes a few extra moments and someday we may need to render these into a nicely formatted document when the company acquiring Neon is doing technical due diligence.

  See [Documenting your code with XML comments](https://docs.microsoft.com/en-us/dotnet/csharp/codedoc)

// DO THIS:

///

/// /// This is the most amazing class in the entire history of the world. /// /// /// This class obsoletes the old type which /// ended up being a disappointment. /// /// public class MyFantasticType2 { /// /// Constructor. /// /// <param name="name">The instance name. public MyFantasticType2(string name) { this.Name = name; }
/// <summary>
/// Returns the instance name.
/// </summary>
public string Name { get; private set;}

/// <inheritdoc/>
public override string ToString()
{
    return Name;
}

}

// NOT THIS:

///

/// This is the most amazing class in the entire history of the world. /// /// NOTE: This class obsoletes the old type which /// ended up being a disappointment. /// public class MyFantasticType2 { /// /// /// /// <param name="name">
public MyFantasticType2(string name) { this.Name = name; }
public string Name { get; private set;}

public override string ToString()
{
    return Name;
}

}


  All public and internal types and members must include code documentation.  I sometimes document private types and members as well, if I think it enhances readability.

* **Blank Lines**: In general, code elements like types, methods, properties, etc. should be separated by a single line and you shouldn't have extra blank lines in your code.

// DO THIS:

//----------------------------------------------------------------------------- // FILE: MyFile.cs // CONTRIBUTOR: Joe Bloe, Jane Doe // COPYRIGHT: Copyright (c) 2016-2019 by neonFORGE, LLC. All rights reserved.

using System; using System.Collections.Generic;

using Neon.Common;

namespace Neon.MyStuff { ///

/// /// This is the most amazing class in the entire history of the world. /// /// /// This class obsoletes the old type which /// ended up being a disappointment. /// /// public class MyFantasticType2 { /// /// Constructor. /// /// <param name="name">The instance name. public MyFantasticType2(string name) { this.Name = name; }
    /// <summary>
    /// Returns the instance name.
    /// </summary>
    public string Name { get; private set;}

    /// <inheritdoc/>
    public override string ToString()
    {
        return Name;
    }
}

}

// NOT THIS:

//----------------------------------------------------------------------------- // FILE: MyFile.cs // CONTRIBUTOR: Joe Bloe, Jane Doe // COPYRIGHT: Copyright (c) 2016-2019 by neonFORGE, LLC. All rights reserved.

using System; using System.Collections.Generic;

using Neon.Common;

namespace Neon.MyStuff {

/// <summary>
/// <para>
/// This is the most amazing class in the entire history of the world.
/// </para>
/// <note>
/// This class obsoletes the old <see cref="MyFantastic1"/> type which
/// ended up being a disappointment.
/// </note>
/// </summary>
public class MyFantasticType2
{

    /// <summary>
    /// Constructor.
    /// </summary>
    /// <param name=\"name\">The instance name.</param>
    public MyFantasticType2(string name)
    {
        this.Name = name;

    }

    /// <summary>
    /// Returns the instance name.
    /// </summary>
    public string Name { get; private set;}

    /// <inheritdoc/>
    public override string ToString()
    {
        return Name;
    }

}

}


* **Unit Test Exceptions:** Unit tests don't need to include code documentation and may use underscores in test class and method names.  This can be helpful to avoid name conflicts with the types and namespaces being tested and also to help distinguish related test methods from one another.