EditorConfig Guide

Let me tell you a story that many Tech Leads will recognize. Picture this: You're in your fifth code review of the day. The code logic is solid, the architecture makes sense, but you find yourself typing the same comment for the third time this week: "Please add braces to this if statement, even if it's a single line."

Sound familiar?

I used to spend hours every week on these kinds of comments. Not because I enjoy being picky, but because inconsistent code is like a messy kitchen. One day someone puts the coffee in the fridge, the next day it's in the pantry, and before you know it, everyone's wasting time searching for the basics instead of cooking great meals.

As a Tech Lead, one of my core missions is to ensure consistency during the development process. Not because I'm a control freak, but because consistency frees up mental energy for the things that truly matter: solving business problems, crafting elegant architectures, and mentoring the team.

This is where EditorConfig changed my life. Or at least, it changed my workday.

The Problem: Death by a Thousand Paper Cuts

Before I discovered EditorConfig, here's what a typical code review looked like for me:

Monday morning, reviewing John's pull request:

"Hey John, can you add braces to this if statement? It's our coding standard."

Tuesday afternoon, reviewing Sarah's feature:

"Sarah, great work on the business logic! Quick note: we use braces even for single-line if statements."

Wednesday, reviewing Mike's hotfix:

"Mike, everything looks good, but... yeah, you guessed it... the braces."

Thursday, team standup:

Me: "Reminder everyone, please use braces for all if statements." Team: collective groan

Friday, retrospective:

Team member: "Can we automate these coding style checks somehow?" Me: wishing I had thought of that myself

The worst part? I wasn't even reviewing the important stuff anymore. I was so focused on formatting and style that I'd miss architectural issues or potential bugs. It was like being a restaurant critic who only comments on the napkin folding.

My Wake-Up Call

The moment I knew I had to change was during a critical production bug review. While I was leaving comments about brace placement and indentation, I completely missed a subtle race condition that caused a data corruption issue in production.

That hurt. Not just because of the production incident, but because I realized I had become the bottleneck. My team was waiting for my reviews, and I was wasting their time and mine on things that could be automated.

I needed a solution that would:

  1. Enforce coding standards automatically - no more manual comments
  2. Work across different IDEs - Visual Studio, VS Code, Rider
  3. Be configurable and flexible - every team has different preferences
  4. Be version-controlled - consistency should be part of the codebase
  5. Provide immediate feedback - before code review, during development

Enter EditorConfig.

What is EditorConfig? Let Me Explain Like You're New to the Team

Imagine you join a new development team. You clone the repository, open your favorite IDE, and start coding. You prefer tabs, but the codebase uses spaces. You like ending files with a newline, but the existing code doesn't. Your IDE formats code one way, but the team's standards are different.

Within hours, you've created merge conflicts in files you barely touched, just because of formatting differences.

EditorConfig prevents this. It's a simple configuration file that lives in your repository and tells every supported IDE exactly how to format code in your project.

Think of it as a universal translator for code formatting. No matter what IDE your team uses, everyone follows the same rules automatically.

The best part? It's supported by practically every modern IDE and editor:

  • Visual Studio
  • Visual Studio Code
  • JetBrains Rider
  • Sublime Text
  • Atom
  • And dozens more

My EditorConfig Journey: From Chaos to Consistency

Let me walk you through how I implemented EditorConfig in my team and what I learned along the way.

Step One: Understanding the Basics

An EditorConfig file is named dot editorconfig and lives in your repository root. It uses a simple INI-style format that anyone can read and understand.

Here's what a minimal file looks like:

# This is the root EditorConfig file
root = true

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

That's it. Three simple rules that prevent countless tiny inconsistencies.

Step Two: Getting Specific with C# Rules

For my dotnet projects, I wanted more control. I wanted to enforce our team's coding standards, especially that infamous brace rule.

Here's what I created:

# Root EditorConfig file
root = true

# All files - basic formatting
[*]
charset = utf-8
end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

# C# files - specific rules
[*.cs]
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true

# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left

# The famous brace rule that started it all
csharp_prefer_braces = true:warning

# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false

# Organize usings
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# Naming conventions
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.severity = warning
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.symbols = interface
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.style = begins_with_i

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_symbols.interface.applicable_kinds = interface

Let me explain the most important part - the severity levels. This was a game-changer for me.

Understanding Severity Levels: The Secret Sauce

EditorConfig doesn't just tell your IDE how to format code - it can also enforce rules with different levels of strictness:

  • silent - Apply the rule but don't show any indication
  • suggestion - Show a subtle hint (three dots under the code)
  • warning - Show a yellow squiggly line (like a spell checker)
  • error - Show a red squiggly line (breaks the build if configured)

Here's how I use them strategically:

# Must-have rules that prevent bugs or major issues
csharp_prefer_braces = true:warning

# Strong preferences that improve readability
dotnet_sort_system_directives_first = true:suggestion

# Nice-to-have but not critical
csharp_space_after_cast = false:silent

This three-tier approach was crucial. I learned this the hard way when I first set everything to "error" and my team nearly revolted. Start gentle, then increase strictness as the team adapts.

Step Three: The Brace Rule That Started Everything

Remember my original problem? The endless comments about braces? Here's the exact rule that solved it:

[*.cs]
# Prefer braces even for single-line if statements
csharp_prefer_braces = true:warning

This single line means that code like this:

// ? Visual Studio now warns about this
if (condition)
    DoSomething();

Gets a yellow squiggly line. Visual Studio even offers a quick fix (Control + Period) to automatically add the braces:

// ? This is what we want
if (condition)
{
    DoSomething();
}

No more code review comments. No more back-and-forth. The developer sees the warning immediately, fixes it before committing, and we're done.

Step Four: Real-World Examples from My Team

Let me show you some actual scenarios where EditorConfig saved us time and prevented issues.

Example 1: The New Team Member

Emma joined our team and had been coding in Java for years. She preferred tabs, a different brace style, and different naming conventions.

Without EditorConfig, her first pull request would have been a nightmare of formatting changes.

With EditorConfig, her IDE automatically matched our standards from day one. She barely noticed the difference, and her code review was focused on the actual logic.

Example 2: The Mixed IDE Team

Half my team uses Visual Studio, a quarter uses VS Code, and another quarter uses Rider. Before EditorConfig, each IDE had its own default formatting, leading to constant conflicts.

Now, it doesn't matter. The EditorConfig file is the source of truth, and everyone's code looks consistent.

Example 3: The Legacy Codebase

We had a project that started five years ago with completely different coding standards. Inconsistency was rampant.

I added an EditorConfig file to the root, and over time, as developers touched files, they gradually became consistent. We didn't have to do a massive formatting commit that would destroy git blame history.

My Complete EditorConfig Template for .NET Teams

After years of refinement, here's the EditorConfig file I now use for all my .NET projects. Feel free to use it as a starting point:

# EditorConfig is awesome: https://EditorConfig.org

# Top-most EditorConfig file
root = true

###############################
# All Files
###############################
[*]
charset = utf-8
end_of_line = crlf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

###############################
# Code Files
###############################
[*.{cs,csx,vb,vbx}]
indent_size = 4
max_line_length = 120

###############################
# XML Config Files
###############################
[*.{config,props,targets,nuspec,resx}]
indent_size = 2

###############################
# JSON Files
###############################
[*.{json,json5}]
indent_size = 2

###############################
# YAML Files
###############################
[*.{yml,yaml}]
indent_size = 2

###############################
# Markdown Files
###############################
[*.md]
trim_trailing_whitespace = false

###############################
# C# Files
###############################
[*.cs]

# Organize usings
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# this. and Me. preferences
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning

# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning

# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion

# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
dotnet_style_readonly_field = true:warning

# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent

# Pattern matching preferences
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion

# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion

# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion

# Expression-level preferences
csharp_prefer_braces = true:warning
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion

# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true

# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents_when_block = false

# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_after_comma = true
csharp_space_before_open_square_brackets = false

# Wrapping preferences
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true

###############################
# Naming Conventions
###############################

# Interfaces must start with I
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.severity = warning
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.symbols = interface
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.style = begins_with_i

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_symbols.interface.applicable_kinds = interface

# Private fields start with underscore
dotnet_naming_rule.private_fields_should_be_prefixed_with_underscore.severity = warning
dotnet_naming_rule.private_fields_should_be_prefixed_with_underscore.symbols = private_field
dotnet_naming_rule.private_fields_should_be_prefixed_with_underscore.style = begins_with_underscore

dotnet_naming_style.begins_with_underscore.required_prefix = _
dotnet_naming_style.begins_with_underscore.capitalization = camel_case

dotnet_naming_symbols.private_field.applicable_kinds = field
dotnet_naming_symbols.private_field.applicable_accessibilities = private

# Async methods end with Async
dotnet_naming_rule.async_methods_should_end_with_async.severity = warning
dotnet_naming_rule.async_methods_should_end_with_async.symbols = async_methods
dotnet_naming_rule.async_methods_should_end_with_async.style = ends_with_async

dotnet_naming_style.ends_with_async.required_suffix = Async
dotnet_naming_style.ends_with_async.capitalization = pascal_case

dotnet_naming_symbols.async_methods.applicable_kinds = method
dotnet_naming_symbols.async_methods.required_modifiers = async

# Constants are PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = warning
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = pascal_case

dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_symbols.constants.applicable_kinds = field
dotnet_naming_symbols.constants.required_modifiers = const

How to Implement EditorConfig in Your Team: Lessons Learned

Let me share the approach that worked for my team:

Week 1: Introduction and Buy-In

Monday:

  • Team meeting: "Hey everyone, I want to show you something that will save us all time"
  • Quick demo of EditorConfig in action
  • Explain the why, not just the what

Key message: "This isn't about me being picky. This is about freeing up our code reviews to focus on architecture, logic, and learning from each other instead of arguing about where to put a brace."

Week 2: Pilot with a Small Project

  • Choose a small, non-critical project
  • Add a basic EditorConfig file
  • Let the team experience it firsthand
  • Gather feedback

What I learned: Start permissive. Use "suggestion" severity for almost everything. Let developers get used to seeing the hints without feeling overwhelmed.

Week 3: Refinement

  • Team retrospective: What rules are helpful? Which are annoying?
  • Adjust severity levels based on consensus
  • Document the reasoning behind each rule

Pro tip: Add comments in your EditorConfig file explaining why rules exist. Future team members will thank you.

Week 4: Rollout to Main Projects

  • Add EditorConfig to your main repositories
  • Update team documentation
  • Make it part of onboarding for new developers

Critical step: Don't reformat existing code all at once! Let it happen organically as files are modified. This preserves git history and prevents massive merge conflicts.

Common Pitfalls and How to Avoid Them

Let me save you from the mistakes I made:

Pitfall 1: Being Too Strict Too Soon

My mistake: I initially set almost everything to "error" severity. The team rebelled within a day. Pull requests piled up because developers were fixing hundreds of warnings instead of writing features.

Solution: Start with "suggestion" for most rules, "warning" for important ones, and "error" only for critical issues that could cause bugs.

Gradually increase severity as the team adapts.

Pitfall 2: Not Explaining the Why

My mistake: I just added the EditorConfig file with a commit message: "Added code formatting rules"

Solution: Hold a team meeting. Explain that this is about reducing cognitive load, not about control. Show real examples of time wasted in code reviews.

Pitfall 3: Ignoring Team Feedback

My mistake: A team member said "I really prefer tabs over spaces" and I said "too bad, spaces are better"

Solution: Have a discussion. Sometimes they have valid points. Sometimes you need to explain the reasoning. But always listen and be willing to adjust if it makes sense.

Pitfall 4: Massive Reformatting Commits

My mistake: I ran a formatter on the entire codebase and created a commit that touched 500 files

Solution: Never do this. It destroys git blame and creates merge hell. Let formatting changes happen naturally as files are edited.

If you absolutely must reformat, use git blame ignore revisions.

Real Impact: The Before and After

Let me show you the concrete benefits I measured after six months of using EditorConfig:

Before EditorConfig:

  • Average code review time: 45 minutes per pull request
  • Formatting-related comments: 30% of all review comments
  • Merge conflicts due to formatting: 2-3 per week
  • Time spent in "style" discussions: 2 hours per week

After EditorConfig:

  • Average code review time: 30 minutes per pull request
  • Formatting-related comments: less than 5% of review comments
  • Merge conflicts due to formatting: maybe 1 per month
  • Time spent in "style" discussions: 15 minutes per month

The real win: Code reviews became about what matters:

  • Is the logic correct?
  • Does this scale?
  • Are we handling edge cases?
  • What can we learn from this approach?

Beyond the Basics: Advanced EditorConfig Techniques

Once your team is comfortable with EditorConfig, you can go deeper:

Per-Project Configuration

You can have different rules for different parts of your solution:

# Root .editorconfig
root = true

[*]
# Global rules

# Tests can be more lenient
[*Tests/*.cs]
csharp_prefer_braces = true:suggestion
max_line_length = 150

# Legacy code - just basic formatting
[Legacy/**/*.cs]
# Only enforce the absolute minimum

Integration with Build Pipeline

Add EditorConfig validation to your CI/CD:

# Example Azure DevOps YAML
- task: DotNetCoreCLI@2
  displayName: 'Build and Verify Formatting'
  inputs:
    command: 'build'
    arguments: '/warnaserror'

This turns warnings into errors during CI, preventing non-compliant code from being merged.

Team-Specific Rules

Different teams in your organization might have different preferences:

# Team A prefers explicit types
[TeamA/**/*.cs]
csharp_style_var_for_built_in_types = false:warning

# Team B prefers var
[TeamB/**/*.cs]
csharp_style_var_for_built_in_types = true:suggestion

The Human Side: Leading Through Standards

Here's something I learned that nobody talks about: EditorConfig isn't just a technical tool, it's a leadership tool.

When I first became a Tech Lead, I thought my job was to make all the technical decisions and enforce them. But that created resentment and dependency.

EditorConfig taught me something different: encode your standards, then get out of the way.

By putting our coding standards in a file that everyone can see, discuss, and propose changes to, I:

  1. Made expectations clear - no more guessing what I wanted
  2. Removed myself as a bottleneck - the IDE gives feedback instantly
  3. Created space for real code reviews - we discuss architecture, not tabs vs spaces
  4. Empowered the team - they could propose changes to the EditorConfig file
  5. Freed up my time - to mentor, design, and solve real problems

The best part? When a new developer asks "Why do we do it this way?", we can point to the EditorConfig file and the comments in it. It's documentation that enforces itself.

Your Action Plan: Getting Started Today

If I've convinced you to give EditorConfig a try, here's what you do right now:

In the next 10 minutes:

  1. Create a file named dot editorconfig in your repository root
  2. Add the basic rules from the "Complete Template" section above
  3. Commit and push

In the next day:

  1. Share the change with your team
  2. Explain the why
  3. Ask for feedback

In the next week:

  1. Refine rules based on team discussion
  2. Adjust severity levels
  3. Add it to your team documentation

In the next month:

  1. Review the impact
  2. Gather metrics
  3. Celebrate the time saved

In the next quarter:

  1. Expand to more advanced rules
  2. Consider adding to CI/CD
  3. Make it part of your team's standard toolkit

Final Thoughts: From Code Reviews to Code Leadership

Looking back, implementing EditorConfig was one of the best decisions I made as a Tech Lead. Not because it made the code perfect (nothing does), but because it freed me to focus on what really matters: helping my team grow, solving complex problems, and building great software.

Code reviews aren't about nitpicking. They're about learning, sharing knowledge, and catching real issues before they become production problems.

EditorConfig handles the nitpicking for you, so you can focus on the real review.

And honestly? That's what being a Tech Lead is about: removing obstacles, creating clarity, and empowering your team to do their best work.

Now if you'll excuse me, I have a code review to do. And for the first time in a long time, I'm actually looking forward to it.

Because we're going to talk about architecture, not braces.

Want to learn more?

See you for more tech leadership insights on devskillsunlock.com