Imagine you want to create a coding convention for your development team. One approach would be to adopt a strategy that bigger companies adopt. While this approach can be a good starting place, it’s not exactly ideal. Because there are a lot of criteria in your team that can change what is the best way FOR YOUR TEAM. So, in post we’re going to take a look at those criteria and give examples when you might want to reconsider your selected coding convention.
Things to Consider When we Adopt a Coding Convention
Here are some of the things that you might want to consider when selecting a coding convention for your team.
- Consistency with Existing Standards
-
- .NET Framework Guidelines: Adhering to Microsoft’s official .NET coding conventions ensures consistency with widely accepted practices and enhances readability and maintainability.
- Refer to Microsoft’s C# coding conventions.
- React and JavaScript/TypeScript Standards: Follow the recommended practices from the React documentation and popular JavaScript/TypeScript style guides like Airbnb or Google.
- .NET Framework Guidelines: Adhering to Microsoft’s official .NET coding conventions ensures consistency with widely accepted practices and enhances readability and maintainability.
- Team Expertise and Preferences
-
- Skill Levels: Consider the experience level of your team members. Conventions should be accessible to all, from junior developers to senior engineers.
- Team Input: Involve the team in the decision-making process to ensure buy-in and adherence.
- Project Requirements and Constraints
- Project Size and Complexity: Larger projects might benefit from stricter conventions to manage complexity.
- Codebase Maintenance: If the project is long-term, robust conventions help with maintaining and scaling the code.
- Tooling and Automation
- Linters and Formatters: Utilize tools like ESLint for JavaScript/TypeScript and StyleCop or Roslyn analyzers for .NET to enforce conventions automatically.
- Prettier: Consider using Prettier for code formatting in JavaScript/TypeScript projects to ensure consistent styling.
- Documentation and Training
- Documentation: Maintain a well-documented style guide that’s easily accessible to the team.
- Training: Provide regular training sessions or resources to help the team understand and follow the conventions.
- Performance and Best Practices
- Performance Implications: Ensure that the conventions promote efficient coding practices, especially for performance-critical applications.
- Security: Incorporate secure coding practices into your conventions to mitigate common vulnerabilities.
- Review and Adaptation
- Code Reviews: Regular code reviews should be part of the workflow to ensure adherence to conventions and to facilitate continuous improvement.
- Feedback Loop: Periodically review and update the conventions based on team feedback and evolving best practices.
Example Conventions for .NET and React
.NET (C#) Example Conventions:
- Naming:
- Classes and interfaces should use PascalCase.
- Methods should use PascalCase.
- Private fields should use camelCase with an underscore prefix (_camelCase).
- Constants should be in all uppercase with underscores (ALL_UPPERCASE).
- Braces:
- Use braces for all control structures (if, else, for, while, etc.).
- Opening brace on the same line as the declaration.
- Spacing:
- Use four spaces for indentation, no tabs.
- One space before and after operators and keywords.
React (JavaScript/TypeScript) Example Conventions:
- Naming:
- Component names should use PascalCase.
- Hook names should use camelCase and start with “use” (e.g., useFetch).
- File Structure:
- One component per file.
- Group related files (e.g., styles, tests) in the same directory as the component.
- JSX:
- Use self-closing tags when an element has no children.
- Use single quotes for strings in JSX attributes.
- Hooks:
- Use hooks at the top level of React function components, not inside loops, conditions, or nested functions.
Hands on Exercise to Change a Coding convention Based on the Needs of our team
In this section I’m going to first introduce a coding convention that is mostly copied from Microsoft coding convention, then we’re going to see how this coding convention is going to change based on the needs of our team and I’m going to explain why that change is meaningful to our team.
Sample Coding Convention
Braces
- Style: Start braces on a new line using Allman style.
- Single Line Statement Blocks:
- May omit braces if properly indented and not nested within other statement blocks.
- Exception: A nested using statement can start on the following line at the same indentation level, even if it contains a controlled block.
- Single-Statement If:
- Never use the single-line form, e.g., if (source == null) throw new ArgumentNullException(“source”);.
- Braces are required if any block of an if/else if/…/else compound statement uses braces or if a single statement body spans multiple lines.
- Braces may be omitted only if the body of every block associated with an if/else if/…/else compound statement is placed on a single line.
Indentation
- Use four spaces for indentation (no tabs).
Naming Conventions
- Fields and Variables:
- Use camelCase for internal and private fields.
- Use readonly where possible.
- Prefix internal and private instance fields with _.
- Use PascalCasing for public fields with no prefix, used sparingly.
Spaces
- Avoid unnecessary spaces, such as extra spaces around variables (e.g., if (someVar == 0)).
Visibility
- Always specify the visibility of a field or variable, even if it’s the default.
- Visibility should be the first modifier.
Namespace Imports
- Specify namespace imports at the top of the file, outside of namespace declarations.
- Sort imports alphabetically, with System.* namespaces placed on top.
Empty Lines
- Avoid more than one empty line at any time, such as between members of a type.
Language Keywords
- Use language keywords instead of long names (e.g., int, string, float instead of Int32, String, Single).
Var Usage
- Use implicit typing (var) for local variables when:
- The type is obvious from the right side of the assignment.
- The precise type is not important.
Constant Variables and Fields
- Use PascalCasing for naming constant local variables and fields.
- Exception: For interop code, the constant value should exactly match the name and value of the code you are calling via interop.
nameof(…)
- Use nameof(…) instead of writing the name out manually.
Field Placement
- Fields should be specified at the top within type declarations.
Non-ASCII Characters
- Use Unicode escape sequences (\uXXXX) instead of literal characters for non-ASCII characters.
Comments
- Use comments only if the meaning of the code cannot be conveyed using good coding practices.
- Guidelines:
- Place the comment on its own line.
- Start the comment text with a capital letter.
- End the comment text with a period.
- Add one space between the comment delimiter (//) and the comment text.
Queries
- Use meaningful names for query variables, e.g.,
var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;
Best Practices
- Follow SOLID principles for designing classes and modules.
- Avoid class-level variables; use local variables instead.
- Use constants for values that don’t change.
- Avoid magic numbers in code; use well-named constants or variables.
- Follow a consistent code style throughout the project.
- Use the using statement to dispose of resources that implement the IDisposable interface.
- Avoid deep inheritance hierarchies; prefer composition over inheritance.
- Use the is and as operators to test and cast objects.
- Use the nameof operator to get the name of a variable, type, or member.
Modified Coding Convention to Fit the Needs of Our Team
1. Team Experience and Preferences
Original Convention:
- Braces: We always start braces on a new line and use Allman style braces.
- Single line statement block can go without braces but must be properly indented on its own line and must not be nested in other statement blocks that use braces.
Modified Convention: If the team is composed mostly of junior developers, it might be more beneficial to enforce stricter rules to avoid common mistakes:
- Braces: Always use braces for all control structures, even for single-line statements. This reduces the likelihood of errors and enhances code readability for less experienced developers.
- Reasoning: Junior developers might struggle with identifying the scope of single-line statements, leading to potential errors. Using braces consistently can help them understand the structure of the code better.
// Original if (source == null) throw new ArgumentNullException("source"); // Modified if (source == null) { throw new ArgumentNullException(nameof(source)); }
2. Project Size and Complexity
Original Convention:
- Indentation: We use four spaces of indentation (no tabs).
Modified Convention: For a large-scale project, readability and maintainability become crucial:
- Indentation: Stick with four spaces, but also enforce stricter rules around maximum line length and nesting depth to prevent deeply nested and hard-to-read code.
- Reasoning: Large projects can become unwieldy, and enforcing these rules helps keep the codebase manageable.
// Ensure that lines do not exceed 120 characters and limit nesting to 3 levels deep. if (someCondition) { if (anotherCondition) { if (yetAnotherCondition) { DoSomething(); } } }
3. Tooling and Automation
Original Convention:
- Var: Use implicit typing (var) for local variables when the type of the variable is obvious from the right side of the assignment, or when the precise type is not important.
Modified Convention: If the team is heavily using static analysis tools and linters, the conventions might need to align with the rules enforced by these tools:
- Var Usage: Restrict the use of
var
to scenarios where it significantly improves readability or when the exact type is clear from the context. Explicit typing should be preferred to avoid confusion. - Reasoning: Tools like StyleCop or Roslyn Analyzers might have specific rules that help maintain a consistent and clear codebase. Aligning with these tools can improve code quality and catch errors early.
// Original var customerName = customer.Name; // Modified string customerName = customer.Name;
4. Performance and Best Practices
Original Convention:
- Namespace imports: Namespace imports should be specified at the top of the file, outside of namespace declarations, and should be sorted alphabetically, with the exception of System.* namespaces, which are to be placed on top of all others.
Modified Convention: For performance-critical applications, additional conventions might be necessary:
- Namespace Imports: Enforce the removal of unused namespaces using tools or IDE settings to avoid unnecessary overhead.
- Reasoning: While it may seem minor, keeping the code clean from unused imports can slightly improve compilation times and reduce clutter.
// Original using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; // Modified (with unused namespaces removed) using System; using System.Collections.Generic;
5. Review and Adaptation
Original Convention:
- Comments: Do not use comments unless the meaning of the code cannot be conveyed using good coding practices.
Modified Convention: Based on team feedback and ongoing reviews, the convention around comments might be adapted to encourage better documentation:
- Comments: Encourage the use of XML documentation comments for public methods and properties to improve codebase documentation and support tools that generate documentation automatically.
- Reasoning: Regular code reviews might reveal that certain parts of the codebase are not well understood without comments. Encouraging XML documentation can help bridge this gap.
// Original // Get the name of the customer from the database var customerName = customer.Name; // Modified /// <summary> /// Gets the name of the customer from the database. /// </summary> /// <param name="customer">The customer object.</param> /// <returns>The name of the customer.</returns> public string GetCustomerName(Customer customer) { return customer.Name; }
By considering factors such as team experience, project requirements, and tooling, you can tailor your coding conventions to better suit your specific circumstances. Regularly revisiting and adapting these conventions based on feedback and evolving needs ensures that your coding standards remain effective and relevant.
Summary
Don’t just copy a coding convention from another company! Instead, consider your specific team’s needs. This blog post explores factors that can influence your coding conventions, including team experience, project size, and tooling. Examples are provided to illustrate how conventions can be adapted to fit these different needs. Regularly review and update your conventions to keep them effective.