Skip to main content

Modern C# Code Quality Tooling (2026)

MANDATORY: All C# projects MUST use the modern .NET tooling stack for code quality enforcement.

This document describes the modern, actively-maintained tooling stack for C# code quality in 2026.

Overview

Modern C# Tooling Philosophy:

  • Use Microsoft-supported tools as the foundation
  • Add actively-maintained community tools for enhanced coverage
  • Prefer opinionated formatters over configurable ones (zero debates)
  • Enforce at build time to prevent violations

The 2026 Stack:

CSharpier (formatting)
+ .NET Analyzers (built-in code quality)
+ Roslynator (comprehensive analysis)
+ EditorConfig (IDE preferences)
= Professional C# Code Quality
```text

---

## Why NOT StyleCop.Analyzers

### StyleCop.Analyzers is Obsolete (2026)

**Problems:**
- ❌ **Last stable release:** 2018 (8 years old)
- ❌ **Beta version:** Stuck at 1.2.0-beta.435 since 2016
- ❌ **Community-maintained:** Slow updates, not Microsoft-supported
- ❌ **Superseded:** Microsoft built equivalent functionality into .NET SDK
- ❌ **Outdated conventions:** Doesn't reflect modern C# patterns

**What replaced it:**
- ✅ **.NET Analyzers** (IDE* and CA* rules) - Built into .NET 5.0+ SDK
- ✅ **CSharpier** - Modern opinionated formatter
- ✅ **Roslynator** - Actively maintained (2023+), 500+ modern rules

**References:**
- [Microsoft: Code Analysis FAQ](https://learn.microsoft.com/en-us/visualstudio/code-quality/analyzers-faq?view=vs-2022)
- [C# Code Style by EditorConfig in .NET 5+ SDK](https://developers.mews.com/c-code-style-by-editorconfig-in-net-5-sdk-and-beyond/)
- [dotnet format or CSharpier?](http://coffeethinkcode.com/2023/05/05/dotnet-format-or-csharpier/)

---

## Modern Tooling Stack

### Stack Components

**1. CSharpier (Formatting - Opinionated)**
- **Purpose:** Automatic code formatting (like Prettier for JavaScript)
- **Status:** Actively maintained (2024+)
- **Philosophy:** Zero configuration, consistent formatting everywhere
- **Website:** https://csharpier.com/

**2. .NET Analyzers (Quality - Microsoft)**
- **Purpose:** Code quality and correctness (IDE* and CA* rules)
- **Status:** Built into .NET SDK 5.0+
- **Coverage:** Style (IDE*), Code Analysis (CA*), Design Guidelines
- **Docs:** https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview

**3. Roslynator (Comprehensive - Community)**
- **Purpose:** 500+ additional analyzers, refactorings, and fixes
- **Status:** Actively maintained (updated regularly)
- **Coverage:** Comprehensive modern C# patterns
- **GitHub:** https://github.com/dotnet/roslynator

**4. EditorConfig (Configuration)**
- **Purpose:** IDE preferences and .NET Analyzer severity configuration
- **Status:** Standard across all .NET IDEs
- **Scope:** Editor behavior + analyzer rule severity

---

## CSharpier - Automated Formatting

### What is CSharpier?

CSharpier is an **opinionated code formatter** for C#. It enforces a consistent style by parsing your code and re-printing it with its own rules.

**Philosophy:** Stop debating code style. Format automatically.

### Installation

**Global Tool (Recommended):**
```bash
dotnet tool install -g csharpier
```text

**Local Tool (Per-Project):**
```bash
dotnet new tool-manifest
dotnet tool install csharpier
```text

**NuGet Package (Build Integration):**
```xml
<ItemGroup>
<PackageReference Include="CSharpier.MSBuild" Version="0.27.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
```text

### Configuration

CSharpier requires **minimal configuration**. Create `.csharpierrc.json`:

```json
{
"printWidth": 120,
"useTabs": false,
"tabWidth": 4,
"endOfLine": "lf"
}
```text

**That's it.** No debates about brace style, spacing, or line breaks.

### Usage

**Format entire solution:**
```bash
dotnet csharpier .
```text

**Check formatting (CI/CD):**
```bash
dotnet csharpier . --check
```text

**Format specific files:**
```bash
dotnet csharpier src/MyFile.cs
```text

### What CSharpier Handles

CSharpier automatically enforces:
- ✅ Indentation (4 spaces)
- ✅ Line breaks and wrapping
- ✅ Brace placement (Allman style for C#)
- ✅ Spacing around operators
- ✅ Trailing commas in collections
- ✅ Consistent property/method formatting
- ✅ Array and object initializer formatting

**You never debate formatting again.**

### IDE Integration

**Visual Studio:**
- Install "CSharpier" extension
- Format on save: Tools → Options → CSharpier

**Visual Studio Code:**
- Install "CSharpier - Code formatter" extension
- Set as default formatter in settings.json

**JetBrains Rider:**
- Install "CSharpier" plugin
- Configure in Settings → Tools → CSharpier

---

## .NET Analyzers - Built-in Quality

### What are .NET Analyzers?

Built-in code analyzers that ship with the .NET SDK (5.0+). No external packages required.

**Rule Prefixes:**
- **IDE*** - Code style rules (formatting, naming, preferences)
- **CA*** - Code analysis rules (quality, design, performance, security)

### Enable in Project File

```xml
<PropertyGroup>
<!-- Enable all analyzers -->
<EnableNETAnalyzers>true</EnableNETAnalyzers>

<!-- Analysis mode -->
<AnalysisMode>AllEnabledByDefault</AnalysisMode>

<!-- Treat warnings as errors in Release -->
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>

<!-- Enforce code style in build -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
```text

### Key Rule Categories

**IDE Rules (Code Style):**
```text
IDE0001-IDE9999: Style preferences
- Naming conventions
- Use of 'var' vs explicit types
- Expression preferences (pattern matching, etc.)
- Code simplification suggestions
```text

**CA Rules (Code Analysis):**
```text
CA1000-CA1999: Design rules (inheritance, interfaces, naming)
CA2000-CA2999: Reliability rules (disposal, threading)
CA3000-CA3999: Security rules (injection, crypto)
CA5000-CA5999: Security rules (data flow analysis)
```text

### Configuration via EditorConfig

Configure rule severity in `.editorconfig`:

```ini
[*.cs]

# Naming conventions
dotnet_naming_rule.async_methods_end_in_async.severity = warning
dotnet_naming_rule.interfaces_start_with_i.severity = warning

# Code style
dotnet_diagnostic.IDE0001.severity = warning
dotnet_diagnostic.IDE0005.severity = warning # Remove unnecessary usings

# Code quality
dotnet_diagnostic.CA1001.severity = warning # Types that own disposable fields
dotnet_diagnostic.CA1031.severity = suggestion # Do not catch general exceptions
dotnet_diagnostic.CA1716.severity = warning # Identifiers should not match keywords
```text

### Enforcement

**Build-time:**
```bash
dotnet build /warnaserror
```text

**Continuous:**
```bash
dotnet build --no-incremental
```text

---

## Roslynator - Comprehensive Analysis

### What is Roslynator?

A collection of **500+ analyzers**, refactorings, and code fixes for C#. Actively maintained, modern patterns.

**GitHub:** https://github.com/dotnet/roslynator
**Documentation:** https://josefpihrt.github.io/docs/roslynator/

### Installation

Add to your project file:

```xml
<ItemGroup>
<PackageReference Include="Roslynator.Analyzers" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
```text

### Key Analyzer Categories

**Roslynator covers:**

1. **Simplification (RCS1*)**
- Simplify boolean expressions
- Remove redundant code
- Use pattern matching
- Inline variables

2. **Readability (RCS2*)**
- Improve code clarity
- Consistent formatting
- Naming improvements

3. **Performance (RCS3*)**
- Avoid allocations
- Use efficient patterns
- Optimize LINQ

4. **Design (RCS4*)**
- SOLID principles
- API design
- Type design

5. **Maintainability (RCS5*)**
- Reduce complexity
- Extract methods
- Simplify conditionals

### Example Rules

**RCS1033: Remove redundant boolean literal**
```csharp
// ❌ Before
if (condition == true)

// ✅ After
if (condition)
```text

**RCS1036: Remove unnecessary blank line**
```csharp
// ❌ Before
public class Foo
{

public void Bar()

// ✅ After (CSharpier handles this automatically)
```text

**RCS1080: Use 'Count' property instead of 'Any' method**
```csharp
// ❌ Before
if (list.Any())

// ✅ After
if (list.Count > 0)
```text

**RCS1179: Use return instead of assignment**
```csharp
// ❌ Before
bool result;
if (condition)
result = true;
else
result = false;
return result;

// ✅ After
return condition;
```text

### Configuration

Configure rule severity in `.editorconfig`:

```ini
[*.cs]

# Roslynator rules
dotnet_diagnostic.RCS1033.severity = warning
dotnet_diagnostic.RCS1036.severity = silent # CSharpier handles this
dotnet_diagnostic.RCS1080.severity = suggestion
dotnet_diagnostic.RCS1179.severity = warning
```text

### Disable Rules

If a Roslynator rule conflicts with CSharpier:

```ini
# Disable formatting-related rules (CSharpier handles these)
dotnet_diagnostic.RCS1001.severity = none
dotnet_diagnostic.RCS1003.severity = none
dotnet_diagnostic.RCS1036.severity = none
```text

---

## EditorConfig - IDE Preferences

### What is EditorConfig?

A configuration file (`.editorconfig`) that defines coding styles and preferences across editors.

**Purpose:**
- Configure .NET Analyzer rule severity
- Set IDE preferences
- Define naming conventions
- Ensure consistency across team

### Full .editorconfig Example

```ini
root = true

# All files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# C# files
[*.cs]
indent_style = space
indent_size = 4
tab_width = 4

# .NET formatting options
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# Code style
csharp_prefer_braces = true:warning
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion

# Naming conventions
dotnet_naming_rule.interfaces_start_with_i.severity = warning
dotnet_naming_rule.interfaces_start_with_i.symbols = interface_symbols
dotnet_naming_rule.interfaces_start_with_i.style = interface_style

dotnet_naming_symbols.interface_symbols.applicable_kinds = interface
dotnet_naming_style.interface_style.capitalization = pascal_case
dotnet_naming_style.interface_style.required_prefix = I

# .NET Analyzer severity
dotnet_diagnostic.CA1001.severity = warning
dotnet_diagnostic.CA1031.severity = suggestion
dotnet_diagnostic.IDE0001.severity = warning
dotnet_diagnostic.IDE0005.severity = warning

# Roslynator severity
dotnet_diagnostic.RCS1033.severity = warning
dotnet_diagnostic.RCS1080.severity = suggestion

# Disable formatting rules (CSharpier handles these)
dotnet_diagnostic.IDE0055.severity = none
dotnet_diagnostic.RCS1036.severity = none
```text

---

## Project Configuration

### Complete .csproj Setup

```xml
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>

<!-- .NET Analyzers -->
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

<!-- Treat warnings as errors in Release -->
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<!-- CSharpier -->
<PackageReference Include="CSharpier.MSBuild" Version="0.27.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

<!-- Roslynator -->
<PackageReference Include="Roslynator.Analyzers" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<!-- Configuration files -->
<AdditionalFiles Include=".editorconfig" />
</ItemGroup>

</Project>
```text

### Required Configuration Files

**Project Root Structure:**
```text
MyProject/
├── .editorconfig # .NET Analyzer + Roslynator configuration
├── .csharpierrc.json # CSharpier configuration
├── MyProject.sln
└── src/
└── MyProject.csproj
```text

**.csharpierrc.json:**
```json
{
"printWidth": 120,
"useTabs": false,
"tabWidth": 4,
"endOfLine": "lf"
}
```text

**.editorconfig:** See full example above

---

## Build Enforcement

### Build Commands

**Local Development:**
```bash
# Format code
dotnet csharpier .

# Build with analyzer checks
dotnet build

# Build treating warnings as errors
dotnet build /warnaserror
```text

**CI/CD Pipeline:**
```bash
# Check formatting (don't modify)
dotnet csharpier . --check

# Build with strict enforcement
dotnet build --configuration Release /warnaserror
```text

### GitHub Actions Example

```yaml
name: Code Quality

on: [push, pull_request]

jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore dependencies
run: dotnet restore

- name: Check formatting
run: dotnet csharpier . --check

- name: Build with analyzers
run: dotnet build --configuration Release /warnaserror

- name: Run tests
run: dotnet test --no-build --configuration Release
```text

### Pre-Commit Hook

Create `.git/hooks/pre-commit`:

```bash
#!/bin/bash

echo "Running CSharpier..."
dotnet csharpier .

if [ $? -ne 0 ]; then
echo "❌ Formatting failed. Commit aborted."
exit 1
fi

echo "Running build with analyzers..."
dotnet build /warnaserror

if [ $? -ne 0 ]; then
echo "❌ Build failed due to analyzer violations. Commit aborted."
exit 1
fi

echo "✅ All checks passed."
exit 0
```text

Make it executable:
```bash
chmod +x .git/hooks/pre-commit
```text

---

## Pre-Commit Workflow

### Engineer Workflow

**Before Committing C# Code:**

```text
STEP 1: Format code automatically
$ dotnet csharpier .

STEP 2: Build with analyzer enforcement
$ dotnet build /warnaserror

STEP 3: Run tests
$ dotnet test

STEP 4: Commit if all pass
$ git add .
$ git commit -m "Your message"
```text

**If build fails:**
```text
IF analyzer violations found THEN
1. Review the warnings/errors
2. Fix the issues (use IDE quick fixes)
3. Re-run: dotnet build /warnaserror
4. Repeat until zero violations
END IF
```text

### Reviewer Workflow

**Reviewing C# Code:**

```text
STEP 1: Verify formatting
$ dotnet csharpier . --check
Expected: "All files are formatted correctly."

STEP 2: Verify build passes
$ dotnet build /warnaserror
Expected: "Build succeeded. 0 Warning(s) 0 Error(s)"

STEP 3: Review code quality
- Check for logical issues
- Verify tests comprehensive
- Assess architecture decisions

STEP 4: Verdict
IF formatting check fails THEN
REJECT: "Code not formatted. Run: dotnet csharpier ."
ELSE IF build has violations THEN
REJECT: "Analyzer violations present. Fix and resubmit."
ELSE IF code quality issues THEN
REQUEST CHANGES: [List issues]
ELSE
APPROVE
END IF
```text

---

## Comparison: Old vs New

### StyleCop.Analyzers (Old - 2018)

```xml
<!-- ❌ OLD WAY (Don't use) -->
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
```text

**Problems:**
- Last updated 2018
- Beta version stuck since 2016
- Manual configuration required
- Outdated conventions
- Not Microsoft-supported

### Modern Stack (New - 2026)

```xml
<!-- ✅ NEW WAY (Use this) -->
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CSharpier.MSBuild" Version="0.27.0" />
<PackageReference Include="Roslynator.Analyzers" Version="4.12.0" />
</ItemGroup>
```text

**Benefits:**
- ✅ Actively maintained (2024+)
- ✅ Microsoft-supported foundation
- ✅ Zero-config formatting
- ✅ 500+ modern analyzers
- ✅ Build-time enforcement
- ✅ IDE integration everywhere

---

## Summary

### MANDATORY Requirements

All C# projects MUST:

1. ✅ **Use CSharpier** for automatic formatting
- Format before every commit: `dotnet csharpier .`

2. ✅ **Enable .NET Analyzers** in project file
- `EnableNETAnalyzers=true`
- `EnforceCodeStyleInBuild=true`

3. ✅ **Install Roslynator** for comprehensive analysis
- Add Roslynator.Analyzers NuGet package

4. ✅ **Configure EditorConfig** for rule severity
- Create `.editorconfig` with team standards

5. ✅ **Enforce at build time**
- CI/CD must run: `dotnet build /warnaserror`
- Local commits: pre-commit hook

### Zero Tolerance

**Build MUST pass with:**
- ✅ Zero formatting violations (CSharpier)
- ✅ Zero .NET Analyzer warnings
- ✅ Zero Roslynator warnings (configured severity)
- ✅ All tests passing

**Reviewers MUST block if:**
- ❌ Formatting check fails
- ❌ Build has analyzer violations
- ❌ Code quality issues present

---

## References

**Official Documentation:**
- [Microsoft: .NET Code Analysis](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview)
- [Microsoft: Code Quality Analyzers FAQ](https://learn.microsoft.com/en-us/visualstudio/code-quality/analyzers-faq?view=vs-2022)
- [CSharpier Official Site](https://csharpier.com/)
- [Roslynator GitHub](https://github.com/dotnet/roslynator)
- [Roslynator Documentation](https://josefpihrt.github.io/docs/roslynator/)

**Articles:**
- [C# Code Style by EditorConfig in .NET 5+ SDK](https://developers.mews.com/c-code-style-by-editorconfig-in-net-5-sdk-and-beyond/)
- [dotnet format or CSharpier?](http://coffeethinkcode.com/2023/05/05/dotnet-format-or-csharpier/)
- [Roslynator Analyzers 2.3.1](https://www.infoq.com/news/2020/01/roslynator-analyzers-231/)

---

**Last Updated:** 2026-01-09
**Status:** ACTIVE - Modern tooling standard for all C# projects