code image

Objective

This page attempts to provide coding and design guidelines that are both useful and pragmatic.

Naming Guidelines

Use US-English

All type members, parameters, and variables should be named using words from the American English language.

Choose easily readable, preferably grammatically correct names. For example, “HorizontalAlignment” is more readable than “AlignmentHorizontal.”

Readability over brevity should be preferred. The property name “CanScrollHorizontally” is better than “ScrollableX.”

Avoid using names that conflict with programming language keywords.

It is acceptable to use words, phrases, and/or abbreviations that are domain-specific. “FMR1Module” is preferred over “FragileXMentalRetardation1Module”.

Use Pascal casing for all public identifiers within a class library, including namespaces, classes and structures, properties, and methods.

Use Camel casing for private and protected identifiers within the class library parameters passed to methods.

Use a “prefixed” camel case modification where the name starts with an underscore for variables. This modification helps the reader identify the lifetime of the variable.

Upper casing is used only for abbreviated identifiers and acronyms of four letters or less.

The upper case “snake case” is used when using a number directly in the code. In this situation, the Upper Snake Case overrides the Const or Readonly casing.

The following table shows the casing for object types and lifetime.

IDENTIFIERCASEEXAMPLE
Class, StructPascalDonationManager
InterfacePascalIDonationRepository
Enum typePascalErrorLevel
Enum valuesPascalFatalError
EventPascalValueChange
Exception classPascalWebException
FieldCamellistOfSamples
Const FieldPascalMaximumSamples
Read-onlyPascalApiPortNumber
MethodPascalToString
NamespacePascalSystem.Drawing
ParameterCameltypeName
Type ParameterCamelTView
PropertyPascalBackgroundColor
Magic NumbersUpper Snake CaseAGAPE_GREEN
Case style

Design Guidelines

These design guidelines will help maintain fluid, manageable and maintainable code.

Generally speaking, you should try to adhere as closely as possible to the SOLID principles of object-oriented design:

  • S – Single Responsibility Principle
    • An object should have only a single responsibility.
  • O – Open-Closed Principle
    • An object should be open for extension but closed for modification.
  • L – Liskov Substitution Principle
    • Objects in a program should be replaceable with instances of their subtypes without altering that program’s correctness.
  • I – Interface Segregation Principle
    • Many client-specific interfaces are better than one general-purpose interface.
  • D – Dependency Inversion Principle
    • Depend upon abstractions, do not depend upon concretions. Base classes should not depend on subclasses.

Objects

When designing the object, consider the responsibility, accessibility, and lifetime of the object. Start with the smallest responsibility, strictest access, and shortest lifetime.

Use classes as an abstraction of real objects or business concepts, struct as representations of data, and events as an action to be taken.

Consider the environment where the code will run as you design the objects. It is ok to create singletons or static classes when writing for desktop applications. However, you will want to avoid it if you want your code to scale.

Concrete classes have their place, but they are tough to test. Consider using interfaces and dependency injection when unit tests are required. In most cases, unit tests should be required.

When an object is created, it should be usable. You should ensure that classes have behaviors and states where behaviors change the state.

When writing a multi-threaded application, consider using the “volatile” keyword. This keyword is underutilized and can be very powerful when used appropriately.

It is encouraged to avoid using void methods. It is easier to troubleshoot a method when we know what it is returning. However, “void” methods do have their place. Therefore, it is essential that you use XML documentation so that IntelliSense can tell you what you need.

Exceptions

Always log exceptions.

Throw exceptions rather than returning some kind of status value. When doing this, make sure that the exception is the most specific and that the message is rich and meaningful. Remember that you might be the one troubleshooting the code a few months after you completed the code. You will want to make sure that you are communicating the right information to you of the future.

Avoid swallowing errors by catching non-specific exceptions, such as Exception, SystemException, and so on, in application code. Only top-level code, such as a last-chance exception handler, should catch a non-specific exception for logging purposes and a graceful shutdown of the application.

When writing asynchronous code, remember that exceptions that occur within the async/await block and inside a Task’s action are propagated to the awaiter but not the caller.

Maintainability

Maintainable code generally leads to higher quality and lower bugs.

Prefer readable, maintainable code over brevity. Think about someone else stepping through your code. What would be useful for that person?

  • Methods should be short. If you find yourself writing a large method, consider breaking it out into more descriptive private methods and clearly document what it does.
  • Avoid double negatives.
  • Avoid Magic Numbers in your methods. Instead, create const that holds the value and name it in a way that makes it clear.
  • Avoid using “var” when the type is not obvious.
  • When declaring a variable, do it as late as possible.
  • Avoid using Thread.Sleep(). If you find that you need to use it, you should consider refactoring not to need it.
  • Don’t change loop variables inside a loop.
  • Avoid nesting loops.
  • Favor the use of “using{}” as often as possible to make sure that memory is cleaned up as fast as possible.
  • Avoid using the “dynamic” types. Instead, favor using “object.”
    • The “dynamic” keyword tells the compiler that you do not know the type until runtime. Since the compiler does not check or validate this type, it could cause problems at runtime that are not easy to diagnose.
  • Avoid requiring the use of reflection.

Note: There are valid use cases for “dynamic” types and for “reflection.” However, the potential hazards are more significant than the general or casual benefits that they provide. So, in general, use cases, avoid using either. But, if you find a specific use case where you can benefit from these, use them wisely and seek council.

  • Favor the use of “struct” over complex tuples.
  • Consider one entry and one exit. Multiple returns can be confusing. If you must validate conditions before performing work, consider making the validation a separate method.
  • Multi-threaded and async methods are not the same. Async methods use background workers where Thread.Start() runs code in a separate thread.

Comments and Documentation

Use US English for comments. You should use inline comments to highlight the flow in the code and to explain a complex area. You should not use inline comments for obvious code.

Using XML tags is a must. Using these tags will allow IntelliSense to provide useful details while using the types. Also, automatic documentation generation tooling relies on these tags.

References

Microsoft. 2017. What Is Windows Communication Foundation? 29 03. Accessed 04 16, 2019.

OAuth Foundation. n.d. OAuth 2.0. Accessed May 2, 2019. https://oauth.net/2/.

OpenID Foundation. n.d. OpenID Connect FAQ and Q&As. Accessed May 2, 2019. https://openid.net/connect/faq/.

OWASP. 2019. OWASP Top Ten Project. 28 Feb. Accessed May 2, 2019. https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project.

Smith, James. n.d. Coding Standards. Accessed Oct 22, 2020. https://expertdev.blog/coding-standards/.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *