<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Design Before the Pattern Name on Brian Faust</title><link>https://wimdy.works/series/design-before-the-pattern-name/</link><description>Recent content in Design Before the Pattern Name on Brian Faust</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 01 Jan 0001 00:00:00 +0000</lastBuildDate><atom:link href="https://wimdy.works/series/design-before-the-pattern-name/rss.xml" rel="self" type="application/rss+xml"/><item><title>A Design Principle Is a Compressed Failure History</title><link>https://wimdy.works/thoughts/a-design-principle-is-a-compressed-failure-history/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-design-principle-is-a-compressed-failure-history/</guid><description>&lt;p&gt;“This violates the single-responsibility principle” is not an explanation. It
may be the conclusion of one, but the useful part starts earlier:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Lookup policy, selection policy, and construction policy change for different
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;reasons. One class owns all three. A lookup change therefore makes selection
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tests and construction code part of the review surface.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Once that reasoning exists, “single responsibility” gives it a short name that
another engineer can retrieve. Without the reasoning, it is just a respectable
label which can justify almost any refactor.&lt;/p&gt;</description></item><item><title>Design Becomes Valuable When the Next Change Arrives</title><link>https://wimdy.works/thoughts/design-becomes-valuable-when-the-next-change-arrives/</link><pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/design-becomes-valuable-when-the-next-change-arrives/</guid><description>&lt;p&gt;One migration removed four vendor packages. Two payment drivers, their client
interfaces, and their factory interfaces did not change; the concrete clients
changed substantially.&lt;/p&gt;
&lt;p&gt;Two application controllers were less fortunate. They had constructed a
vendor connection directly to manage webhooks, so the replacement had to add a
small webhook-management role and move the remote protocol behind it.&lt;/p&gt;
&lt;p&gt;The same migration produced both results: some earlier seams absorbed the
change, while other paths showed exactly where the vendor had leaked through.
That tells us more than declaring the design “clean.” A design makes a
prediction about future change, and the next real change tells us which parts
of that prediction were right.&lt;/p&gt;</description></item><item><title>The Type a Caller Receives Defines the Role</title><link>https://wimdy.works/thoughts/the-type-a-caller-receives-defines-the-role/</link><pubDate>Sun, 01 Nov 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/the-type-a-caller-receives-defines-the-role/</guid><description>&lt;p&gt;A payment driver received a client through a factory. The factory promised a
five-operation interface. The concrete client had nine additional public
methods.&lt;/p&gt;
&lt;p&gt;Those extra methods covered token charging, status checks, settlement,
cancellation, token management, payment-method lookup, and refund
cancellation. None of the callers used them. When the transport was
replaced, all nine disappeared along with more than a hundred lines of concrete
client code.&lt;/p&gt;
&lt;p&gt;The driver did not change, nor did its interface or factory interface. That is
what a narrow collaboration buys: not fewer methods everywhere, but a smaller
answer to “what may this caller rely on?”&lt;/p&gt;</description></item><item><title>A Method Should Tell You What Changed</title><link>https://wimdy.works/thoughts/a-method-should-tell-you-what-changed/</link><pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-method-should-tell-you-what-changed/</guid><description>&lt;p&gt;A balance reservation could be pending, captured, released, or expired, and
only a pending reservation could move to one of the other three states. That
gave the system three commands:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;capture reservation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;release reservation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;expire reservation&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It did not get one method called &lt;code&gt;updateReservation()&lt;/code&gt; with a status argument.
The names said which transition the caller intended. The responses included the
resulting status, so a caller did not have to guess what a successful request
had produced.&lt;/p&gt;</description></item><item><title>Construction Should Not Hide Durable Writes</title><link>https://wimdy.works/thoughts/construction-should-not-hide-durable-writes/</link><pubDate>Tue, 01 Sep 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/construction-should-not-hide-durable-writes/</guid><description>&lt;p&gt;A test called a model factory&amp;rsquo;s &lt;code&gt;make()&lt;/code&gt; method. The model itself was never
saved. Two related records were.&lt;/p&gt;
&lt;p&gt;The factory computed the model&amp;rsquo;s default address and account fields by creating
real database rows. A test which only needed an in-memory user therefore changed
shared state before it reached its first assertion.&lt;/p&gt;
&lt;p&gt;A surrounding test transaction usually hid the damage by rolling it back.
Tests without that transaction left the rows behind, while another test used
&lt;code&gt;TRUNCATE&lt;/code&gt; for cleanup, committed the MySQL transaction, and made earlier writes
survive the rollback. The method returned an unpersisted object, but its name
still gave the wrong account of what had happened.&lt;/p&gt;</description></item><item><title>Global State Makes Local Reasoning Impossible</title><link>https://wimdy.works/thoughts/global-state-makes-local-reasoning-impossible/</link><pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/global-state-makes-local-reasoning-impossible/</guid><description>&lt;p&gt;A repository test created a shipment two days before &lt;code&gt;now&lt;/code&gt;, then counted
shipments between the start and end of the current month. It passed most days.
During the first two days of a month, “two days ago” belonged to the previous
month and the count became zero.&lt;/p&gt;
&lt;p&gt;Nothing in the test name mentioned a calendar boundary, and nothing in the
method signature mentioned time. The same test logic produced a different
result because its generated data moved with one more input: the instant at
which the test ran. Freezing the clock in the middle of the month stabilized
the example. It did not make the dependency local.&lt;/p&gt;</description></item><item><title>Framework Convenience Has a Boundary</title><link>https://wimdy.works/thoughts/framework-convenience-has-a-boundary/</link><pubDate>Wed, 01 Jul 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/framework-convenience-has-a-boundary/</guid><description>&lt;p&gt;A model was saved inside a database transaction. Its observer dispatched a
queue job. The worker started quickly, looked for the record which described
its work, and found nothing.&lt;/p&gt;
&lt;p&gt;The record existed in the transaction which dispatched the job, but it was not
yet visible to the worker&amp;rsquo;s database connection. Retained history connects that
race to successful wallet payments without a corresponding wallet transaction.
The job had been published; the state it needed had not. The framework did
exactly what the code asked. The shortcut failed because its lifecycle was
smaller than the business operation.&lt;/p&gt;</description></item><item><title>Dependencies Should Be Visible at Construction</title><link>https://wimdy.works/thoughts/dependencies-should-be-visible-at-construction/</link><pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/dependencies-should-be-visible-at-construction/</guid><description>&lt;p&gt;This line created a database record:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserFactory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;external_id&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It also performed an HTTP request.&lt;/p&gt;
&lt;p&gt;The factory did not mention a client. The model did not accept one. A
synchronous observer noticed the external identifier, prepared a profile
export, and resolved a connector from global framework state. The connector
then looked up a remote account.&lt;/p&gt;
&lt;p&gt;In tests without a fake, that lookup reached DNS or the network and retried
three times with exponential backoff. Tests which had run in under five
seconds took more than thirty. The slow test was annoying, but it was only the
visible symptom. A local-looking construction operation had acquired an
undeclared remote dependency.&lt;/p&gt;</description></item><item><title>A Service Class Can Become a Procedural Junk Drawer</title><link>https://wimdy.works/thoughts/a-service-class-can-become-a-procedural-junk-drawer/</link><pubDate>Fri, 01 May 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-service-class-can-become-a-procedural-junk-drawer/</guid><description>&lt;p&gt;A web controller contained nearly two thousand lines of shipment creation.
Moving those lines into a class described as a creation orchestrator sounded
like an architectural improvement.&lt;/p&gt;
&lt;p&gt;The new class contained nearly two thousand lines of shipment creation.&lt;/p&gt;
&lt;p&gt;The first move was still useful. It removed the procedure from the HTTP
adapter. What it did not do was make the procedure cohesive. Validation,
payment gating, draft persistence, provider booking, tracking, document
creation, response mapping, notifications, and indexing still changed inside
one file. &lt;code&gt;Service&lt;/code&gt; and &lt;code&gt;orchestrator&lt;/code&gt; are roles only when the code earns those
names. Otherwise they are folders for procedures we no longer want in
controllers.&lt;/p&gt;</description></item><item><title>A Primitive Can Hide a Domain Decision</title><link>https://wimdy.works/thoughts/a-primitive-can-hide-a-domain-decision/</link><pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-primitive-can-hide-a-domain-decision/</guid><description>&lt;p&gt;Uppercasing a country code looks like normalization:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mb_strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It makes &lt;code&gt;fi&lt;/code&gt; and &lt;code&gt;FI&lt;/code&gt; equal. It also turns &lt;code&gt;finland&lt;/code&gt; into &lt;code&gt;FINLAND&lt;/code&gt; and leaves
&lt;code&gt;FIN&lt;/code&gt; looking plausible.&lt;/p&gt;
&lt;p&gt;The string is tidier, not safer. The function has changed its shape without
answering the domain question: is this a supported country identity in the
representation this boundary promises? A primitive becomes expensive when
every caller must remember that answer. The reason to introduce a domain type
isn&amp;rsquo;t that strings are unfashionable. It is that the scalar no longer carries
enough of the contract.&lt;/p&gt;</description></item><item><title>Boolean Parameters Hide Different Operations</title><link>https://wimdy.works/thoughts/boolean-parameters-hide-different-operations/</link><pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/boolean-parameters-hide-different-operations/</guid><description>&lt;p&gt;This method call is legal:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$exporter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It is also semantically wrong.&lt;/p&gt;
&lt;p&gt;The first boolean in the exporter meant “source billing details from
the effective payer.” The payload still targeted the original account and kept
that account&amp;rsquo;s operational fields. The second boolean meant “the source&amp;rsquo;s
billing mode is an authoritative instruction.”&lt;/p&gt;
&lt;p&gt;An effective-payer export described who pays; it was not allowed to change the
original account&amp;rsquo;s billing mode. The &lt;code&gt;true, true&lt;/code&gt; combination therefore had to
be neutralized by a later guard.&lt;/p&gt;</description></item><item><title>Invalid State Should Be Difficult to Construct</title><link>https://wimdy.works/thoughts/invalid-state-should-be-difficult-to-construct/</link><pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/invalid-state-should-be-difficult-to-construct/</guid><description>&lt;p&gt;A wallet reservation held one amount while a booking was in progress. When the
booking later reached the billing system, an imported record could contain no
price. The reservation knew how much money had been held, but not how that
amount divided into net price and VAT.
There was no authoritative way to create the final sale from the hold alone;
inferring a tax rate would turn a guess into accounting data.&lt;/p&gt;</description></item><item><title>A Class Name Should Describe a Role</title><link>https://wimdy.works/thoughts/a-class-name-should-describe-a-role/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-class-name-should-describe-a-role/</guid><description>&lt;p&gt;Several classes in one application had almost the same shape. Each extended a
connection manager, read named configuration, and delegated object creation to
a factory. One managed payment gateways, another rating clients, and another
booking clients.&lt;/p&gt;
&lt;p&gt;The shared abstraction made their code look alike. Their callers were asking
different questions.&lt;/p&gt;
&lt;p&gt;Payment callers needed a gateway with a given name. Rating callers sometimes
needed to decide which connection matched a shipment. Booking callers selected
connections from different rules for creating, collecting, and cancelling,
then assembled operation-specific services around the result.&lt;/p&gt;</description></item></channel></rss>