Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Error Handling in Clean Code: Exceptions, the Try Pattern and the Result Pattern
Use the Try pattern or Result<T> for expected business outcomes (out of stock, invalid coupon, payment declined), argument exceptions for bugs and exceptions with added context for infrastructure failures, and catch them only at the application's boundary. We clean up the return codes, the static LastError and the empty catch blocks of the legacy OrderService with these three tools; in our measurement a thrown exception is about 1,165 times slower than TryParse.
Objects or Data Structures? Records, DTOs and Primitive Obsession
An object hides its data and exposes behavior, a data structure exposes data and carries no behavior, and the hybrid in between is the most expensive of all. Use records for DTOs, encapsulation for the domain and value objects such as Money and Email for concepts carried in strings and decimals; we also give a short introduction to the Law of Demeter.
Formatting and File Layout: Making Code Read Like a Newspaper
Formatting is communication, not aesthetics, and a tool rather than a person should enforce it: wrong indentation in a brace-less if makes the log say an e-mail was sent when it wasn't, and SonarAnalyzer and dotnet format catch it, not the compiler. The post covers the newspaper metaphor, vertical layout, member order, one type per file, converting to file-scoped namespaces and splitting a 259-character LINQ chain into lines, with real output.
Comments in Clean Code: When to Write Them and When to Turn Them into Code
Put in code what code can say (names, constants, enums, methods) and in a comment what it can't (why, warnings, external context, a public API's contract): most comments in the legacy OrderService repeat the code, two of them lie, and changelogs and commented-out code belong in Git. The post shows how XML doc comments are checked through CS1572/CS1573/CS1574/CS1591 and covers Ousterhout's case for interface comments, with real output.
Naming in Clean Code: Intention-Revealing Names, .NET Guidelines and Mixed Turkish-English Names
A good name answers the questions a reader would otherwise ask the code: replacing d, tmp2, k and int t in the legacy OrderService with domain names and constants makes the pricing readable without changing behavior (same totals in five scenarios). The post covers .NET naming rules, Hungarian notation, mixed Turkish-English names and the Turkish I trap that turns Id into ıd under tr-TR, with real output.
What Is Clean Code? Readability, Cost of Change and the 2025 Second Edition
Clean code is code that works and is also cheap to read and change: in Bookstore's legacy OrderService the same cart yields four different totals because of the Turkish I trap, a drifted copy of the pricing logic and DateTime.Now. The 2025 second edition rewrote the book into Code, Design, Architecture and Craftsmanship parts; this series cleans the 609-line file step by step.
Critiquing the Principles: CUPID, Over-Engineering and Where SOLID Really Pays Off
SOLID's value depends on context: in multi-team, long-lived, frequently changing code it lowers the cost of change, while in scripts, prototypes and small CRUD apps it often just adds layers. The measured cost of adding a push channel to minimal, balanced and excessive designs of the same notification feature, Dan North's CUPID proposal and Ousterhout's deep module warning.
Component Principles: Splitting a .NET Solution into Projects (REP, CCP, CRP, ADP, SDP, SAP)
When splitting a solution into projects, classes that change together belong in the same project, dependencies must be acyclic and flow toward stability, and stable projects should be abstract. Measuring the cost of a Common project everyone depends on, computing instability and abstractness on a real solution, and reading the main sequence correctly.
Lesser-Known Principles: Separation of Concerns, Least Astonishment, Fail Fast, CQS and GRASP
The principles outside SOLID drive most everyday decisions: Fail Fast catches errors at startup, CQS keeps a question from changing the answer, and GRASP asks which class a responsibility belongs to. Catching a configuration typo with options validation, the Math.Round surprise, side effects in getters, Explicit Dependencies, Persistence Ignorance and deciding when principles collide.
Composition over Inheritance: Fragile Base Class, Delegation and Pluggable Behavior
Inheritance ties a subclass to its base class's implementation details, so a harmless change in the base class can silently break a subclass, while composition and delegation depend only on the contract. The fragile base class problem shown with real output, the class explosion when behavior grows along two axes, and where inheritance is right: Collection<T> hooks and sealed.
DRY, KISS and YAGNI: Duplication, Simplicity and "You Aren't Gonna Need It"
DRY means a single source for knowledge, not for code; merging two rules that change for different reasons just because they look alike is not DRY but a wrong abstraction. Backing out of a wrong abstraction as Metz suggests, the rule of three, measuring KISS with CA1502, and the four costs Fowler lists for YAGNI.
Dependency Inversion Principle: DIP vs DI vs IoC and the Composition Root
DIP asks for the compile-time dependency to flow from details to abstractions and for the abstraction to be owned by its user; DI is only the technique of supplying dependencies from outside, and IoC is control of the flow moving to a framework. Which project the interface belongs in, using Program.cs as the composition root, and why a service locator defeats DIP.