Modern Code and Best Practices

How to Write Readable and Maintainable Code

Learn the habits that make code easier to read, maintain, and evolve over time.

Ahmed Oumezzine Ahmed Oumezzine 7 min read
  • Table of contents unavailable
How to Write Readable and Maintainable Code

Introduction

You spend hours in front of your screen, writing C#, building features, and fixing bugs.

And yet…

You come back to a piece of code you wrote three weeks ago and wonder:

“Wait… what was I trying to do here?”

You are not alone.

And it is not because you are a bad developer.

It is simply because nobody taught you how to write readable code.

You were taught how to make things work.

But not how to make them maintainable, scalable… and human-friendly.

In this article, I want to help you move from being a developer who “writes code” to one who builds something that can last.

No unnecessary jargon.

Just practical advice, as if we were talking over a cup of coffee.

And most importantly: ideas you can start applying tomorrow — even if you are just getting started.

Anyone can write code that a machine can understand. A good developer writes code that humans can understand.

— Adapted from Martin Fowler

Why Readability Is Your New Superpower

Imagine two teams:

  • Team A: writes code quickly. It works, but it is obscure. Every change takes days. New team members are lost.
  • Team B: takes a little longer to write code… but the result is extremely clear. Changes are fast. New team members can contribute from day one.

Guess which one costs less in the long run?

👉 Spoiler: Team B.

According to Microsoft, developers can spend up to 80% of their development time reading code rather than writing it.

So if your code is difficult to read, you are wasting time.

Your time.

Your colleagues' time.

Your company's time.

Real productivity is not about writing quickly. It is about understanding quickly.

1. Use Names That Speak for Themselves, Not Riddles

Close your eyes.

Read this variable name: empAge.

Do you understand it?

Yes, but you have to think about it.

And every tiny pause costs mental energy.

What if you read employeeAge instead?

Immediate.

Clear.

Effortless.

Here are some simple .NET naming rules, based on Microsoft's conventions but adapted to real-world development:

  • For classes, methods, and properties, use PascalCase. For example: CustomerService, GetOrder(), FirstName. The first letter of each word is uppercase.
  • For local variables and method parameters, prefer camelCase. For example: customerName, orderCount. The first word begins with a lowercase letter and following words begin with uppercase letters.
  • Interfaces should start with an uppercase I, followed by PascalCase. Examples: IOrderRepository, IUserService. That small I at the beginning acts like a sign saying: “this is an interface.”
  • Finally, for constants, use uppercase letters with underscores: UPPER_CASE. For example: MAX_LOGIN_ATTEMPTS, DEFAULT_TIMEOUT.


This makes them instantly recognizable in the code.

A Human Tip:

Imagine reading your code out loud to an intern.

If you have to explain what a name means, the name is probably not clear enough.

And most importantly: be consistent.

A decent convention used everywhere is better than a perfect convention applied inconsistently.

🔧 Smart tool: Install StyleCop.Analyzers in your project. It gently reminds you when you forget a rule — like a small coach inside your IDE.

2. Break It Down Like a Chef: Small Methods, Big Impact

A plate of pasta is simple.

But if the chef tries to cook, season, serve, and clean everything at the same time, it becomes chaos.

Code works the same way.

A 100-line method that does everything?

👉 It is a ticking time bomb.

Here is the golden rule:

One method = one responsibility.

Example:

public void GenerateInvoice(Customer customer)
{
    var invoice = CreateInvoice(customer);
    SaveInvoice(invoice);
    SendInvoiceEmail(invoice);
}

Three lines. Three responsibilities.

And each method can be tested independently.

Imaginary conversation:

– “But that creates more lines of code!”
– Yes. And that is better.
Because now you can say: “Create an invoice. Save it. Send it.”
It is code that speaks for itself.

And what about lists?

Use LINQ.

It Is Like a Filter for Clear Thinking:

var adults = customers.Where(c => c.Age >= 18).OrderBy(c => c.Name);

3. Clean Architecture: Your Code Needs a Blueprint

You would not build a house without a blueprint.

So why do it with your code?

In .NET, two architectures are especially common:

  • Layered Architecture: Presentation → Application → Domain → Infrastructure.
  • Clean Architecture: the domain sits at the center, with everything else around it.
Metaphor:
Your domain is the heart.
Infrastructure — database, APIs — is the skin.
If you change the skin, the heart should not die.

And what connects everything?

Dependency injection.

Think of it like a restaurant where every service — kitchen, waitstaff, checkout — works together without being tightly coupled.

services.AddScoped<IOrderService, OrderService>();

And in .NET 9?

Take advantage of records for immutable objects:

public record Customer(string Name, int Age);

 Less risk of accidentally changing state.

✅ Safer code.

✅ Easier to read.

4. Comments? Yes, but Only When They Add Value

The best comment?

The one that does not need to exist.

Because the code speaks for itself.

But sometimes you need to explain why, not what.

❌ Bad:

// Checks whether the email is valid
if (email.IsValid()) ...

👉 The code already tells you that.

✅ Good:

// Send the email only after the user has confirmed the address (GDPR)
if (customer.EmailConfirmed) ...

✨ Now you are explaining the business context.

That is the real value of a comment.

And what about documentation?

Use XML Comments (///) + Swagger.

You can generate documentation automatically.

Your colleagues — and your future self six months from now — will thank you.

5. Your Secret Tools for Clean Code

You are not alone.

The .NET ecosystem is packed with tools that can help you.

Here is your survival kit:

  • Roslyn Analyzers: detect bad practices in real time.
  • SonarLint: your code-quality coach inside Visual Studio.
  • xUnit / NUnit: for reliable unit tests.
  • GitHub Actions / Azure DevOps: run tests and linting on every commit.
🎯 Pro tip:
Configure a CI/CD pipeline that blocks pull requests when the code does not meet your rules.
Think of it as a safety net.

And what about code reviews?

They are one of your best learning tools.

Every pull request can become a mini training session.

💬 “This part is a little long. Could you split it into smaller methods?”
→ That is not criticism. It is help. 

6. Handle Errors… Without Hiding Them

An empty try-catch is like ignoring a fire.

❌ Avoid this:

try { ... }
catch (Exception) { }

👉 You lose the error, and the bug will come back later — often worse.

✅ Do this instead:

try
{
service.ProcessOrder(order);
}
catch (PaymentException ex)
{
logger.LogError(ex, "Payment error for order {OrderId}", order.Id);
throw; // Re-throw the exception if you cannot handle it here
}

🔍 Simple rule:

Catch only the exceptions you can actually handle.
Otherwise, let them propagate.
And always log them. 

🔄 7. Before / After: The Transformation

Here is a typical “before” example:


public void DoIt(Customer c)
{
if (c.Age >= 18)
{
if (c.HasValidLicense)
{
Database.Save(c);
}
}
}
👉 It works.
👉 But it is hard to read.
👉 And difficult to test properly.

Now here is the “after” version:

public void RegisterCustomer(Customer customer)
{
if (!IsEligible(customer)) return;
SaveCustomer(customer);
}

private bool IsEligible(Customer customer)
=> customer.Age >= 18 && customer.HasValidLicense;

private void SaveCustomer(Customer customer)
{
// Save logic
}
✅ Readability.
✅ Testability.
✅ Maintainability.

A small change. A big impact.

What Next? Keep Growing

Here are my human-to-human recommendations:

  • Write your code like a book: every class and every method should tell a story.
  • Refactor gradually: you do not have to change everything today. One method at a time.
  • Explore open-source projects: look at how ASP.NET Core is structured. It is a gold mine.
  • Avoid “magic numbers”: const int MIN_AGE = 18; > 18.
  • Use modern C# features: List<int> numbers = [1, 2, 3]; — much clearer, right?

Conclusion: Writing Clean Code Is an Act of Consideration

Writing readable code is not just a technical concern.

It is an act of respect:

  • For yourself six months from now.
  • For your colleagues.
  • For the future developers who will maintain your project.
🌱 You do not learn to write clean code in a single day.
But every small improvement matters.

So start now.

Pick one method.

Give it a better name.

Break it into smaller pieces.

And smile knowing that you just made the world… a little easier to read.

🗣️ What about you?
What is your favorite practice for keeping .NET code clean?
Share it in the comments — or send me a message.
I love these conversations.
Because we learn better… together. 
Share in X