Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
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.
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.
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.
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.
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.
Prototype Pattern: record and with, Shallow vs Deep Copy and Why ICloneable Is Discouraged
Prototype creates a new object by copying a configured sample instead of building it from scratch; in C#, the with expression on records does this while preserving the runtime type, but the copy is shallow. A prototype registry, controlled deep copies with a copy constructor, ICloneable's vague contract, the measured cost of copying through JSON and the with traps that skip validation.
Adapter Pattern: Fitting a Third-Party SDK to Your Own Interface
Adapter fits a class you can't change to the interface the client expects; in C# that is almost always an object adapter, built with composition. Connecting two imaginary payment SDKs to a single IPaymentGateway, turning a callback into a Task with TaskCompletionSource, mapping error codes to domain exceptions and verifying both adapters with the same contract test.
Bridge Pattern: Letting Abstraction and Implementation Grow Independently
Bridge splits two independent axes of change (like message type and channel) into two hierarchies connected by a reference, instead of squeezing them into one inheritance hierarchy, so the class count becomes a sum rather than a product. Moving Bookstore's notifications from a 3 × 3 class explosion to 3 + 3, the HttpClient/HttpMessageHandler and ILogger/ILoggerProvider bridges and how it differs from Adapter.
Composite Pattern: Treating Tree Structures Through One Interface
Composite puts leaves and the composites containing them behind the same interface, so the client can use a single item and a nested tree without telling them apart; the recursion stays in the only place that knows the tree, inside the composite. Pricing nested book bundles in Bookstore, the transparency versus safety choice, IConfiguration and CompositeChangeToken, and the stack overflow of a cyclic tree.
Decorator Pattern: Adding Caching, Logging and Retry Without Touching the Code
Decorator adds behavior to an object at runtime by implementing the same interface and delegating to the object inside; the order of decorators is a design decision that determines what they measure and what they protect. Adding cache, logging and retries to Bookstore's catalog, the Stream family and ownership with leaveOpen, manual DI registration and Scrutor's Decorate, DelegatingHandler and how default interface methods silently bypass a decorator.
Facade Pattern: Presenting a Complex Subsystem Through a Single Door
A facade gathers the knowledge of using several subsystems in the right order, with the right compensation steps, into one class; it gives clients an easy entry point without closing off the subsystem. A CheckoutFacade in Bookstore that coordinates stock, payment, invoicing and notification, the real code behind File.ReadAllTextAsync and GetFromJsonAsync, how Facade differs from Service Layer, Adapter and Mediator, and the god facade trap.
Flyweight Pattern: Saving Memory with Shared State — string.Intern and ArrayPool
Flyweight stores the repeating, immutable part of many objects (intrinsic state) once through a factory and shares it, keeping the line-specific part (extrinsic state) outside. A BookInfoFactory that cuts a million-line report from 377 MB to 30 MB, the side effects of string.Intern, the Task and array instances .NET already shares, why ArrayPool is an object pool rather than a flyweight, and BenchmarkDotNet measurements.