📁 Sangam Dhakal - Portfolio v2.0 - Netscape Navigator 4.0
_
□
×

📚 Interview question

What analysers run during a .NET build, how do IDE code style rules differ from CA code quality rules, and how do you configure them across a team?

The .NET analyser ecosystem: built-in CA code quality analysers and IDE code style analysers, AnalysisLevel and AnalysisMode, EnforceCodeStyleInBuild, .editorconfig severities, third-party analyser packages, suppressions, and rolling stricter rules out to a team.

Mid-Level Theory Intermediate analyzers build-system code-analysis code-quality editorconfig

🎯 Key points

  • Roslyn analysers run inside the compiler and report diagnostics in the IDE and the build
  • CAxxxx rules are code quality analysers shipped in the .NET SDK; enabled by default for net5.0+ projects, with only a default subset raised as warnings
  • IDExxxx rules are code style analysers; they run in the IDE, and in the build only with EnforceCodeStyleInBuild=true
  • AnalysisLevel picks the rule set version (latest, 10.0) and AnalysisMode its breadth (None, Default, Minimum, Recommended, All)
  • Severities are set per rule in .editorconfig or a .globalconfig with dotnet_diagnostic.<ID>.severity
  • Third-party analysers (StyleCop, Roslynator, Meziantou, security analysers) come as PackageReferences with PrivateAssets=all
  • Suppress narrowly: [SuppressMessage] with a justification or a scoped .editorconfig section, not blanket NoWarn

⏱ 30-second answer

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.

🗣 Full answer (senior)

How I would answer this in an interview

"Analysers are plug-ins that run inside the Roslyn compiler, so they report diagnostics both in the editor and during the build. The SDK ships two families. CA rules are code quality analysers covering design, performance, reliability and security; they're on by default for net5.0 and later, but only a default subset reports as warnings. AnalysisLevel picks the rule set version, like latest or 10.0, and AnalysisMode its breadth, from None through Recommended to All.

IDE rules are code style rules, like file-scoped namespaces or var usage. They run in the editor, but in the build only when EnforceCodeStyleInBuild is true, so the mistake I see is a team expecting them to fail CI when they never run there. Severities for both families go in .editorconfig or a .globalconfig with dotnet_diagnostic.<ID>.severity, which every editor honours. Third-party analysers such as StyleCop, Roslynator or Meziantou come in as package references with PrivateAssets="all".

For a team I put the levels in Directory.Build.props, pin AnalysisLevel if warnings are errors, and keep severities in one root config. I suppress narrowly, with [SuppressMessage] and a justification or a scoped config section rather than a blanket NoWarn. New rules roll out as suggestion, then warning, then error, with dotnet format doing the mechanical fixes in CI and security rules at error, so the warning list stays something people trust."


Families of analysers

Family IDs Comes from Runs in build by default?
Compiler warnings CS0xxx, CS8xxx (nullable) The C# compiler Yes
Code quality CA1xxx–CA5xxx .NET SDK (Microsoft.CodeAnalysis.NetAnalyzers) Yes, for the enabled set
Code style IDE0xxx .NET SDK code style analysers No, only with EnforceCodeStyleInBuild=true
Third-party e.g. SA (StyleCop), RCS (Roslynator), MA (Meziantou) NuGet packages Yes

CA categories include Design, Globalization, Performance (CA18xx, such as CA1848 for high-performance logging), Reliability (CA2xxx, such as CA2007 ConfigureAwait), Security (CA2100 SQL injection, CA5xxx crypto rules) and Usage.

Controlling CA rules

<PropertyGroup>
  <AnalysisLevel>latest</AnalysisLevel>      <!-- or a version such as 10.0 -->
  <AnalysisMode>Recommended</AnalysisMode>   <!-- None, Default, Minimum, Recommended, All -->
</PropertyGroup>

You can combine them as <AnalysisLevel>latest-recommended</AnalysisLevel>. Pinning a version (10.0) means SDK upgrades don't silently add new warnings; latest picks them up automatically. Per-category modes exist too, such as AnalysisModeSecurity.

Controlling IDE rules

Code style preferences live in .editorconfig (csharp_style_namespace_declarations = file_scoped:warning). To make them fail the build, set <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> and give the rules a warning or error severity.

Severities and suppressions

[*.cs]
dotnet_diagnostic.CA2007.severity = none        # ASP.NET Core apps have no sync context
dotnet_diagnostic.CA1848.severity = warning     # use LoggerMessage / source-generated logging
dotnet_diagnostic.IDE0161.severity = warning    # file-scoped namespaces

[tests/**.cs]
dotnet_diagnostic.CA1707.severity = none        # underscores in test names

For a single justified exception, use [SuppressMessage("Reliability", "CA2000", Justification = "Ownership transferred to caller")] or #pragma warning disable with a comment. Avoid project-wide NoWarn for anything but well-understood rules.

Production example

A team's CI passed while developers' IDEs were full of style suggestions, because EnforceCodeStyleInBuild was off. Turning it on with .editorconfig severities at suggestion first, then warning for a curated set, and running dotnet format once to fix existing code, made the IDE and CI agree. Enabling the security CA rules at warning also caught two string-concatenated SQL commands (CA2100) in a legacy repository.

Best practices, performance and security

  • Keep analyser configuration in the repo: levels in Directory.Build.props, severities in .editorconfig.
  • Pin AnalysisLevel to a version if you treat warnings as errors, and upgrade it deliberately.
  • Analysers cost build time; enormous rule sets on huge solutions can add minutes. Measure with a binary log, and consider running heavy third-party analysers only in CI.
  • Turn on the security rules: they're cheap insurance.

🔍 What the interviewer is checking

Checks practical knowledge of code analysis in .NET.

Green flags: distinguishes CA and IDE rules; knows AnalysisLevel/AnalysisMode and EnforceCodeStyleInBuild; configures severities in .editorconfig; suppresses narrowly; rolls rules out gradually.

Red flags: thinks analysers are an IDE-only feature; uses blanket NoWarn; enables everything at once.

📚 Learn in depth

1. Where analysers run

                     Roslyn compilation (CoreCompile -> Csc)
   source + references + analyser assemblies (SDK + NuGet analyzers/ folders)
                              |
            +-----------------+------------------+
            |                                    |
      Compiler diagnostics                 Analyser diagnostics
      CS0xxx, CS8xxx (nullable)            CA   (SDK code quality, NetAnalyzers)
                                           IDE  (code style) -- build only if EnforceCodeStyleInBuild
                                           SA / RCS / MA / RS (packages)
            |                                    |
            +---------------+--------------------+
                            v
   severity from: .editorconfig / .globalconfig  >  AnalysisLevel + AnalysisMode defaults
   then:          NoWarn (drop)   WarningsAsErrors / TreatWarningsAsErrors (promote)
                            |
          IDE: squiggles + quick fixes        Build: warnings or errors in the output

Analysers are .NET assemblies loaded into the compiler. The IDE runs them live on open files (and optionally the whole solution in the background); the build runs them inside CoreCompile. Source generators are loaded the same way.

2. The families

Family IDs Shipped in In the build by default Configured by
Compiler CS0xxx, CS8xxx The C# compiler Yes Nullable, WarningLevel, .editorconfig
Code quality CA1xxx to CA5xxx The .NET SDK (EnableNETAnalyzers, on by default for net5.0+) Yes, for the rules enabled by the level and mode AnalysisLevel, AnalysisMode, .editorconfig
Code style IDE0xxx The .NET SDK No: only with EnforceCodeStyleInBuild=true .editorconfig style options and severities
Third party SA (StyleCop), RCS (Roslynator), MA (Meziantou), RS0030 (banned APIs) and others NuGet packages Yes Package docs, .editorconfig

The SDK's CA analysers are versioned with the SDK. To get newer rules without a new SDK, reference the Microsoft.CodeAnalysis.NetAnalyzers package explicitly.

CA categories worth knowing

Category Range or examples Catches
Design CA1000 to CA1070 API shape problems in libraries
Globalization CA1304, CA1305, CA1307, CA1309 Culture-sensitive parsing and comparison (ToUpper(), string.Format without culture)
Performance CA18xx: CA1822, CA1848, CA1859, CA1860 Static candidates, high-performance logging, avoidable allocations
Reliability CA2000, CA2007, CA2016 Undisposed objects, ConfigureAwait, not forwarding CancellationToken
Security CA2100, CA3xxx, CA5xxx SQL injection, taint flow, weak crypto (CA5350, CA5351), insecure deserialisation
Usage CA2200, CA2201, CA2211 Rethrowing incorrectly, reserved exception types

3. AnalysisLevel and AnalysisMode

<PropertyGroup>
  <AnalysisLevel>10.0</AnalysisLevel>          <!-- which version of the rule set -->
  <AnalysisMode>Recommended</AnalysisMode>     <!-- how many rules are on as warnings -->
  <!-- or combined: <AnalysisLevel>10.0-recommended</AnalysisLevel> -->
  <AnalysisModeSecurity>All</AnalysisModeSecurity>   <!-- per-category override -->
</PropertyGroup>
Setting Values Notes
AnalysisLevel A version (8.0, 9.0, 10.0), latest, preview, none A pinned version stays stable when the SDK changes; latest picks up new rules with each SDK
AnalysisMode None, Default, Minimum, Recommended, All How many rules are raised as warnings; All is very noisy in older code
AnalysisMode<Category> Same values Per category: Security, Performance, Reliability, Design, Usage, Naming, Globalization and others
CodeAnalysisTreatWarningsAsErrors true/false Lets CA warnings stay warnings even when TreatWarningsAsErrors is on, useful during rollout

4. Severities in .editorconfig

Severity IDE Build
error Red squiggle Fails the build
warning Green squiggle Warning (error with TreatWarningsAsErrors)
suggestion Dots under the code Not reported
silent Quick fix only Not reported
none Off Off

Three levels of precision, most specific wins:

[*.cs]
dotnet_analyzer_diagnostic.severity = suggestion                 # every analyser rule
dotnet_analyzer_diagnostic.category-Security.severity = error     # a whole category
dotnet_diagnostic.CA2007.severity = none                          # one rule

Code style rules have two parts: the preference and the severity.

csharp_style_namespace_declarations = file_scoped:warning   # preference + severity shorthand
dotnet_diagnostic.IDE0161.severity = warning                # explicit severity (wins in the build)

Set the dotnet_diagnostic severity explicitly for rules you want in the build; the :warning shorthand isn't always honoured by the command-line build.

The IDE0005 trap

"Remove unnecessary usings" (IDE0005) only reports in the build when GenerateDocumentationFile is true, because the compiler needs documentation-comment analysis to know a using is unused. Teams often enable the rule and wonder why CI never reports it.

5. Sharing configuration across a team

Mechanism Scope Best for
Root .editorconfig with root = true Files under that folder, by path pattern Style preferences and per-folder severities (for example relaxed rules for tests)
.globalconfig (is_global = true) Every file in the projects that include it Organisation-wide rule sets; can be shipped in a NuGet package
Directory.Build.props Every project AnalysisLevel, AnalysisMode, EnforceCodeStyleInBuild, GlobalAnalyzerConfigFiles items
Directory.Packages.props GlobalPackageReference Every project, as a private asset Third-party analysers

A shared package (for example Contoso.CodeStandards) that contains a .globalconfig, analyser references and a .props file lets many repositories adopt the same baseline and upgrade it with one version bump.

6. Suppressing narrowly

Technique Scope Use when
[SuppressMessage("Category", "CA2000:...", Justification = "...")] One member or type A reviewed, justified exception
GlobalSuppressions.cs with Target = "~M:..." A specific symbol, from one file Generated by the IDE; keep justifications
#pragma warning disable CA2000 // reason ... restore A few lines Local, temporary exceptions
.editorconfig section [Generated/**.cs] A folder or pattern Generated code, tests, samples
NoWarn The whole project Rarely; only for rules the team has decided don't apply

Always restore a #pragma, always add a justification, and review suppressions like code.

7. Rolling out stricter rules

 1. pick the rules (security and reliability first, then performance, then style)
 2. set them to suggestion -> see the count, nobody's build breaks
 3. fix mechanically where possible: dotnet format analyzers --diagnostics CA1822 IDE0161
 4. raise to warning; fix the rest in small PRs per area
 5. raise to error for rules that catch real bugs
 6. upgrade AnalysisLevel in its own PR on each SDK upgrade, repeat

Track the warning count per project over time. A number that only goes down is the best evidence the rollout is working.

8. Performance

  • Analysers run inside every compilation. Large third-party packs on big solutions can add minutes.
  • Build with -p:ReportAnalyzer=true -bl and open the binary log: it shows the time each analyser took.
  • RunAnalyzersDuringBuild=false (or -p:RunAnalyzers=false) speeds up a quick local build, as long as CI still runs them.
  • In the IDE, limit background analysis to open documents on very large solutions.

9. Security

  • Turn on the security category (AnalysisModeSecurity=All) and make it an error on main. Rules such as CA2100 (SQL injection), CA3001 to CA3012 (taint analysis for injection) and CA5350/CA5351 (weak hashing and encryption) are cheap insurance.
  • Microsoft.CodeAnalysis.BannedApiAnalyzers with a BannedSymbols.txt can ban dangerous APIs, such as BinaryFormatter or direct HttpClient construction, across the codebase.
  • Analyser packages run code inside your build. Take them from trusted publishers and pin their versions like any other dependency.

10. .NET 11 awareness

The .NET 11 SDK will ship a new rule set (AnalysisLevel 11.0) with new CA rules. With a pinned level, upgrading the SDK doesn't change your warnings; raise the level in a separate PR once you've reviewed the new rules. Keep production on the .NET 10 SDK until then.

11. Try it yourself

  1. Create a console app with a block-scoped namespace and a string.Format without a culture, then build and note the warnings.
  2. Add <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> and dotnet_diagnostic.IDE0161.severity = warning, and build again.
  3. Add an unused using and enable IDE0005 at warning; build with and without GenerateDocumentationFile.
  4. Set <AnalysisLevel>10.0-all</AnalysisLevel> and count the new warnings, then go back to recommended.
  5. Add dotnet_analyzer_diagnostic.category-Security.severity = error and write a SqlCommand with string concatenation to trigger CA2100.
  6. Relax CA1707 for a [tests/**.cs] section and confirm it only applies to tests.
  7. Build with -p:ReportAnalyzer=true -bl and find the slowest analyser in the binary log.

💻 Code

<!-- Directory.Build.props -->
<Project>
  <PropertyGroup>
    <AnalysisLevel>10.0-recommended</AnalysisLevel>
    <AnalysisModeSecurity>All</AnalysisModeSecurity>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
  </PropertyGroup>
</Project>
<!-- Directory.Packages.props: a third-party analyser for every project (private to each project) -->
<ItemGroup>
  <GlobalPackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
</ItemGroup>
# .editorconfig
root = true

[*.cs]
csharp_style_namespace_declarations = file_scoped:warning
dotnet_diagnostic.IDE0161.severity = warning
dotnet_diagnostic.CA1848.severity = warning
dotnet_diagnostic.CA2100.severity = error
dotnet_diagnostic.CA2007.severity = none

[tests/**.cs]
dotnet_diagnostic.CA1707.severity = none
using System.Diagnostics.CodeAnalysis;

namespace Orders.Data;

public sealed class ConnectionFactory
{
    [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope",
        Justification = "The caller owns and disposes the returned connection.")]
    public SqlConnection Create(string connectionString) => new(connectionString);
}

The last example assumes a reference to Microsoft.Data.SqlClient and using Microsoft.Data.SqlClient;.

⚠ Common mistakes

  • Mistake: expecting IDE style rules to fail CI. IDE0xxx rules run in the editor but not in the command-line build unless EnforceCodeStyleInBuild is true, so style problems pass CI while developers see them locally. Instead: set EnforceCodeStyleInBuild in Directory.Build.props, give the agreed rules an explicit dotnet_diagnostic.<ID>.severity, and run dotnet format --verify-no-changes in CI.

  • Mistake: combining AnalysisLevel=latest with TreatWarningsAsErrors. A new SDK can add rules to the enabled set, so the first build on the new SDK fails on code nobody changed. Instead: pin AnalysisLevel to a version (for example 10.0-recommended) and raise it in its own pull request after reviewing the new rules.

  • Mistake: turning on AnalysisMode=All in a legacy codebase. Thousands of warnings appear at once, nobody can triage them, and the team responds with blanket suppressions. Instead: start with Recommended, raise categories one at a time (security and reliability first), and roll new rules out as suggestion, then warning, then error.

  • Mistake: using blanket NoWarn lists. Project-wide suppressions hide real problems in new code as well as old, and nobody remembers why each entry was added. Instead: suppress narrowly with [SuppressMessage] and a justification, a restored #pragma, or a scoped .editorconfig section, and review suppressions like code.

  • Mistake: scattered .editorconfig files without root = true. Files in different folders get different severities, and a stray file above the repo can change the rules for everyone. Instead: keep one root .editorconfig with root = true (or a shared .globalconfig), and allow folder overrides only for agreed cases such as tests or generated code.

  • Mistake: enabling IDE0005 and expecting the build to report unused usings. The rule only reports in the build when GenerateDocumentationFile is true, so CI stays silent. Instead: set GenerateDocumentationFile to true (and suppress CS1591 if you don't want missing-comment warnings) when you enforce IDE0005.

  • Mistake: adding analyser packages to libraries without PrivateAssets="all". The analyser dependency flows to every consumer, who then inherits rules and version conflicts they never asked for. Instead: reference analysers with PrivateAssets="all", or use GlobalPackageReference under Central Package Management.

  • Mistake: ignoring the security rules. SQL injection (CA2100), weak cryptography (CA5350, CA5351) and insecure deserialisation slip through review because the rules are off or only suggestions. Instead: set AnalysisModeSecurity=All, make the security category an error on main, and ban dangerous APIs with the banned-API analyser.

  • Mistake: running heavy analysers without measuring their cost. Build times grow by minutes on large solutions and developers start disabling analysers locally. Instead: measure with -p:ReportAnalyzer=true -bl, drop or scope slow packages, and allow quick local builds with RunAnalyzers=false as long as CI always runs them.

  • Mistake: treating analyser findings as all-or-nothing debt. Either every warning must be fixed before anything ships, or none ever are. Instead: fix bug-finding rules (security, reliability, nullable) everywhere, apply style rules to new and touched code, and track the remaining count per project so it goes down over time.

🏛 Senior insight: trade-offs

Analysers are the cheapest code reviewer you'll ever have, but only if their output is trusted. A senior engineer curates the rule set rather than turning everything on: a pinned AnalysisLevel, recommended mode, security rules at error, a short list of style rules that the team agreed on, and EnforceCodeStyleInBuild so the IDE and CI agree. New rules roll out as suggestion, then warning, then error, with dotnet format doing the mechanical fixes. Suppressions carry justifications and are reviewed, so the warning list stays meaningful.

At scale (many teams, many repositories) the configuration itself becomes a product: a shared .globalconfig and analyser GlobalPackageReferences distributed through a common Directory.Build.props or an internal NuGet package, versioned and upgraded deliberately. That gives every repo the same baseline while letting a team document an exception in its own config.

Debt versus purity. Not every rule is worth fixing in old code. Seniors fix rules that catch bugs (security, reliability, nullable) everywhere, apply style rules to new and touched code only, and track the remaining warning count per project so debt goes down rather than being hidden.

🔗 Follow-ups the interviewer may ask

  1. When: Candidate mentions TreatWarningsAsErrors or NoWarn. Why: Moves from configuring analysers to the warning policy that enforces them.
    How to answer

    The file under review turns on TreatWarningsAsErrors and then hides 25 warnings with NoWarn. That list includes nullable warnings, obsolete-API warnings and a security rule, so the policy looks strict while silencing the warnings that matter most. I'd remove those suppressions. Rules that genuinely don't apply would get a severity in .editorconfig, scoped to the right folders and commented. Rules we can't fix yet would go in WarningsNotAsErrors, so they stay visible as warnings. I'd also pin AnalysisLevel so an SDK upgrade can't break the build overnight, apply the same settings locally and in CI, and ratchet a legacy codebase towards zero warnings rather than flipping everything at once.

  2. When: Candidate configures severities in .editorconfig. Why: Checks .editorconfig mechanics across editors.
    How to answer

    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.

  3. When: Candidate mentions dotnet format or fixing violations in bulk. Why: Checks enforcement in CI with dotnet format.
    How to answer

    The pipeline restores three times, builds in Debug, runs dotnet format in a way that silently fixes files instead of failing, and publishes from a different build than the one it tested. I'd use global.json for the SDK, restore once with --locked-mode, build once in Release with --no-restore, then test and publish with --no-build. I'd run dotnet format --verify-no-changes so formatting drift fails the build, collect test results and coverage, and cache the NuGet folder.

🛠 Real-world problems: how you handle them

Style rules visible in the IDE but never enforced in CI
Situation
A 40-project .NET 10 solution with an .editorconfig that set dozens of style preferences. Developers saw suggestions in Visual Studio and Rider, but CI never failed on them, and code style drifted between teams.
Signals
Pull requests with mixed namespace styles and var usage; reviewers leaving style comments by hand; dotnet build output with no IDE diagnostics at all.
Investigation
Checked the build: EnforceCodeStyleInBuild was not set, so IDE rules didn't run in the build. Ran dotnet format --verify-no-changes and found over 3,000 violations. Also found AnalysisLevel unset and security CA rules at their defaults.
Resolution
Enabled EnforceCodeStyleInBuild, curated a set of 15 style rules at warning, ran dotnet format once in a dedicated PR, pinned AnalysisLevel to 10.0-recommended, and set AnalysisModeSecurity to All, which surfaced two CA2100 SQL injection risks that were fixed.
Prevention
dotnet format --verify-no-changes in CI, analyser settings centralised in Directory.Build.props and .editorconfig under CODEOWNERS, and a quarterly review of new rules when the SDK is upgraded.

How to tell it (STAR)

**Situation:** In our [40-project] solution at [company], the IDE showed style suggestions everywhere but CI never enforced them, so reviewers spent time on style comments. **Task:** I wanted CI and the IDE to agree, and to use analysers for more than style. **Action:** I found EnforceCodeStyleInBuild was off, agreed a short list of rules with the team, fixed [3,000] violations in one mechanical PR with dotnet format, pinned the analysis level, and turned on the security rules. **Result:** Style comments in reviews dropped [noticeably], and the security rules caught [two] SQL injection risks before they reached production.

↩ Usually asked after

More in Build System

Last updated 1 Nov 2026.

🌐 Sangam's Webring

⏪ Previous 🏠 Main Next ⏩
💬 Chat with me