
Next.js vs htmx + Rust. When to Use Each Stack
Compare Next.js with Axum, Askama, and htmx through browser state, contracts, caching, deployment, security, mobile clients, and project handover.

A design pattern is not proof of seniority. It is a name for a solution that has repeatedly worked for a particular kind of problem. If the problem is absent, the pattern adds files, interfaces, and vocabulary without reducing any real risk.
When I design business software, I do not begin by asking which pattern could fit. I ask what is likely to change, who owns that change, and what must remain stable around it. The answer may point to Strategy, Adapter, Factory, or a direct function.
Decide from the expected change, not the pattern name
The smallest abstraction that isolates a likely change is usually the right one.
One stable rule
Keep a direct function.
Changing algorithm
Consider Strategy.
Foreign interface
Isolate it with an Adapter.
Several creation paths
A Factory can hide assembly.
Consider delivery pricing. The first release supports pickup and one courier. Two branches in one function are easier to understand than a hierarchy of classes. The input, rule, and result remain visible together.
The pressure changes when pricing rules evolve independently. One carrier charges by weight, another by region, and contracted customers have their own terms. The function now has several unrelated reasons to change. An adjustment for one carrier can disturb every other path.
Good design does not react to line count. It reacts to independent reasons for change. Fifty stable lines can be simpler than ten lines edited weekly by three teams for three different business reasons.
Strategy fits when the task remains stable but the algorithm can vary. Quoting delivery is still one task. The calculation changes by carrier or contract.
type DeliveryQuote = (order: Order) => Money;
const quoteDelivery = (order: Order, quote: DeliveryQuote): Money =>
quote(order);
A Strategy does not need to be a class. In TypeScript, a type and a set of pure functions are often enough. The order does not know a carrier's price table, and the calculator does not need to know where the order originated. A new rule can be added without editing existing algorithms.
The pattern is a poor fit when one calculation is stable or when all variants share complicated mutable state. Tiny strategy files may then move branches around rather than isolate change. The actual boundary must be found first.
An ERP, payment gateway, or carrier API brings foreign names, data types, and failure modes. Allow them into the application core and the domain begins to speak a vendor's language. The next API revision then reaches orders, invoices, and user-facing code.
An Adapter translates that interface into a small contract owned by our system. The domain can request reserveStock even when one ERP describes stock movements and another exposes item reservations. The difference remains at the integration boundary.
The boundary improves testing too. A domain test does not need a network or a vendor sandbox. It verifies behaviour against the contract we own. A separate integration test proves that the adapter translates requests and responses correctly.
An Adapter should not become a drawer for arbitrary mess. If it only renames each field one for one and the provider interface is stable, it may add little. Its value grows with the semantic distance between the external model and our domain.
Factory handles object or workflow creation when assembly depends on the input type. A PDF invoice processor may need OCR, invoice validation, and an accounting mapper. A contract uses a different extractor and another set of checks.
The caller should not know the construction order of every dependency. It asks for a processor for a document type. The Factory assembles the processor and returns it through a shared contract.
Business judgement must not disappear inside a large Factory with dozens of branches. Selecting components for a document type belongs to creation. Deciding whether an invoice may post automatically is a domain rule. It should remain explicit and independently tested.
Same feature, different place of change
Patterns earn their place when they reduce the reach of the next real change.
Before
One service knows pricing, providers, and document formats.
After separation
The domain calls a small interface and change details stay behind the boundary.
During review, we ask three questions. Can we name a concrete expected change? Will the pattern keep that change behind one boundary? Is the result clearer to someone who does not know the history of the decision?
If the answer is no, the abstraction probably arrived early. Code does not need protection from every imaginable future. It needs protection from changes implied by the product, contracts, and operating model.
Patterns can work together. An Adapter gives the domain one interface, a Strategy holds the variable rule, and a Factory assembles the necessary dependencies. The combination is useful only when each part protects a different boundary. If three pattern names describe one conditional, the design is too large.
A business owner does not need to inspect a class diagram. They need to know whether adding a payment method or replacing an ERP touches the whole application. A CTO needs to see where the rule lives, which contract contains it, and which test protects its behaviour.
When we design custom software, we connect each technical boundary to a real business change. The next article applies the same thinking at system scale when choosing a modular monolith or microservices. For the effect of AI on development work itself, read our account of human judgement in AI-assisted development.
The best pattern fades into the codebase. A future change has an obvious home, a test has a clear purpose, and the rest of the system does not need to know.
Maroš Bednár prepared the article with AI support for research and language editing. He reviewed and approved the technical conclusions, examples, and final text.

Compare Next.js with Axum, Askama, and htmx through browser state, contracts, caching, deployment, security, mobile clients, and project handover.

A buyer checklist for security, integrations, data roles, ownership, support, and exit planning before signing a software contract.
A practical guide to defining requirements, comparing suppliers, validating delivery evidence, and reaching a defensible technology-partner decision.