The #region Debate: When Code Folding Helps and When It Hides Problems

A balanced look at #region in C# — where it genuinely helps navigate large files, and where it just hides code that needs refactoring.

Few features in C# generate as much disagreement among developers as #region. It’s a preprocessor directive that marks a block of code an editor can collapse or expand — introduced back in Visual Studio .NET 2003 as a way to organize large files by grouping related code under a label. Used well, it improves navigation in a big file. Used as a crutch, it becomes a way to avoid fixing the actual problem: a file or method that’s grown too large.

The case for #region

Code organization. Collapsing and expanding sections makes large files easier to navigate, especially with long methods or classes.

Readability, when grouping is logical. Grouping properties, methods, and constructors under clear region names gives large classes structure at a glance.

Navigation and focus. Folding away everything except the section you’re working on lets you concentrate on what matters right now, without the rest of the file competing for attention.

Team consistency. If a team has an established convention — a region for fields, one for constructors, one for public methods — following it makes a shared codebase easier for everyone to navigate, and is often simply a courtesy to the developers who came before you.

Managing generated or boilerplate code. Designer-generated code (Windows Forms, service references) has historically been wrapped in regions like “Designer generated code,” keeping it out of the way of code that’s actually meant to be read and edited by hand — a role partial classes have mostly taken over today.

The case against it

It can mask a code smell. The sharpest criticism: regions let developers hide an overgrown class or method instead of fixing it. The problems are still there, just out of view — one commentator memorably called this “code deodorant.” A file full of collapsed regions may be quietly hiding duplication, long methods, or a violation of single responsibility that refactoring should actually address.

It costs readability for newcomers. Collapsing code you wrote helps you focus. It doesn’t help someone opening the file for the first time, who now has to expand section after section just to see what’s actually there — an extra layer of friction on every read.

It can discourage refactoring. Because the code “looks” manageable once folded, teams can let files balloon to thousands of lines without addressing the underlying design. Regions don’t reduce complexity; they just relocate it out of view.

It’s often unnecessary in well-structured code. If classes and methods are already small and focused, there’s little left for regions to organize. It’s telling that StyleCop’s rule SA1124 flags any use of #region at all — the reasoning being that well-factored code organizes itself through class, method, and namespace structure, with no folding markers needed.

Tooling support isn’t universal. Most editors handle #region fine, but simple diff/merge tools — notably during Git conflict resolution — don’t render the folding at all. In that context, a region is just visual noise.

Two worked examples

Beneficial use — grouping distinct, self-contained concerns within a class:

public class DataProcessor
{
    #region Configuration Settings
    private readonly string _connectionString;
    private int _maxRetries;
    #endregion

    #region Data Loading Methods
    public DataTable LoadFromDatabase(string query) { /* ... */ }
    public DataTable LoadFromFile(string filePath) { /* ... */ }
    #endregion

    #region Data Processing Methods
    public void ProcessData(DataTable data) { NormalizeData(data); }
    private void NormalizeData(DataTable data) { /* ... */ }
    #endregion
}

Here, each region genuinely groups a distinct concern, and collapsing “Configuration Settings” lets a reader focused on data processing ignore it entirely.

Misuse — folding steps of a single method instead of extracting them:

public void ProcessOrder(Order order)
{
    #region Validate Input
    if (order == null) throw new ArgumentNullException(nameof(order));
    if (order.Amount <= 0) throw new ArgumentException("Amount must be positive.");
    #endregion

    #region Save to Database
    SaveOrderToDatabase(order);
    #endregion

    #region Send Confirmation
    SendConfirmation(order);
    #endregion
}

This looks organized, but it’s masking the real issue: ProcessOrder is doing three distinct things and should be split into three methods. Folding the steps doesn’t reduce the method’s complexity — it just hides it, at the cost of making it easy to miss, for example, that validation throws before anything else runs.

Regions around local functions

Local functions add a wrinkle. They already help keep logic close to where it’s used without polluting the class with extra methods — but folding them with #region cuts both ways.

On the upside, collapsing local functions can keep a method’s high-level flow visible while hiding implementation detail that isn’t relevant to understanding the flow itself — useful when the local functions handle specialized, rarely-inspected tasks.

On the downside, it can blur the abstraction level a local function was supposed to clarify in the first place, adds another fold a reader has to open to get the full picture, and — as with regions generally — can become a substitute for actually breaking up a method that’s grown too long.

Best practices, if you use it at all

  1. Never use it inside a method. If a method feels like it needs a region, that’s a signal to extract a smaller method instead — not to fold part of it away.
  2. Prefer refactoring over hiding. A region around a long field list doesn’t make the class smaller; consider Extract Class instead.
  3. Name regions clearly. #region Validation Logic tells a reader something; #region Stuff doesn’t.
  4. Never nest regions. Nesting adds confusion without adding organization — pick one logical level of grouping and stop there.
  5. Be consistent, and respect team or linter conventions. If your team or a tool like StyleCop disallows regions, that settles it — don’t reintroduce them piecemeal.

The bottom line

#region is a tool for organizing code that’s already reasonably well-structured — not a substitute for making it well-structured in the first place. Used sparingly and named clearly, it can genuinely help navigate a large file. Used as a way to avoid dealing with an overgrown class or method, it just moves the mess somewhere less visible. If you find yourself reaching for a region, it’s worth pausing to ask whether the code should be reorganized instead of folded away.