ASP.NET Hosting

Anti-Patterns in Web APIs That Cause Production Applications to Fail

In this post, we will learn about 𝗪𝗲𝗯 𝗔𝗣𝗜 𝗔𝗻𝘁𝗶-𝗣𝗮𝘁𝘁𝗲𝗿𝗻𝘀 𝗧𝗵𝗮𝘁 𝗕𝗿𝗲𝗮𝗸 𝗬𝗼𝘂𝗿 .𝗡𝗘𝗧 𝗔𝗽𝗽𝘀 𝗶𝗻 𝗣𝗿𝗼𝗱𝘂𝗰𝘁𝗶𝗼𝗻.

It’s simple to create a Web API that functions on your computer. It is a very other task to build one that can withstand production traffic, failures, security concerns, and size.

These are typical ASP.NET Core Web API anti-patterns that frequently result in maintenance nightmares, performance degradation, and outages.

Now let’s begin.

1. Returning 200 OK for Everything

Anti-Pattern

return Ok(new
{
    Success = false,
    Message = "User not found"
});

Why It’s Bad

Clients cannot distinguish between successful and failed requests using HTTP semantics.

Better Approach

return NotFound("User not found");

Use proper status codes:

  • 200 OK
  • 201 Created
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

2. Catching Every Exception Globally and Hiding Details
Anti-Pattern

try
{
    ...
}
catch(Exception)
{
    return StatusCode(500);
}

Problems

  • Loses debugging information
  • Hides root causes
  • Makes production support difficult

Better Approach

Use centralized exception middleware:

app.UseExceptionHandler();

Log exceptions with:

ILogger<T>

Return standardized problem details.

return Problem(
    title: "Unexpected error occurred",
    statusCode: 500);

3. Synchronous Database Calls

Anti-Pattern

var users = _context.Users.ToList();

Why It’s Dangerous

Under load:

  • Blocks threads
  • Reduces throughput
  • Causes thread pool starvation

Better Approach

var users = await _context.Users.ToListAsync();``

Use async end-to-end.

4. Fat Controllers

Anti-Pattern

[HttpPost]
public IActionResult CreateOrder(...)
{
    // validation
    // business logic
    // database updates
    // notifications
    // email sending
}
Problems
  • Hard to test
  • Hard to maintain
  • Violates separation of concerns

Better Approach

await _orderService.CreateOrderAsync(request);

Keep controllers thin.

5. Exposing EF Core Entities Directly

Anti-Pattern

return Ok(customerEntity);

Problems

  • Leaks internal structure
  • Serialization issues
  • Security risks
  • Circular references

Better Approach

Use DTOs:

public record CustomerDto(
    int Id,
    string Name,
    string Email);
``

Map entities to DTOs.

6. Overfetching Data

Anti-Pattern

var users = await _context.Users
    .Include(x => x.Orders)
    .Include(x => x.Payments)
    .Include(x => x.Addresses)
    .ToListAsync();

Problems

  • Huge payloads
  • Slow SQL
  • Memory pressure

Better Approach

Project only required fields:

var users = await _context.Users
    .Select(u => new UserListDto
    {
        Id = u.Id,
        Name = u.Name
    })
    .ToListAsync();

7. Missing API Versioning

Anti-Pattern

Deploying breaking changes directly.

/api/customers

Result

Existing consumers suddenly fail.

Better Approach

/api/v1/customers
/api/v2/customers

Or use header-based versioning.

8. No Request Validation

Anti-Pattern

public async Task<IActionResult> Create(UserRequest request)
{
    // directly process
}

Problems

  • Invalid data enters system
  • Database corruption
  • Unexpected exceptions

Better Approach

Use:

[ApiController]

and FluentValidation:

RuleFor(x => x.Email)
    .NotEmpty()
    .EmailAddress();

9. Ignoring Cancellation Tokens

Anti-Pattern

public async Task<IActionResult> Get()
{
    await _service.ProcessAsync();
}

Problem

Work continues even after:

  • Browser closes
  • Mobile app disconnects
  • Load balancer times out

Better Approach

public async Task<IActionResult> Get(
    CancellationToken ct)
{
    await _service.ProcessAsync(ct);
}

10. Logging Too Little or Too Much

Too Little

_logger.LogError("Error");

No context.

Too Much

_logger.LogInformation(JsonSerializer.Serialize(request));

May expose:

  • Passwords
  • Tokens
  • PII

Better Approach

Structured logging:

_logger.LogInformation(
    "Creating order {OrderId} for customer {CustomerId}",
    orderId,
    customerId);

11. No Rate Limiting

Anti-Pattern

API accepts unlimited requests.

Risks

  • DDoS attacks
  • Resource exhaustion
  • Database overload

Better Approach (.NET 7+)

builder.Services.AddRateLimiter(...);

app.UseRateLimiter();

Protect critical endpoints.

12. Ignoring Health Checks and Observability

Anti-Pattern

You only discover problems after customers complain.

Missing Components

  • Health checks
  • Metrics
  • Tracing
  • Dashboards

Better Approach

builder.Services.AddHealthChecks();
app.MapHealthChecks("/health");

Add:

  • OpenTelemetry
  • Application Insights
  • Prometheus
  • Grafana

for proactive monitoring.

Production-Ready API Checklist

  • Proper HTTP status codes
  • Global exception handling
  • Async all the way
  • Thin controllers
  • DTOs instead of entities
  • Request validation
  • API versioning
  • Cancellation tokens
  • Structured logging
  • Rate limiting
  • Health checks
  • Observability & tracing

The majority of production API issues in .NET applications are not caused by complex algorithms. They’re caused by these architectural and operational anti-patterns that quietly accumulate until traffic, scale, or failures expose them. Fixing them early dramatically improves reliability, performance, and maintainability.

Conclusion

In this article, I have tried to cover Difference Between Controller and ControllerBase in ASP.NET Core.