Improving Your Team's Productivity Through Consistent Code Style
Tags: dotnet, csharp
Published: 2024-07-01 10:58:22

---
title: Improving Your Team's Productivity Through Consistent Code Style
published: true
date: 2024-06-17 07:00:01 UTC
tags: dotnet,csharp
canonical_url: https://blog.postsharp.net/code-style.html
series: The Timeless .NET Engineer
---

 ![](https://blog.postsharp.net/assets/images/2024/2024-06-code-style/code-formatting.svg)

While it may seem trivial, the first step towards fostering a maintainable, team-friendly codebase is reaching a consensus on code style and ensuring its strict enforcement. This includes code formatting (spaces, blank lines, parentheses, and brackets), naming (casings, prefixes, suffixes, etc.), and usage of `this`, `var` and more. The ultimate goal? Make running a full automated clean-up on your codebase a routine task. In this article, I explain the practices we are following on the [Metalama](https://www.postsharp.net/metalama) team. After all, since we are building tools that help C# developers write better quality code, it’s only logical that our own code is of the highest possible quality. Don’t take my words for granted: our source code is [available on GitHub](https://github.com/postsharp/Metalama.Framework/), so you can check for yourself if we live up to our standards.

## Code Style As a Philosophy

You might be wondering, why all the fuss about code style? Let’s highlight two main reasons:

1. A consistent style makes code easier to read. Since developers typically spend 80% of their time _reading_ code, code quality is one of the most important factors for the long-term productivity of your team. There are tools, like [ReSharper’s Virtual Formatter](https://blog.jetbrains.com/dotnet/2022/08/11/virtual-formatter-in-resharper-2022-2/), that can help with reading code in a consistent style while you are in the IDE, but that doesn’t solve the issue in other places where code is read, such as your Git repository.

2. A uniform code style simplifies pull requests and merges. Have you ever been frustrated by irrelevant changes appearing in a PR diff because a developer ran a code cleanup? This annoyance could’ve been avoided if the code style had been adhered to from the start. Consistent code style also minimizes merge conflicts. One of the objectives of the code quality pipeline is that everybody should be able to reformat the entire codebase without causing a revolution in the team – as long as it’s done in a separate PR of course. In other words, complete reformatting should be a non-event.

## Step 1. Achieving Consensus on Code Style

While coding style can be a matter of preference, it’s crucial that everyone on the team aligns with a chosen convention. Everyone will have their own opinion, which can easily lead to endless and heated debates. Your main objective here is to get through the consensus-building process as soon and quickly as possible.

1. I suggest starting with Microsoft’s [common coding convention](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions) for C#. It’s a solid starting point, widely accepted by the C# community, but it doesn’t cover every detail.
2. At the beginning of the project, you might still find yourselves in lengthy discussions. Don’t dismiss these debates: it is better to have them sooner than later. Listen to all arguments swiftly, remind everyone that the emphasis is not on the chosen style itself, but on making an unambiguous decision that will be consistently applied. Ensure everyone is aware of the [bikeshedding](https://en.wikipedia.org/wiki/Law_of_triviality) dynamics in collective decision-making. Then, let the most experienced developer make the final call.

Remember, deciding on a coding style is an iterative process. Don’t spend too much time on this step—you’ll revisit it later.

## Step 2. Configuring Your IDE

Next, you need to configure your coding style in the IDE.

### Configuring Code Formatting

To keep everyone on the same page, store the coding style configuration in the source repository, not in the user profile. This file is typically named [.editorconfig](https://editorconfig.org/). The EditorConfig format is supported by Visual Studio, ReSharper, Rider, and many other editors.

Here’s how you can use the Code Style editor in Visual Studio:

![Code Style options dialog in Visual Studio](https://blog.postsharp.net/assets/images/2024/2024-06-code-style/vs-options.png)

Your code style configuration should be thorough and specific, leaving no room for personal interpretations. I recommend setting the severity to warning whenever _practically_ possible. Some formatting rules are brittle because they can be easily broken if you have `#if` clauses in your code. On our team, we turn off any brittle rules. We only enforce code formatting in the `Debug` build configuration because having a zero-warning codebase for all platforms and all configurations is very cumbersome for minimal benefits.

In addition to `.editorconfig`, we also use JetBrains’ tools due to their superior code formatting and cleanup capabilities. We set up a _team-shared layer_ in Rider to ensure the Rider configuration is stored in the source repository. Instead of the `*.sln.DotSettings` file, I choose a different layer file that can be imported from all solutions in the repository.

In addition to .editorconfig, we also use JetBrains’ tools due to their superior code formatting and cleanup capabilities. Here’s the Rider settings dialog:

![Layers options dialog in Rider](https://blog.postsharp.net/assets/images/2024/2024-06-code-style/rider-code-style.svg)

In our team, we set up the same team-shared layer in Rider in each solution to ensure the Rider configuration is stored in the source repository. So instead of the \*.sln.DotSettings file, we create a different layer file that can be imported from all solutions in the repository. To access layers, click on the Managed Layers at the left bottom corner of the settings dialog.

![Layers options dialog in Rider](https://blog.postsharp.net/assets/images/2024/2024-06-code-style/rider-layers.png)

When the layer is created, make sure to save your code style changes into the proper layer. It’s a good practice for team members to reset their code style settings in both personal layers.

Feel free to copy our [.editorconfig](https://github.com/postsharp/Metalama.Framework/blob/release/2024.1/eng/style/.editorconfig) and [Rider settings](https://github.com/postsharp/Metalama.Framework/blob/release/2024.1/eng/style/CommonStyle.DotSettings).

### Configuring Code Cleanup

Now that you have set up your code formatting preferences, the next step is to configure a code cleanup profile.

I suggest adding all _harmless_ fixers, i.e., those that make minor syntax changes and, most importantly, are 100% bug-free. For example, include _Apply parenthesis parameters_ and _Add this qualifications_, but not _Make field readonly_ because this analysis sometimes makes mistakes. The ultimate goal is to be able to use your IDE’s _clean up_ feature confidently and without any manual tweaking afterward, so I would avoid any risky fixer.

Unfortunately, Visual Studio does not allow saving cleanup profiles in source control. Ideally, all team members should use the same profile. While Visual Studio offers a _cleanup on save_ option, I am not using it because it sometimes has bugs, and when it does, you have no way at all to fix them except by opening your file in another editor than Visual Studio.

Rider, on the other hand, allows to store the clean-up profile in the team-shared settings layer. Instead of format-on-save, Rider can automatically perform a code cleanup before each commit. I personally don’t use this feature because I always use Rider for git commits. Besides, since some of my teammates prefer Visual Studio, we need a vendor-neutral solution.

## Step 3. Reporting Warnings for Style Violations

Remember that our goal is to improve the team’s productivity, both in the _short_ and _long_ terms. If your process is too lax, your code quality will degrade, and you will hamper your long-term productivity. However, if your process is too strict, any build or pull request could become a nightmare.

Therefore, it’s essential to find a good balance. We’ve found the following compromise to work well for us:

1. As I said before, we set the severity of most code style violations to _warning_. For instance, our coding style requires all instance members to be qualified with `this`, which translates to the following lines in `.editorconfig`. Note the `warning` setting.

2. We allow builds with warnings on development machines. Why? Because you certainly don’t want to have a perfectly formatted build every time you want to run your tests or your apps.

3. However, in CI builds, we treat warnings as errors. This way, code with style issues won’t be merged. To detect continuous integration builds, use the [ContinuousIntegrationBuild](https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#continuousintegrationbuild) property or the specific environment variable of your CI pipeline.

4. While this article focuses on code style and conventions, you should also enable code analysis for various rulesets. Check out [Overview of .NET source code analysis](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) for more details. We on the Metalama team are also using StyleCop, which is redundant with Rider/Resharper analysis but integrates directly with the C# compiler.

Avoid being too strict about _whitespace_ violations. Requiring an exact number of whitespaces for a merge could lead to multiple rounds of whitespace fixing commits, which can slow down productivity. Remember, your ultimate goal is to improve productivity, not to achieve a whitespace-perfect codebase.

## Step 4. Planning for Periodic Full Cleanups

At this point, your development and build pipeline should ensure that PRs are reasonably well-formatted before they undergo code review. While demanding perfection for each PR is impractical, formatting defects can accumulate over time, necessitating a thorough cleanup.

On the Metalama team, we perform a comprehensive cleanup immediately after the _dev freeze_ milestone of each release, usually every 6 to 12 weeks. The goal is to periodically restore the codebase to perfect order.

Running the cleanup tool should be a non-event. It should be entirely deterministic, non-disruptive, and not cause any frustration.

You have two main options:

- [dotnet format](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format) is a .NET SDK command-line tool that reformats code to align with `.editorconfig` settings. However, its capabilities are currently limited. For me, “good enough” means being entirely satisfied with the tool’s output, and `dotnet format` isn’t quite there yet.
- JetBrains offers two types of cleanup tools. You can use the interactive tool in ReSharper or Rider, or ReSharper’s [CleanupCode](https://www.jetbrains.com/help/resharper/CleanupCode.html) command-line tool. The latter is free and doesn’t require a license for the entire team if all you need is code reformatting. This is what our team uses. You will need at least one license to set up the correct configuration.

To ensure all runs produce the same predictable output, create a script that runs the tool with the exact parameters. This output, termed the _canonically formatted code_, should be your gold standard.

From this point forward, no one should be blamed for reformatting code to its canonical form. Instead, the blame should be placed on the developer merging non-canonical code.

## Step 5. Taking Code Validation to the Next Level

If you’ve successfully implemented all the above steps, congratulations! Your processes now ensure consistent adherence to the code style, making team members more confident in reformatting and refactoring code.

To further enhance codebase maintainability and readability, consider validating your codebase with more complex rules. For instance, enforcing that all classes implementing `IFactory` end with the `*Factory` suffix, or checking that no one uses the `double` type in the `Billing` namespace (you should use `int` or `decimal` for any money!). Consider writing [architectural unit tests](https://github.com/TNG/ArchUnitNET) or using our tool Metalama to enforce [naming conventions](https://doc.postsharp.net/metalama/conceptual/architecture/naming-conventions) and [verify dependencies](https://doc.postsharp.net/metalama/conceptual/architecture/usage).

For example, here is how you could enforce a naming convention using Metalama:

```cs
[DerivedTypesMustRespectNamingConvention( "*Factory" )]
public interface IFactory<T>
{
   T Create();
}

```

And here is how you could prohibit internal members of a namespace from being used from a different namespace:

```cs
namespace TheNamespace
{
    internal class Fabric : NamespaceFabric
    {
        public override void AmendNamespace( INamespaceAmender amender )
        {
            amender.Verify().InternalsCanOnlyBeUsedFrom( 
                                   r => r.CurrentNamespace() );
        }
    }
}

```

## Conclusion

Mastering code formatting is a crucial step towards creating a maintainable, team-friendly codebase. Adhering to and enforcing a particular style creates an environment that improves code-reading productivity, a critical factor considering that developers spend a whopping 80% of their time reading code. Consistent style also reduces merge issues and simplifies pull requests. A clean, well-maintained codebase is a pleasure to work with and the pride of every team member.

---

This article was first published on a [https://blog.postsharp.net](https://blog.postsharp.net) under the title [Improving Your Team's Productivity Through Consistent Code Style](https://blog.postsharp.net/code-style.html).