Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Builder Pattern: Fluent Builders, WebApplicationBuilder and Where Object Initializers and required Fall Short
required, init and object initializers make a many-parameter constructor readable, but they do not solve cross-field rules, step order or an immutable result that accumulates piece by piece; Builder enforces those rules at a single Build() moment. How with skips a record's rule, the GoF Builder with a director versus the fluent builder, UriBuilder, connection string builders, the ImmutableArray builder, HostApplicationBuilder and the step builder, with real output.
Abstract Factory: Creating Families of Compatible Objects Together
Abstract Factory makes invalid combinations impossible by creating products that must fit together (such as blob storage, queue and secret store) from a single factory; the price is that adding a new product changes every factory. Choosing the family through configuration, ADO.NET's DbProviderFactory and CanCreateBatch, and a factory per tenant with keyed services, with real output.
Factory Method and Static Factories: Delegating Creation to Subclasses or a Single Method
The GoF Factory Method leaves the choice of product to a subclass; a static factory method is a single named method that replaces a constructor, can cache and can return a hidden subtype. The factory-method-in-constructor bug and CA2214, DbConnection.CreateDbCommand, ILoggerFactory and IHttpClientFactory, Func<T>, ActivatorUtilities and keyed services in DI, and ownership of IDisposable products, with real output.
Singleton Pattern: Thread-Safe Lazy<T>, Static Fields and Why the DI Singleton Lifetime Is Better
If you need a single instance, the cleanest route is not the classic Singleton but registering with AddSingleton in the DI container: the single-instance guarantee stays, while global access and hidden dependencies go away. The race in a naive Singleton, the history of double-checked locking, static readonly and beforefieldinit, Lazy<T>'s exception caching, captive dependencies and shared state inside a singleton, with real output.
What Is a Design Pattern? The GoF Catalog, Pattern Anatomy and Patterns That Vanish in Modern Languages
A design pattern is a named solution to a design problem that recurs in a given context, with defined roles and a known price (its consequences); it is not a diagram to memorise. The idea from Alexander to the GoF, the GoF's 13-section template, the purpose × scope map, and Norvig's observation on patterns that turn invisible in C# as yield, event and lambdas.
Clean Code in Practice: Cleaning Up a Legacy OrderService Step by Step
The safe way to clean up legacy code is to first pin the current behavior with a small seam and characterization tests, then make every step a separate commit and keep behavior changes in their own fix commits. The post cleans up the 609-line OrderService in 16 commits with green tests at every commit; the highest Cognitive Complexity drops from 92 to 10, and the smells left on purpose are listed with their reasons.
The Clean Code Debate: Ousterhout, Muratori and the Limits of the Rules
Ousterhout and Martin agree on the goal, reducing the reader's load; they part on how far to split methods, the value of comments and TDD. The post summarizes the 2024–25 debate, Muratori's performance critique (repeated in C# with BenchmarkDotNet) and qntm's critique of the book from each side's own sources.
A Code Smell Catalog: Recognizing Smells in C# Code
A code smell is a surface indication that usually points to a deeper design problem; it's not a diagnosis but a sign of where to look. The post maps Fowler's 24 second-edition smells onto Mäntylä's five groups, looks for each one in the legacy OrderService.cs and shows with real output which ones SonarAnalyzer catches and which it misses.
The Four Rules of Simple Design: Passes Tests, Reveals Intent, No Duplication, Fewest Elements
Kent Beck's simple design rules, in priority order, are: passes the tests, reveals intention, no duplication, fewest elements. The post grows a coupon discount calculation step by step with these four questions, separates coincidental similarity from real duplication and shows the cost of an over-generalized version that passes the same tests.
Clean Test Code: F.I.R.S.T., Readable Asserts and Test Data Builders
A clean test is fast, independent, repeatable, self-validating and timely (F.I.R.S.T.); the legacy OrderService tests break on Saturdays because of DateTime.Now and depending on run order because of the static Db. With TimeProvider and FakeTimeProvider, a test data builder, test names that read like sentences and asserts that state the reason when they fail, we turn the same rules into clean tests.
Classes in Clean Code: Small Classes, Cohesion and Organizing for Change
A class's size is measured by the number of actors who ask it to change, not by lines; methods that share no fields and CA1822 "make it static" warnings signal low cohesion. In the legacy OrderService two price calculations in the same class had drifted apart, quoting 731.50 TRY and charging 690.52 TRY; we gather pricing into a single OrderPricing class and build modern C# classes with primary constructors, sealed and required.
Boundaries: Wrapping Third-Party Code and Learning Tests
Put code you don't control, such as a payment SDK, an email library and a cache, behind an interface shaped by your own need and a single adapter; don't let its types and exceptions leak in. Pin the library's behavior down with learning tests: MimeKit's TryParse even accepts "ada", and on the upgrade from 4.17.0 to 4.18.2 all 13 tests passed unchanged while a transitive security warning disappeared.