The Ed-Fi ODS API Coding Standards are based partly on the following sources:
- IDesign C# Coding Standard 2.4, as published at www.idesign.net. Copyright (C) 2011, IDesign Inc., All Rights Reserved.
- The "Contributing" guide for Microsoft's .NET Core github repository at https://github.com/dotnet/corefx/wiki/Contributing.
Naming Conventions
Use Pascal casing for type, method names and constants:
public class SomeClass { const int DefaultSize = 100; public void SomeMethod() { … } }
Use camel casing for local variable names and method arguments:
public void SomeMethod(int someNumber) { int number; }
Prefix interfaces with
I
.public interface ISomethingProvider { … }
Suffix interface implementations with the non-prefixed interface name.
public class ThisSomethingProvider : ISomethingProvider { … }
Prefix all fields with an underscore (_).
public class SomeClass { private ISomethingProvider _thisSomethingProvider; }
Name methods using a verb or verb-object pair (unless implementing a fluent API).
public decimal CalculateTax(decimal amount) { … }
Use clear and descriptive names for classes, methods, fields and variables. Make sure that after refactoring code that the names are still appropriate.
You can use single-character or mnemonic variable names in the following scenarios:
In
for
loops where it is a common convention to use variables likei
,j
, andk
.In LINQ expressions where it is a common convention to use variables like
x
or a mnemonic for the item being represented (e.g.ssa
for an object of typeStudentSchoolAssociation
).
Name dictionaries using a name format of {ValueName}By{KeyName}.
Precisely describe the keys and values (e.g.
SchoolNameById
would indicate that you can obtain a school's name by its identifier.If each entry’s value is a single item, the name should be singularized (e.g.
StudentById
).- If each entry’s value is a collection, the name should be pluralized (e.g.
StudentsBySectionId
).
Do not abbreviate terms as this leads to usage inconsistency in the code and other application artifacts. Specifically, do not abbreviate "EducationOrganization" as “EdOrg” or “LocalEducationAgency” as “Lea”.
Name abstract classes using a suffix of
Base
(e.g.EdFiControllerBase
).- Use the following guidelines when defining generic types:
For types with a single generic type, prefer the use of "
T
".public interface IList<T>
For types with multiple generic types, use a capital "T" followed by an optional secondary name for additional clarity. In all cases, start the type with a capital letter.
public interface IService<TRequest, TResponse>
Name custom attribute and exception classes using suffixes of
Attribute
andException
, respectively.
Coding Style
- Use 4 spaces for indentation. Do not use tabs.
Use the idomatic C# types instead of .NET Framework types.
// Good string a; object b; int c; double d; // Bad String a; Object b; Int32 c; Double d;
Use explicit types for value-typed variables.
// Good int age = 7; string greeting = "Hello world."; double elephantWeight = 123.45; // Bad var age = 7; var greeting = "Hello world."; var elephantWeight = 123.45;
Use
var
for everything else (including Linq query results regardless of the type), remembering to name the variable in a way that makes the type obvious.Prefer C#'s SQL-like syntax for LINQ queries over method chaining.
var students = _studentsService.GetAll(); // Preferred var firstBobsLastName = (from s in students where s.FirstName = "Bob" select s.LastName) .FirstOrDefault(); // Not preferred var firstBobsLastName = students.Where(x => x.FirstName == "Bob").Select(x => x.Name).FirstOrDefault();
- Use comments liberally to provide useful context for another developer to be able to follow the intent/logic of the code.
Comments should include a space between the forward slashes and the text.
// This is the preferred style //this is not preferred
Comments should have a blank line before and, at the very least, after the segment of code to which the comment applies.
var source = _injectedService.GetScores(); // Calculate the average int count = scores.Count; double sum = scores.Sum(); double average = sum / count; foreach (var item in scores) { Console.WriteLine("Doing something else.") }
- Use the following guidelines when using braces (
and{
}
):Use Allman style braces where each brace begins on a new line.
if (a == b) { // More logic here }
Do not use single-line
if
statements.// Bad if (source == null) throw new ArgumentNullException(); // Good if (source == null) throw new ArgumentException();
Braces should be eliminated if the nested blocks are single line statements.
// Good if (_items == null) _items = source.ToList(); // Bad if (_items.Count == 0) foreach (var item in source) _items.Add(“Item “ + item); // Good if (_items.Count == 0) { foreach (var item in source) _items.Add(“Item “ + item); }
Don’t mix and match bracing styles within the same block.
// Bad if (source == null) throw new Exception(“Something bad happened.”); else { // More logic here … }
- Braces are always acceptable (but reducing their use is preferred because they tend to add “noise” to the code).
Avoid more than one empty line at any time. For example, do not have two blank lines between members of a type.
if (a == b) DoSomething(); else DoSomethingElse(); Console.WriteLine("There's too many blank lines above this one.");
Do not add blank lines between sets of of closing braces.
if (a == b) { if (c == d) { if (e == f) { DoSomething(); } } } // Blank line above this brace should be removed.
- Do not exceed about 30-40 lines of code in a single method. After that, refactor the method into smaller well-named methods.
- Limit the length of a line of code to about 100-110 characters to reduce unnecessary scrolling. Github shows about 113 characters, and many developers now use their monitors in "portrait" orientation.
Namespace imports should be specified at the top of the file, outside of
namespace
declarations and should be sorted alphabetically, withSystem.
namespaces at the top.Good Namespace Styleusing System; using System.Web; using EdFi.Ods; using EdFi.Ods.Common; namespace EdFi.Ods.Data { … }
Bad Namespace Styleusing EdFi.Ods; using System.Web; using System; namespace EdFi.Ods.Data { using EdFi.Ods.Common; … }
Favor a style of "exit fast".
// Good public void SomeMethod(ISomethingProvider somethingProvider) { if (somethingProvider == null) throw new ArgumentNullException("somethingProvider"); var things = _somethingElseProvider.GetThoseThings(); … } // Bad public void SomeMethod(ISomethingProvider somethingProvider) { if (somethingProvider != null) { var things = _somethingElseProvider.GetThoseThings(); … } else { throw new ArgumentNullException("somethingProvider"); } }
- Do not use regions (i.e.
#region
). - Favor adding XML documentation for public methods and properties of interfaces and classes.
- Do not use XML documentation blocks to place comments.
- Follow documentation conventions from Microsoft (when in doubt, explore http://msdn.microsoft.com as a style reference):
- Start the XML summary of methods and properties as though you are finishing a sentence that has already started with the text “This property or method …”
- Use
<see cref="SomeOtherClass" />
to reference other classes in the documentation. - Use
<b>true</b>
tags to emphasize keywords liketrue
,false
andnull
. - Constructors – “Initializes a new instance of the {classname} class [using the specified …].”
- Properties – “Gets or sets the …” or “Gets the …”
- Methods – “Appends a copy of the specified string….”
When specifying <return> documentation, use a format like you’re finish a sentence that starts with “Returns…”, and use semi-colons to tack on the “otherwise” conditions.
String.Contains <returns> content<returns><b>true</b> if the value parameter occurs within this string, or if value is the empty string(""); otherwise, <b>false</b>.
- Define custom exception classes when you expect the exception to be explicitly handled somewhere else in your application. It's a lot more reliable to match an exception based on its type than by extracting information from the message.
- Do not reuse .NET framework exceptions outside of their semantic context (e.g.
ObjectNotFoundException
is defined in multiple places, andAuthorizationFailedException
andSecurityAccessDeniedException
have specific context where they're used).
Design Guidelines
- Apply SOLID principles everywhere. (Highly recommended reading: Chapters 8-12 of Agile Principles, Patterns, and Practices by Robert C. Martin.)
- Single Responsibility Principle - There should only be one reason for a class to change. If you're having to change an existing class, ask yourself, "Is there an abstraction (interface) missing here?"
- Open/Closed Principle - The system should be open for extension, but closed to modification. You should be able to change the behavior of the system by adding a new implementation of an existing interface.
- Liskov Substitution Principle - A derived class should be substitutable its base class, and should not change the fundamental of the abstraction.
- Interface Segregation Principle - An interface should be highly focused. Many interfaces in the ODS API have just one or two methods.
- Dependency Inversion Principle - External dependencies are injected into classes, preferably via the constructor. The Ed-Fi ODS API uses Castle Windsor as its Inversion of Control container.
- Class design
- Do not return types from a public member that allows the data to be modified unless this is the intent.
Testing Style Guidelines