Code review in C#: what to check and how to give feedback
\nCode review is one of the most powerful tools for improving code quality. For beginner C# developers, it often feels stressful: you're afraid your code will be criticized, or you're unsure what exactly to look for in someone else's code. In reality, a good code review is a dialogue, not an exam. In this article, we'll break down what to pay attention to when reviewing C# code (especially in the .NET ecosystem) and how to phrase comments so they help rather than offend.
\n\n1. What to check in C# code: a reviewer's checklist
\nWhen you open a colleague's pull request (PR), your eyes can dart around. To avoid getting lost in details, use a systematic approach. Here are the key areas to check in any C# project.
\n\n1.1. Architecture and readability
\nFirst, assess the big picture. Answer these questions:
\n- \n
- Does this code solve the given problem? \n
- Does it duplicate existing logic (DRY principle)? \n
- Are the names of classes, methods, and variables clear? \n
Bad:
\npublic void DoStuff(int a, int b)\n{\n var x = a + b;\n // ... another 50 lines\n}\nGood:
\npublic int CalculateTotalPrice(int basePrice, int taxAmount)\n{\n return basePrice + taxAmount;\n}\nThe method and parameter names immediately convey the purpose. This reduces cognitive load on the reader.
\n\n1.2. Error handling and null safety
\nC# has a rich type system, but NullReferenceException is a common issue. Check for:
\n- \n
- Are null checks used (
?.,??,??=)? \n - Is exception handling (try-catch) present where necessary? \n
- Are exceptions swallowed in an empty catch block? \n
Example of dangerous code:
\nvar user = GetUserById(id);\nConsole.WriteLine(user.Name); // Error if user == null\nSafe version:
\nvar user = GetUserById(id);\nConsole.WriteLine(user?.Name ?? "Guest");\n\n1.3. Performance and LINQ
\nLINQ is convenient but can be inefficient. Check for:
\n- \n
- Are there multiple iterations over a collection (e.g.,
.Where().Select().ToList()on IQueryable)? \n - Is
Any()used instead ofCount() > 0to check for elements? \n - Are there unnecessary materializations (
.ToList(),.ToArray()) in the middle of a chain? \n
Bad:
\nvar list = items.Where(x => x.IsActive).ToList();\nvar first = list.FirstOrDefault();\nBetter:
\nvar first = items.FirstOrDefault(x => x.IsActive);\n\n1.4. Asynchrony and Task
\nIn .NET, asynchrony is the standard. Look for:
\n- \n
- Is
awaitmissing before an asynchronous call? \n - Are there blocking calls (
.Result,.Wait()) in a synchronous context? \n - Is the convention followed: async methods end with
Async? \n
Anti-pattern:
\nvar data = GetDataAsync().Result; // May cause deadlock\nCorrect:
\nvar data = await GetDataAsync();\n\n2. How to give feedback: gently but constructively
\nEven a perfect checklist is useless if your comments sound like criticism. Here are a few rules to make code review useful for everyone.
\n\n2.1. Use "I" statements and questions
\nInstead of "You wrote bad code," say "I think this method could be simplified. What do you think?".
\nExamples of phrasing:
\n- \n
- "I'm not su