Coding Standards
Prefer asynchronous calls whenever possible
Asynchronous calls are assigned to separate threads and managed by TPL. This frees up the main thread and makes the software more responsive.
Not all cases need to be asynchronous, but in cases of intensive processing or multi-user access (e.g., APIs), it is recommended to use asynchronous calls whenever possible.
When using async, do it all the way
Building on the above item, once an asynchronous call is necessary, all subsequent calls that have this capability should be used asynchronously.
Interrupting this asynchronicity midway ends up making the entire flow synchronous, as the current thread needs to be locked while waiting for a result.
Prefer Ternaries and Null Coalesce
Both structures simplify and facilitate code readability.
Ternary:
Null Coalesce:
Prefer string interpolation
String interpolation allows easy and practical formatting of strings.
Guards
A Guard is a validation call that tests data and breaks the flow when the condition is not met.
They are primarily used for input and output parameter validations to strengthen software consistency through Defensive Programming.
An excellent library for this is Ardalis.GuardClauses, although we have built a wrapper around to extend it and make it feature complete.
Break chains of calls into separate lines
You typically won't use method chains, but well-known ones include LINQ and other Fluent APIs.
Break these calls into separate lines to facilitate readability, especially when including lambdas.
Hard to read:
Easy to read:
Default language for code is English for new projects
Regardless of the language you speak, the default language for code should be English.
Exceptions are domain-specific names for adherence to ubiquitous language.
The reasons for this are as follows:
Consistency: All programming languages on the market were written in English, so we should adhere as much as possible to maintain code consistency.
Convenience: All market standards were also conceived in this language, making it much more complicated to translate or adapt terms.
Avoid the use of abbreviations
Do not abbreviate names or use acronyms that are not commonly known to all who may be involved with the code.
Also, avoid type or scope affixes, such as strText.
Avoid in-liners for readability
In-liners (operations that can be written in a single line) are good only when they are not complex.
Complex in-liners reduce readability, especially for new developers on the team.
As a general rule, avoid complex ones.
No matter if the code becomes more "extensive" as long as it is easy for others to read, understand, and maintain.
Bad:
Good:
Add comments to public scopes for documentation purposes
Add documentation comments (<summary>) to all public scopes (Classes, Methods, Properties, etc.).
Include related tags, such as params, returns, exceptions, etc.
It is always very important to mention in the exceptions tag all exceptions that can be thrown, directly or indirectly, by the scope.
Low Coupling, High Cohesion
Prefer independent components (decoupled) with well-defined purposes.
Following the SOLID principles is already adhering well to this guideline.
Have only one class per .cs file except when inherently related
A .cs file should generally contain only one class.
Exceptions:
Generic classes with the same name but different type parameters;
Sub-classes when necessary.
Avoid logic in property gets/sets
Properties should not contain logic inside, working similarly to variables. Code inside them is difficult to visualize and trace.
For cases where there is a need, such as common cases of notification with PropertyChangedNotify, use methods called by the property.
FluentAssertions is recommended for testing
The FluentAssertions library makes writing test validations easier and more fluent.
Standard:
FluentAssertions:
Test pattern: Given/When/Then
This pattern makes it easy to test the right parts while providing useful information about the test case.
Example:
Class and method versioning
In many cases, there is a need to maintain different versions of the same code for compatibility reasons.
The correct way is to version with namespaces, allowing the choice of version when in use.
Example:
Corporate Standards
Version routes
Routes should be versioned with "/vX/", where X is the version number. This will help introduce new versions and maintain compatibility.
Adhere to RESTful API standards
Do not use verbs in routes, but prefer http verbs
Bad:
Good:
Avoid separators ("-") in favor of nested routes
Bad:
Good:
Use filters as query string parameters
Use HTTP verbs to define the type of action
GET: Queries by id or listings
POST: Entity creation or complex executions
PUT: Entity modification
DELETE: Entity removal
Use consistent error codes:
20X: Success
40X: Client did something wrong
50X: The System behaved unexpectedly
Going deeper:
Examples:
Data Validation
We use FluentValidations in our APIs to validate inputs. It is a great library as it is very flexible and decouples the validation logic from the current code where data is used.