1. Overusing async / await Without Understanding the Cost

The Mistake

Using async/await everywhere—even when no real async work is happening.

public async Task<int> GetCountAsync()
{
    return 10;
}

Why It’s a Problem

Better Approach

Only use async when awaiting actual asynchronous work.

public Task<int> GetCountAsync()
{
    return Task.FromResult(10);
}

Rule of thumb: Async all the way—but only when async is actually needed.

2. Ignoring IDisposable and Resource Lifetimes

The Mistake

Forgetting to properly dispose of unmanaged resources.

var stream = new FileStream("data.txt", FileMode.Open);
// use stream

Why It’s a Problem

Correct Usage

using var stream = new FileStream("data.txt", FileMode.Open);
// use stream

Or (when async):

await using var stream = new FileStream("data.txt", FileMode.Open);

Senior pitfall: “The GC will clean it up” — not fast enough, not safely.

3. Misusing LINQ in Performance-Critical Code

The Mistake

Writing expressive LINQ without considering allocations and execution cost.

var result = users
    .Where(u => u.IsActive)
    .Select(u => u.Email)
    .ToList();

Why It’s a Problem

When It Matters

Alternative

var result = new List<string>();
foreach (var user in users)
{
    if (user.IsActive)
        result.Add(user.Email);
}

Balance readability with performance, not blindly one or the other.

4. Overengineering With Abstractions

The Mistake

Introducing interfaces and layers too early.

IUserService → IUserRepository → IUserDataProvider → IUserStorage

Why It’s a Problem

Better Principle

Abstract when there is real variation or proven need.

Ask:

If not, keep it simple.

5. Forgetting That DateTime Is Tricky

The Mistake

Using DateTime.Now everywhere.

var expiresAt = DateTime.Now.AddDays(1);

Why It’s a Problem

Better Approach

Prefer DateTime.UtcNow or DateTimeOffset.

var expiresAt = DateTimeOffset.UtcNow.AddDays(1);

Time bugs are notorious and often discovered in production.

6. Catching Exception Too Broadly

The Mistake

try
{
    DoSomething();
}
catch (Exception ex)
{
    Log(ex);
}

Why It’s Dangerous

Better Approach

Catch specific exceptions and handle intentionally.

catch (IOException ex)
{
    Log(ex);
    throw;
}

Senior rule: If you catch it, you must own it.

7. Not Understanding Value vs Reference Semantics

The Mistake

Assuming structs behave like classes.

public struct Counter
{
    public int Value;
}

Passing it around and expecting mutations to persist.

Why It’s a Problem

Key Reminder

Use structs only when:

8. Skipping Meaningful Logging

The Mistake

_logger.LogError("Something went wrong");

Why It’s Useless

Better Logging

_logger.LogError(ex, "Failed to process order {OrderId}", orderId);

Logs are for future you at 3 AM.