Clean Code .NET Clean Code principles from Robert C. Martin, adapted for C#/.NET. Use as checklist during code reviews and refactoring. Naming Use Meaningful Names // ❌ Bad int d ; var dataFromDb = db . GetFromService ( ) . ToList ( ) ; // ✅ Good int daySinceModification ; var employees = _employeeService . GetEmployees ( ) . ToList ( ) ; Avoid Hungarian Notation // ❌ Bad int iCounter ; string strFullName ; public bool IsShopOpen ( string pDay , int pAmount ) { } // ✅ Good int counter ; string fullName ; public bool IsShopOpen ( string day , int amount ) { } Use Pronounceable Names // ❌ Bad public class Employee { public DateTime sWorkDate { get ; set ; } public DateTime modTime { get ; set ; } } // ✅ Good public class Employee { public DateTime StartWorkingDate { get ; set ; } public DateTime ModificationTime { get ; set ; } } Use Domain Names // ✅ Good - Use patterns developers know var singletonObject = SingleObject . GetInstance ( ) ; var factory = new PatientFactory ( ) ; var repository = new PatientRepository ( ) ; Variables Return Early, Avoid Deep Nesting // ❌ Bad - Deep nesting public bool IsShopOpen ( string day ) { if ( ! string . IsNullOrEmpty ( day ) ) { day = day . ToLower ( ) ; if ( day == "friday" ) { return true ; } else if ( day == "saturday" ) { return true ; } // ... more nesting } return false ; } // ✅ Good - Guard clauses + early return public bool IsShopOpen ( string day ) { if ( string . IsNullOrEmpty ( day ) ) return false ; var openingDays = new [ ] { "friday" , "saturday" , "sunday" } ; return openingDays . Contains ( day . ToLower ( ) ) ; } Avoid Magic Strings // ❌ Bad if ( userRole == "Admin" ) { } // ✅ Good const string AdminRole = "Admin" ; if ( userRole == AdminRole ) { } // ✅ Better - Use enum public enum UserRole { Admin , User , Guest } if ( userRole == UserRole . Admin ) { } Don't Add Unneeded Context // ❌ Bad - Redundant prefix public class Car { public string CarMake { get ; set ; } public string CarModel { get ; set ; } public string CarColor { get ; set ; } } // ✅ Good public class Car { public string Make { get ; set ; } public string Model { get ; set ; } public string Color { get ; set ; } } Use Default Arguments // ❌ Bad public void CreateMicrobrewery ( string name = null ) { var breweryName = ! string . IsNullOrEmpty ( name ) ? name : "Hipster Brew Co." ; } // ✅ Good public void CreateMicrobrewery ( string breweryName = "Hipster Brew Co." ) { // breweryName is always valid } Functions Functions Should Do One Thing // ❌ Bad - Multiple responsibilities public void SendEmailToListOfClients ( string [ ] clients ) { foreach ( var client in clients ) { var clientRecord = db . Find ( client ) ; if ( clientRecord . IsActive ( ) ) { Email ( client ) ; } } } // ✅ Good - Single responsibility public void SendEmailToActiveClients ( string [ ] clients ) { var activeClients = GetActiveClients ( clients ) ; activeClients . ForEach ( client => Email ( client ) ) ; } public List < Client
GetActiveClients ( string [ ] clients ) { return db . Find ( clients ) . Where ( c => c . IsActive ) . ToList ( ) ; } Avoid Side Effects // ❌ Bad - Modifies global state var name = "Ryan McDermott" ; public void SplitAndEnrichFullName ( ) { var temp = name . Split ( " " ) ; name = $"First: { temp [ 0 ] } , Last: { temp [ 1 ] } " ; // Side effect! } // ✅ Good - Pure function public string SplitAndEnrichFullName ( string name ) { var temp = name . Split ( " " ) ; return $"First: { temp [ 0 ] } , Last: { temp [ 1 ] } " ; } Avoid Negative Conditionals // ❌ Bad public bool IsDOMNodeNotPresent ( string node ) { } if ( ! IsDOMNodeNotPresent ( node ) ) { } // Double negative! // ✅ Good public bool IsDOMNodePresent ( string node ) { } if ( IsDOMNodePresent ( node ) ) { } Avoid Flag Parameters // ❌ Bad - Flag indicates multiple responsibilities public void CreateFile ( string name , bool temp = false ) { if ( temp ) Touch ( "./temp/" + name ) ; else Touch ( name ) ; } // ✅ Good - Separate methods public void CreateFile ( string name ) => Touch ( name ) ; public void CreateTempFile ( string name ) => Touch ( "./temp/" + name ) ; Limit Function Arguments (2 or fewer) // ❌ Bad public void CreateMenu ( string title , string body , string buttonText , bool cancellable ) { } // ✅ Good - Use object public class MenuConfig { public string Title { get ; set ; } public string Body { get ; set ; } public string ButtonText { get ; set ; } public bool Cancellable { get ; set ; } } public void CreateMenu ( MenuConfig config ) { } Encapsulate Conditionals // ❌ Bad if ( article . state == "published" ) { } // ✅ Good if ( article . IsPublished ( ) ) { } Remove Dead Code // ❌ Bad public void OldRequestModule ( string url ) { } // Unused! public void NewRequestModule ( string url ) { } var request = NewRequestModule ( requestUrl ) ; // ✅ Good - Delete unused code public void RequestModule ( string url ) { } var request = RequestModule ( requestUrl ) ; SOLID Principles Single Responsibility (SRP) // ❌ Bad - Two responsibilities class UserSettings { public void ChangeSettings ( Settings settings ) { if ( VerifyCredentials ( ) ) { / ... / } } private bool VerifyCredentials ( ) { / ... / } // Auth responsibility } // ✅ Good - Separated class UserAuth { public bool VerifyCredentials ( ) { / ... / } } class UserSettings { private readonly UserAuth _auth ; public void ChangeSettings ( Settings settings ) { if ( _auth . VerifyCredentials ( ) ) { / ... / } } } Open/Closed (OCP) // ❌ Bad - Must modify to extend class HttpRequester { public bool Fetch ( string url ) { if ( adapterName == "ajaxAdapter" ) return MakeAjaxCall ( url ) ; else if ( adapterName == "httpNodeAdapter" ) return MakeHttpCall ( url ) ; // Must add more else-if for new adapters! } } // ✅ Good - Open for extension, closed for modification interface IAdapter { bool Request ( string url ) ; } class AjaxAdapter : IAdapter { public bool Request ( string url ) { / ... / } } class HttpRequester { private readonly IAdapter _adapter ; public bool Fetch ( string url ) => _adapter . Request ( url ) ; } Liskov Substitution (LSP) // ❌ Bad - Square breaks Rectangle behavior class Square : Rectangle { public override void SetWidth ( double width ) { Width = Height = width ; } } // ✅ Good - Use abstraction abstract class Shape { public abstract double GetArea ( ) ; } class Rectangle : Shape { / ... / } class Square : Shape { / ... / } Interface Segregation (ISP) // ❌ Bad - Robot can't eat but must implement interface IEmployee { void Work ( ) ; void Eat ( ) ; } class Robot : IEmployee { public void Work ( ) { / ... / } public void Eat ( ) { / Robot can't eat! / } } // ✅ Good - Segregated interfaces interface IWorkable { void Work ( ) ; } interface IFeedable { void Eat ( ) ; } class Human : IWorkable , IFeedable { / ... / } class Robot : IWorkable { / ... / } Dependency Inversion (DIP) // ❌ Bad - Depends on concrete types class Manager { private readonly Robot _robot ; private readonly Human _human ; } // ✅ Good - Depends on abstractions class Manager { private readonly IEnumerable < IEmployee
_employees ; public Manager ( IEnumerable < IEmployee
employees ) { _employees = employees ; } } Constructor Dependency Smell (SRP Indicator) Too many constructor dependencies indicate SRP violation: // ❌ Code Smell: 15 dependencies = too many responsibilities! public class LicensePlateAppService : ApplicationService { public LicensePlateAppService ( IRepository < LicensePlate , Guid
licensePlateRepository , IRepository < LicensePlateWithoutTag , Guid
licensePlateWithoutTagRepository , IRepository < ASN , Guid
asnRepository , IRepository < Project , Guid
projectRepository , IRepository < Tag , Guid
tagRepository , IRepository < SKU , Guid
skuRepository , IRepository < Customer , Guid
customerRepository , IRepository < LicensePlateHold , Guid
licensePlateHoldRepository , IRepository < LicensePlateLocation , Guid
licensePlateLocationRepository , IRepository < Location , Guid
locationRepository , IWarehouseAppService warehouseAppService , IWarehouseOwnerAppService warehouseOwnerAppService , IBlobContainer < BulkUpdateLPExcelFileContainer
fileContainer , LicensePlateService . LicensePlateServiceClient licensePlateServiceClient , CommonDependencies < LicensePlateAppService
commonDependencies ) { } } // ✅ Good: Split by responsibility public class LicensePlateAppService { } // CRUD only (~5 deps) public class LicensePlateBulkService { } // Bulk imports (~4 deps) public class LicensePlateEventPublisher { } // Events (~3 deps) Dependency Count Guidelines: Dependencies Status Action 1-5 ✅ Normal Acceptable 6-8 ⚠️ Warning Review for splitting opportunities 9+ ❌ Smell Refactor required - class has too many responsibilities Refactoring Strategies: Extract Service - Move related operations to a dedicated service Facade Pattern - Group related dependencies behind a facade Domain Events - Decouple via publish/subscribe instead of direct calls Mediator Pattern - Use MediatR to reduce direct dependencies Error Handling Don't Use throw ex // ❌ Bad - Loses stack trace catch ( Exception ex ) { logger . LogError ( ex ) ; throw ex ; // Stack trace lost! } // ✅ Good - Preserves stack trace catch ( Exception ex ) { logger . LogError ( ex ) ; throw ; // Rethrows with original stack } // ✅ Also Good - Wrap with inner exception catch ( Exception ex ) { throw new BusinessException ( "Operation failed" , ex ) ; } Don't Ignore Caught Errors // ❌ Bad - Silent swallow catch ( Exception ex ) { } // Never do this! // ✅ Good - Handle or propagate catch ( Exception ex ) { _logger . LogError ( ex , "Operation failed" ) ; throw ; // Or handle appropriately } Use Multiple Catch Blocks // ❌ Bad - Type checking in catch catch ( Exception ex ) { if ( ex is TaskCanceledException ) { / ... / } else if ( ex is TaskSchedulerException ) { / ... / } } // ✅ Good - Separate catch blocks catch ( TaskCanceledException ex ) { // Handle cancellation } catch ( TaskSchedulerException ex ) { // Handle scheduler error } Comments Avoid Positional Markers and Regions // ❌ Bad
region Scope Model Instantiation var model = new Model ( ) ;
endregion
region Action setup void Actions ( ) { }
- endregion
- // ✅ Good - Let code speak
- var
- model
- =
- new
- Model
- (
- )
- ;
- void
- Actions
- (
- )
- {
- }
- Don't Leave Commented Code
- // ❌ Bad
- DoStuff
- (
- )
- ;
- // DoOtherStuff();
- // DoSomeMoreStuff();
- // ✅ Good - Use version control
- DoStuff
- (
- )
- ;
- Only Comment Business Logic Complexity
- // ❌ Bad - Obvious comments
- var
- hash
- =
- 0
- ;
- // The hash
- var
- length
- =
- data
- .
- Length
- ;
- // Length of string
- // ✅ Good - Explains WHY, not WHAT
- // Using djb2 hash for good speed/collision tradeoff
- hash
- =
- (
- (
- hash
- <<
- 5
- )
- -
- hash
- )
- +
- character
- ;
- Quick Reference Checklist
- Code Review Checklist
- Naming
-
- Meaningful, pronounceable, no Hungarian
- Functions
-
- Single responsibility, <3 args, no flags
- Variables
-
- No magic strings, early returns, no nesting >2
- SOLID
-
- Interfaces over concrete, small focused classes
- Dependencies
-
- Constructor has <8 dependencies (SRP indicator)
- Error Handling
-
- No
- throw ex
- , no silent catch, specific exception types
- Comments
-
- No regions, no dead code, explains WHY
- References
- references/solid-principles.md
-
- Full SOLID examples
- references/async-patterns.md
-
- Async/await guidelines
- references/editorconfig-template.md
- .editorconfig template Source : clean-code-dotnet