clean-code-dotnet

Clean Code principles from Robert C. Martin, adapted for C#/.NET. Use as checklist during code reviews and refactoring.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "clean-code-dotnet" with this command: npx skills add thapaliyabikendra/ai-artifacts/thapaliyabikendra-ai-artifacts-clean-code-dotnet

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&#x3C;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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-review-excellence

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-code-review-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

feature-development-workflow

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-advanced-types

No summary provided by upstream source.

Repository SourceNeeds Review