Skip to content

Archive

Design Patterns

13 articles
Software Engineering 11 Sep 2026 8 min read

Using a Facade to Reduce Dependency Surface

Using a Facade to Reduce Dependency Surface A feature starts with one call into a library. A few months later, every caller knows which three components to create, which methods must run first, which defaults belong together, and which low-level errors need translation. The subsystem still works, but its internal structure has leaked into the rest of the codebase. A facade is a deliberately simpler interface placed in front of a more complicated subsystem. Callers depend on the operations they actually need instead of coordinating the subsystem themselves. This article explains how to recognize that design problem, build a focused facade, and decide when the extra layer is useful rather than ceremonial.

Software Engineering 10 Sep 2026 10 min read

Specification Pattern for Composable Business Rules

Specification Pattern for Composable Business Rules Business rules often begin as a few readable conditions. Then the same decisions appear in validation, eligibility checks, filtering, and workflow code. Small differences creep in: one path checks account status but forgets the credit limit; another copies the whole expression and changes only one threshold. The Specification pattern gives a business rule a name and an interface, then allows rules to be combined into larger decisions. It is useful when the same rule matters in several places or when complex policy is easier to understand as a composition of smaller concepts.

Software Engineering 09 Sep 2026 9 min read

Using Factories to Contain Construction Policy

Creating an object is sometimes just allocation plus a few obvious values. In that case, direct construction is easy to read and easy to maintain. But construction can gradually become a decision process: choose an implementation, supply required collaborators, apply defaults, validate combinations, and ensure every caller assembles the object the same way. When that policy is copied across callers, a change to construction becomes a search-and-edit exercise. One caller may miss a new dependency or keep an obsolete default even though the resulting objects share the same conceptual role.

Software Engineering 09 Sep 2026 8 min read

Null Object Pattern for Optional Behavior

Optional behavior often begins with one harmless-looking condition. A component may send notifications only when a notifier is configured, record metrics only when metrics are enabled, or write audit events only in some deployments. As the code grows, the same absence check can spread across many call sites: if notifier != null: notifier.send(message) The condition is simple, but repetition creates a maintenance problem. Every caller must remember that the collaborator may be absent and must know what absence means.

Software Engineering 09 Sep 2026 7 min read

Adding Behavior with the Decorator Pattern

A component often starts with one clear responsibility and then attracts optional behavior. A client sends a request; later the system also needs logging. Some deployments need metrics. Others need caching or access checks. Putting every option inside the original component can make its core job harder to see, while creating subclasses for every combination quickly becomes awkward. The Decorator pattern offers another shape. A decorator implements the same contract as the component it wraps, performs additional work, and delegates the main operation to that wrapped component. Because callers still see the same contract, decorators can be added, removed, and combined without teaching callers about each feature.

Software Engineering 08 Sep 2026 8 min read

Replacing Repeated Absence Checks with the Null Object Pattern

Optional collaborators often begin innocently. A service may have a notifier when notifications are enabled, a metrics recorder when monitoring is configured, or an audit sink in environments that need auditing. The first absence check is easy to understand. The twentieth can make the real behavior harder to see. The Null Object pattern is one way to remove that repetition. Instead of representing “no collaborator” with a null-like value and asking every caller to handle it, provide an object that implements the same contract with behavior appropriate for absence.

Software Engineering 07 Sep 2026 9 min read

Modeling Special Cases as Explicit Behavior

A system often starts with one ordinary case and one exception. A customer has a normal pricing plan unless the account is a guest. A shipment has a delivery date unless it is pickup-only. A report has an owner unless it is generated by the system. The first exception is usually harmless. The problem appears when the same check spreads through the code. Every caller asks whether it has the exceptional case before deciding what to do. Adding a new operation then means finding another place where the condition must be repeated.

Software Engineering 05 Sep 2026 7 min read

Replacing Repeated Null Checks with Null Objects

Optional collaborators often begin harmlessly. A service may accept an optional audit recorder, notification sink, or metrics collector. Then every operation that uses the collaborator grows the same check: if audit != null: audit.record("order_created", order.id) One check is easy to understand. Dozens of checks create a different problem: knowledge that the collaborator may be absent is scattered through code that should be focused on other work. A missed check can fail at runtime, while slightly different checks can produce inconsistent behavior.

Software Engineering 05 Sep 2026 9 min read

Keeping Framework Code Humble at System Boundaries

A user clicks Save. The handler reads text fields, validates an order, calculates a discount, writes data, chooses an error message, and updates the screen. Testing one business rule now requires constructing a UI framework and arranging several unrelated details. The rule itself is not difficult. It is difficult to reach because it is mixed with code that must talk to the outside world. The Humble Object pattern addresses this problem by keeping framework-dependent code deliberately small and moving important decisions into ordinary code that can be exercised directly. This article explains the mental model, shows how to find a useful boundary, and examines where the pattern helps and where it only adds indirection.

Python 04 Sep 2026 9 min read

Use functools.singledispatch for Type-Based Extension Points in Python

A function often starts with one input type and later grows branches for several related types. The first version may be straightforward: def render(value): if isinstance(value, str): ... elif isinstance(value, list): ... elif isinstance(value, dict): ... As the number of supported types grows, this function becomes the place where every extension must be added. The branches mix dispatch logic with the behavior for each type, and independently maintained modules cannot add support without editing the central function.

Software Engineering 04 Sep 2026 6 min read

Replacing Repeated Null Checks with a Null Object

Optional collaborators often begin with one harmless check. A service sends a notification only when a notifier is configured, so the code asks whether the notifier exists before calling it. Later, the same check appears in five methods, then twelve. The repeated condition is telling you something: absence has a defined behavior. In this case, “no notifier” means “do nothing when asked to notify.” The Null Object pattern represents that behavior with an object that follows the same interface as the real collaborator but performs the appropriate neutral action. This article shows how to recognize that situation, apply the pattern without hiding errors, and decide when a normal null check is still the clearer design.

Software Engineering 03 Sep 2026 10 min read

Dependency Injection Without a Container

Dependency injection is often introduced together with a framework or container. That can make a simple design idea look like infrastructure: register services, configure scopes, add annotations, and ask a runtime to assemble an object graph. The underlying technique is smaller. A component receives the collaborators it needs instead of constructing or locating them itself. Another part of the program decides which implementations to supply. For many applications, ordinary constructors and functions are enough. Manual dependency injection keeps object creation visible, makes required dependencies explicit, and avoids coupling application logic to a container API. A container can still be useful when the object graph becomes large or a framework owns object creation, but it is not a prerequisite for dependency injection.

Python 02 Sep 2026 7 min read

Single Dispatch in Python: Extensible Type-Based Behavior

A function that accepts several kinds of input often begins with a few isinstance() checks. That approach is straightforward when the cases are small and local. As the number of supported types grows, however, one function can become a long decision tree that mixes unrelated implementations. Python’s functools.singledispatch offers another design. It turns one function into a generic function whose implementation is selected from the runtime type of its first argument. Type-specific behavior can then be registered separately while callers keep using one public function.