Using a hierarchical .editorconfig for formatting, code style and analyser severities that every IDE honours, and enforcing it with EnforceCodeStyleInBuild and dotnet format in CI.
I commit an .editorconfig at the repo root with root = true. It holds formatting (indentation, new lines), .NET code style preferences such as file-scoped namespaces, and severities for analyser and code style rules via dotnet_diagnostic.<id>.severity. Visual Studio, VS Code and Rider all read it, so style no longer depends on the editor. To enforce it, I set EnforceCodeStyleInBuild so IDE rules run during dotnet build, and run dotnet format --verify-no-changes in CI.
"I commit an
.editorconfigat the repo root withroot = true, which stops tools searching parent folders. It's a plain-text, hierarchical file that Visual Studio, VS Code, Rider anddotnet formatall read, so style stops depending on which editor someone uses. It holds formatting such as indentation and new lines, code style preferences such as file-scoped namespaces, and severities throughdotnet_diagnostic.<id>.severity, from none and silent up to suggestion, warning and error.Because it's hierarchical, a closer file overrides a further one, so a tests folder can relax a rule like underscores in test names without touching production rules. Enforcement is where people get caught out: analyser rules run in the build by default, but the IDE code style rules only run in the build when
EnforceCodeStyleInBuildis true, which I set inDirectory.Build.props, and that difference between IDE and CA rules is worth knowing well. Then CI runsdotnet format --verify-no-changes, which fails if any file would be reformatted.On an existing codebase I roll it out in one dedicated formatting commit and add that commit to
.git-blame-ignore-revs. The mistake I see is turning hundreds of rules into warnings withTreatWarningsAsErrorson day one, so I raise severities in stages, bug-catching rules first. It's one part of making the whole developer environment match CI.Style consistency is a tooling problem, not a review problem, and moving it into the build lets reviewers spend their time on design and correctness."
Each IDE has its own formatter settings, so a mixed team without a shared file ends up with pull requests full of whitespace churn. .editorconfig is an open format that all three IDEs and the .NET SDK understand, and it lives in the repository, so it is reviewed and versioned like code.
indent_style, indent_size, insert_final_newline, charset, end_of_line.csharp_new_line_before_open_brace, dotnet_sort_system_directives_first.csharp_style_namespace_declarations = file_scoped:warning.dotnet_diagnostic.IDE0005.severity = warning (unnecessary using), dotnet_diagnostic.CA1062.severity = none.Tools look for .editorconfig in the file's folder and every parent folder until one has root = true. Closer files override further ones, and within a file later matching sections override earlier ones. That lets a tests/ folder relax a rule (for example underscores in test method names) without changing production code rules.
warning and error severities show in the Error List and Problems panel.CAxxxx) run by default, but code style rules (IDExxxx) run only when EnforceCodeStyleInBuild is true. Put it in Directory.Build.props.IDE0005 (remove unnecessary usings) is only reported during build when GenerateDocumentationFile is also true.dotnet format --verify-no-changes fails if any file would be reformatted.A team of Visual Studio and Rider users had constant brace and using order changes in reviews. We exported the agreed settings to .editorconfig, ran dotnet format once in a dedicated commit, added that commit to .git-blame-ignore-revs, and added dotnet format --verify-no-changes to the pull request pipeline. Review comments about style dropped to almost zero.
The interviewer is checking whether you can make a multi-IDE team consistent without arguments in code review.
Green flags
root = true.EnforceCodeStyleInBuild and dotnet format --verify-no-changes.Red flags
.editorconfig with IDE-specific settings files.repo/
+-- .editorconfig root = true; team-wide rules
+-- src/
| +-- Orders.Api/Program.cs <- uses repo rules
+-- tests/
+-- .editorconfig overrides for tests only
+-- Orders.Tests/OrderTests.cs <- repo rules + tests overrides
Lookup for OrderTests.cs:
tests/Orders.Tests/ -> tests/.editorconfig -> repo/.editorconfig (root = true, stop)
nearest file wins on conflicts; within a file, the last matching section wins
| Kind | Example | Who reads it |
|---|---|---|
| Core EditorConfig properties | indent_style = space, insert_final_newline = true, end_of_line = lf |
Every EditorConfig-aware editor, for every file type |
| .NET formatting options | csharp_new_line_before_open_brace = all, dotnet_sort_system_directives_first = true |
Roslyn formatter, dotnet format, Rider |
| .NET code style preferences | csharp_style_namespace_declarations = file_scoped:warning |
Roslyn IDE analysers (IDExxxx) |
| Rule severities | dotnet_diagnostic.CA2007.severity = none |
Every Roslyn analyser, in the IDE and in the build |
Rider also reads resharper_* keys for its own formatter and inspections.
Sections use globs: [*], [*.cs], [*.{csproj,props,targets}], [src/Legacy/**.cs]. A path in a section is relative to the folder holding that .editorconfig. Later sections override earlier ones, so put general rules first and exceptions after them.
| Severity | In the editor | In the build |
|---|---|---|
none |
Off | Off |
silent |
Fix offered, nothing shown | Nothing |
suggestion |
Grey dots | Nothing |
warning |
Green squiggle, Error List warning | Build warning |
error |
Red squiggle | Build error |
default resets to the rule's own default. With TreatWarningsAsErrors, every warning fails the build.
The .NET SDK passes the .editorconfig files that apply to each source file to the Roslyn compiler as analyser configuration. Analysers read dotnet_diagnostic.* severities and option values at compile time, which is why the same file affects the IDE (live analysis) and the build (csc).
Code style rules (IDExxxx) are special: they only run during the build when EnforceCodeStyleInBuild is true. IDE0005 (unnecessary using) additionally needs GenerateDocumentationFile=true to be reported in the build.
.editorconfig |
.globalconfig |
|
|---|---|---|
| Applies to | Files under its folder, by glob | Every file in the project, regardless of path |
| Found by | Walking up from each file | Being in the project folder or a parent, or being added as GlobalAnalyzerConfigFiles (for example from a NuGet package) |
| Best for | Repo style and folder-specific overrides | Analyser severities shipped in a shared package |
| Precedence | Wins over .globalconfig for the same key |
Lower; use global_level to order several |
.editorconfig with root = true.dotnet format once in a dedicated commit..git-blame-ignore-revs and run git config blame.ignoreRevsFile .git-blame-ignore-revs.EnforceCodeStyleInBuild and add dotnet format --verify-no-changes to CI.| Command | Fixes |
|---|---|
dotnet format whitespace |
Indentation, spacing, new lines (no build needed) |
dotnet format style |
Code style rules (IDExxxx) |
dotnet format analyzers |
Fixable analyser diagnostics (CAxxxx and third-party) |
dotnet format |
All three |
--verify-no-changes |
Fails with a non-zero exit code instead of changing files |
--severity warn |
Only fixes diagnostics at or above that severity |
Turning every rule to warning with EnforceCodeStyleInBuild adds some build time and can overwhelm a legacy codebase. Introduce severities gradually, starting with rules that catch bugs.
.editorconfig from the code example to a project with block-scoped namespaces.IDE0161 (convert to file-scoped namespace).dotnet build and note no warning; add EnforceCodeStyleInBuild and build again.tests/.editorconfig that sets CA1707 to none and confirm only test files are affected.dotnet format, then dotnet format --verify-no-changes and check the exit code is 0..editorconfig at the repo root:
root = true
[*]
indent_style = space
insert_final_newline = true
charset = utf-8
[*.{csproj,props,targets,json,yml}]
indent_size = 2
[*.cs]
indent_size = 4
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_var_for_built_in_types = true:suggestion
dotnet_sort_system_directives_first = true
# Unnecessary using directive
dotnet_diagnostic.IDE0005.severity = warning
# ConfigureAwait is not needed in ASP.NET Core application code
dotnet_diagnostic.CA2007.severity = none
tests/.editorconfig (overrides for test projects):
[*.cs]
# Allow underscores in test method names such as Submit_order_with_no_lines_fails
dotnet_diagnostic.CA1707.severity = none
Directory.Build.props to enforce code style in every build:
<Project>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Do not fail the build for missing XML comments on public members -->
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>
CI step:
dotnet format --verify-no-changes
Mistake: expecting IDE code style warnings in CI without EnforceCodeStyleInBuild.
IDExxxx rules only run in the editor by default, so CI passes code the IDE flags.
Instead: set <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> in Directory.Build.props, and also GenerateDocumentationFile if you want IDE0005 in the build.
Mistake: forgetting root = true.
Tools keep searching parent folders, so a stray .editorconfig higher up (or in the user's home folder) changes behaviour on one machine.
Instead: put root = true at the top of the repo-root .editorconfig.
Mistake: writing inline comments after values.
EditorConfig comments must be on their own line; indent_size = 4 # four spaces makes the value invalid and the setting is ignored.
Instead: put comments on the line above the setting, starting with # or ;.
Mistake: turning on hundreds of warnings at once with TreatWarningsAsErrors.
A legacy codebase suddenly fails to build and everyone is blocked.
Instead: raise severities in stages, fix one rule family at a time, and turn on warnings-as-errors once the count is zero.
Mistake: reformatting the whole repo inside a feature pull request.
The real change is buried in thousands of lines of noise, and git blame points at the wrong person.
Instead: reformat in one dedicated commit, and add its hash to .git-blame-ignore-revs.
Mistake: keeping style only in personal IDE settings.
Nobody else has them, so formatting churns between developers and IDEs.
Instead: put every team rule in the committed .editorconfig, which Visual Studio, VS Code, Rider and dotnet format all read.
Mistake: putting analyser severities for a shared package in .editorconfig files copied between repos.
The copies drift and every repo ends up with different rules.
Instead: ship severities as a .globalconfig in an internal NuGet package (or a shared Directory.Build.props), and keep .editorconfig for repo-specific overrides.
Style consistency is a tooling problem, not a review problem. A senior engineer moves style out of human review into .editorconfig plus CI, so reviewers spend time on design and correctness. The order of rollout matters: agree the rules, apply them in one mechanical commit, exclude that commit from blame, then enforce in CI. Prioritise rules that catch bugs (nullable, CA2000-style resource rules, security analysers) over cosmetic ones, and make severities a team decision recorded in the file itself.
I standardise below the IDE rather than forcing one IDE. The CLI and CI are the source of truth. I pin the SDK with global.json, put style and analyser severities in .editorconfig, centralise build properties in Directory.Build.props and versions with Central Package Management, and commit shared IDE files such as .vsconfig, .vscode/extensions.json and launchSettings.json while ignoring personal state. I offer a Dev Container or a setup script for onboarding, keep secrets in user secrets and Key Vault, and CI runs the same dotnet format, build and test commands developers run locally.
Analysers are plug-ins for the Roslyn compiler. They report diagnostics in the editor and during the build. CA rules (CA1xxx to CA5xxx) are the SDK's code quality analysers, covering design, performance, reliability and security. The .NET analysers are enabled by default for net5.0+ projects (EnableNETAnalyzers), but only a default subset of rules reports as warnings; AnalysisLevel and AnalysisMode set how many apply. IDE rules (IDE0xxx) are code style rules, such as var usage or file-scoped namespaces. They show in the editor but only run in the build when EnforceCodeStyleInBuild is true, which is why style issues often pass CI. Severities for both are set in .editorconfig (or a .globalconfig) with dotnet_diagnostic.<id>.severity. Third-party analysers come in as NuGet packages. For a team, I put the levels in Directory.Build.props, the severities in one root config file, and tighten them step by step.
Last updated 1 Nov 2026.