Skip to content

Archive

Software Engineering

462 articles
Software Engineering 11 Sep 2026 8 min read

Replace Magic Numbers with Named Domain Values

Replace Magic Numbers with Named Domain Values A condition such as attempts >= 5 is easy to execute and surprisingly hard to review. Why five? Is it a security policy, a technical limit, a temporary experiment, or just an arbitrary value copied from somewhere else? The code contains the number but not the reason it exists. A magic number is a numeric literal whose meaning isn’t clear from its context. Replacing one well means more than moving it into a constant. The goal is to preserve the value’s meaning, units, and ownership so a future change has an obvious place to happen.

Software Engineering 11 Sep 2026 8 min read

Replace Data Clumps with Parameter Objects

Replace Data Clumps with Parameter Objects A method takes startDate, endDate, and timezone. Another method takes the same three values. A third passes them unchanged to a lower layer. Soon, changing what “reporting period” means requires editing signatures across the codebase. This is a common design smell called a data clump: several values repeatedly appear together because they are really parts of one concept, but the code still treats them as unrelated pieces. A parameter object gives that concept a name and a boundary.

Software Engineering 11 Sep 2026 9 min read

Parameterize from Above: Make Dependencies Explicit

Parameterize from Above: Make Dependencies Explicit A method can look self-contained while quietly deciding which clock, repository, client, or filesystem implementation the application must use. That hidden construction becomes painful when a test needs a controlled collaborator or production needs a different implementation. Parameterize from Above is a small design move: instead of constructing a dependency inside the code that uses it, accept that dependency from a caller at a higher level. The caller becomes responsible for choosing and constructing the collaborator.

Software Engineering 11 Sep 2026 9 min read

Mutation Testing: Check Whether Tests Detect Broken Behavior

Mutation Testing: Check Whether Tests Detect Broken Behavior A test suite can execute every line of a function and still miss a defect. The tests may call the right code but make weak assertions, cover only one outcome, or never check a boundary condition. Mutation testing probes that gap by making small changes to production code and running the relevant tests against each changed version. If a test fails, the change is said to be killed. If all tests still pass, the mutation survives and deserves inspection.

Software Engineering 11 Sep 2026 7 min read

Middle Man Code Smell: Remove Delegation That Adds No Value

Middle Man Code Smell: Remove Delegation That Adds No Value Delegation is useful when one object asks another object to do work. It can separate responsibilities and keep implementation details behind a boundary. But delegation can become ceremony when an object does little more than forward most calls to another object. That situation is commonly called the Middle Man code smell. The extra layer adds names, files, navigation, and maintenance work without owning a meaningful decision.

Software Engineering 11 Sep 2026 8 min read

Humble Object Pattern for Hard-to-Test Boundaries

Humble Object Pattern for Hard-to-Test Boundaries Some code is difficult to test for reasons that have little to do with its business rules. A screen handler may depend on a UI framework. A file watcher may need operating-system events. A scheduled job may be invoked by infrastructure that is awkward to reproduce in a unit test. A common mistake is to put more logic inside that difficult boundary. Tests then need the framework, filesystem, clock, process, or device just to check an ordinary decision.

Software Engineering 11 Sep 2026 9 min read

Fencing Tokens: Stop Stale Lock Holders from Writing

Fencing Tokens: Stop Stale Lock Holders from Writing A distributed lock can tell a client that it owns a resource for a limited period. That does not guarantee the client stops acting when the period ends. A process can pause for garbage collection, lose network access, become descheduled, or stall on an overloaded machine. During that pause, its lease can expire and another client can acquire the same lock. When the first process resumes, it may still believe it is entitled to write.

Software Engineering 11 Sep 2026 9 min read

Feature Envy: Move Behavior Closer to the Data It Uses

Feature Envy: Move Behavior Closer to the Data It Uses A method can live in one class while spending most of its time inspecting another. It asks that other object for several values, combines them according to rules about that object, and perhaps repeats the same pattern elsewhere. The code works, but changing the data often means hunting down behavior in unrelated places. This is the design smell commonly called feature envy. The name matters less than the question behind it: does this behavior belong closer to the data and rules it depends on?

Software Engineering 11 Sep 2026 8 min read

Expand and Contract Database Changes for Safe Deployments

Expand and Contract Database Changes for Safe Deployments A database schema can change in milliseconds while an application fleet takes minutes or hours to converge on a new version. During that interval, old and new application instances may use the same database at the same time. That overlap turns an ordinary schema edit into a compatibility problem. Renaming a column in one migration, for example, can break old instances immediately even when the new application code is correct.

Software Engineering 11 Sep 2026 8 min read

Consumer-Driven Contract Tests for Service Compatibility

Consumer-Driven Contract Tests for Service Compatibility Two services can pass their own test suites and still fail when deployed together. A provider may rename a field, narrow an accepted value, change a status code, or remove an endpoint. Its internal tests can remain green because those tests describe the provider’s own view of correct behavior. A consumer can still depend on the old interaction. Consumer-driven contract testing turns selected consumer expectations into executable contracts. The consumer records the interactions it requires. The provider then verifies those contracts against its implementation.

Software Engineering 11 Sep 2026 13 min read

Concurrency Limits: Bound In-Flight Work

Concurrency Limits: Bound In-Flight Work A service can receive traffic at an acceptable average rate and still collapse because too many operations overlap. The issue is not only how many requests arrive per second. It is also how many requests are active at the same time. A concurrency limit places a cap on active work. When all slots are occupied, additional work must wait, fail fast, or take another explicit path. This simple control can protect database connections, CPU-heavy routines, remote dependencies, worker capacity, and memory that grows with each active operation.

Software Engineering 11 Sep 2026 9 min read

Composed Method: Keep Each Routine at One Level of Abstraction

Composed Method: Keep Each Routine at One Level of Abstraction A routine becomes hard to read when it mixes two different jobs: describing a process and implementing every mechanical detail of that process. Consider an order checkout function that validates the cart, calculates a total, writes SQL, formats an email, and records metrics in one long block. A reader must move constantly between business intent and low-level mechanics. The code may be correct, yet its structure hides the story of the operation.

Software Engineering 11 Sep 2026 8 min read

Common Closure Principle: Group Code That Changes Together

Common Closure Principle: Group Code That Changes Together A codebase can have tidy classes and still make routine changes expensive. A new pricing rule might require edits in a controller module, a generic utilities package, a shared validation package, and a reporting package. Each edit is small, but one business decision now crosses several boundaries. The Common Closure Principle offers a practical way to judge those boundaries: code that tends to change for the same reason should tend to live in the same component. A component can be a package, module, library, or another unit that a team changes and releases together.

Software Engineering 11 Sep 2026 7 min read

Command-Query Separation for Predictable Methods

Command-Query Separation for Predictable Methods A method named getBalance() looks harmless. A caller expects it to report a value. If calling it also recalculates fees, updates an account, and writes an audit record, that caller has to understand much more than the name suggests. Command-query separation is a design principle for avoiding this kind of surprise. A command asks the system to change state. A query asks for information and does not change observable state. Keeping those responsibilities separate makes call sites easier to reason about and gives method contracts a clearer shape.

Software Engineering 11 Sep 2026 9 min read

Cache-Aside Consistency: Prevent Stale Overwrites

Cache-Aside Consistency: Prevent Stale Overwrites Cache-aside is attractive because the application controls a simple protocol. A read checks the cache first. On a miss, it reads the database and places the result in the cache. A write updates the database and then invalidates or refreshes the cache. Each step is easy to describe. Concurrency makes the combined behavior less obvious. A delayed cache fill can publish an older database value after a newer write has already completed. The database remains correct, yet later readers can receive stale data from the cache. This article develops the race precisely and presents practical designs that keep an old fill from replacing a newer state.

Software Engineering 10 Sep 2026 11 min read

Tolerant Reader Pattern for Evolving Contracts

Tolerant Reader Pattern for Evolving Contracts A producer adds an optional field to a response. No existing meaning changed, yet an older consumer starts rejecting every message because its parser expected exactly five fields. The producer made a seemingly compatible change, but the consumer had quietly coupled itself to details it never used. The Tolerant Reader pattern addresses that problem from the consumer side. A tolerant reader describes and validates the information it actually depends on while allowing unrelated parts of an incoming representation to vary. Used carefully, this makes contracts easier to evolve without turning validation into guesswork.

Software Engineering 10 Sep 2026 10 min read

Token Bucket Rate Limiting for Controlled Bursts

Token Bucket Rate Limiting for Controlled Bursts A service may handle 100 requests per second comfortably on average while still needing to accept a short burst of 300 requests after a client reconnects. A rigid per-second limit treats those situations as the same problem: once the current window is full, otherwise acceptable work is rejected. Token bucket rate limiting gives you a more useful control. It separates two decisions: how quickly permission to do work is replenished and how much permission may accumulate for a burst. Once you understand those two numbers, you can reason about the limiter without depending on a particular library or platform.

Software Engineering 10 Sep 2026 9 min read

Testing Asynchronous Behavior with Eventual Assertions

Testing Asynchronous Behavior with Eventual Assertions A test starts background work and then checks the result. On a fast machine the work finishes first and the test passes. Under CI load, the assertion runs a few milliseconds earlier and fails. Someone adds sleep(1 second). The failure disappears, but every successful run now pays a full second, and a sufficiently slow run can still fail. The problem is not that the test needs a longer delay. The test doesn’t know exactly when the result will become observable.

Software Engineering 10 Sep 2026 8 min read

Tell, Don’t Ask: Keep Decisions with the Data

Tell, Don’t Ask: Keep Decisions with the Data A checkout service reads an order’s status, total, and payment state, decides whether cancellation is allowed, and then changes the order. Later, a support tool needs the same operation and copies most of that decision logic. The two callers eventually disagree about one rule. Tell, Don’t Ask is a software design principle that helps prevent this kind of drift. Instead of asking an object for internal state so another object can make a decision about it, prefer telling the object what outcome you want and letting the object enforce the rules that belong to its state.

Software Engineering 10 Sep 2026 8 min read

Stale-While-Revalidate for Responsive Caches

Stale-While-Revalidate for Responsive Caches A cache entry expires just as a request arrives. The cached value is still only seconds old, but the request now has to wait while the application fetches a replacement from a slower dependency. If many entries expire during a busy period, cache refreshes can turn a normally fast read path into a burst of slow work. Stale-while-revalidate changes that trade-off. For data that can safely be slightly out of date, the application may return an expired cached value immediately while refreshing it separately for future requests. The reader gets predictable latency, and the cache still moves toward fresh data.

Software Engineering 10 Sep 2026 9 min read

Split Phase Refactoring to Separate Computation Stages

Split Phase Refactoring to Separate Computation Stages A function starts by interpreting input, then gradually accumulates validation, business rules, formatting, and output logic. None of those steps is necessarily complicated. The difficulty comes from having them interleaved: changing how input is interpreted can unexpectedly affect code that should only care about the interpreted result. Split Phase is a refactoring that separates one computation into distinct stages. The first phase produces an explicit intermediate result. The next phase consumes that result without needing to know how it was produced.

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 10 Sep 2026 8 min read

Saga Pattern for Multi-Step Workflows

Saga Pattern for Multi-Step Workflows A workflow reserves inventory, charges a payment, and schedules delivery. Each step is handled by a different component with its own state. The inventory reservation succeeds, but payment fails. What should the system do with the reservation that already committed? A single database transaction can’t usually roll back work that has already been committed by independent components. The saga pattern handles this kind of workflow by treating it as a sequence of local transactions. When a later step fails, the workflow runs explicit compensating actions for earlier steps where business reversal is possible.

Software Engineering 10 Sep 2026 10 min read

Replace Primitive Obsession with Domain Types

Replace Primitive Obsession with Domain Types A customer ID, an email address, and a currency code can all be represented as strings. That doesn’t make them interchangeable. When a codebase treats every meaningful value as a generic string, integer, or boolean, callers have to remember rules that the type itself doesn’t express. This problem is often called primitive obsession: using general-purpose primitive values where the domain has a more specific concept. The practical fix isn’t to wrap every string in a class. It’s to introduce a domain type when doing so gives the program a useful place to enforce meaning and rules.