<?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://vacua.dev/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://vacua.dev/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://vacua.dev/thoughts/a-design-principle-is-a-compressed-failure-history/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><guid>https://vacua.dev/thoughts/a-design-principle-is-a-compressed-failure-history/</guid><description>&lt;p&gt;“This violates the single-responsibility principle” is not an explanation.&lt;/p&gt;
&lt;p&gt;It may be the conclusion of one. The explanation 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;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;Now “single responsibility” gives that recurring pressure a short name. The
name helps another engineer retrieve the argument. It does not replace it.&lt;/p&gt;</description></item><item><title>Design Becomes Valuable When the Next Change Arrives</title><link>https://vacua.dev/thoughts/design-becomes-valuable-when-the-next-change-arrives/</link><pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/thoughts/design-becomes-valuable-when-the-next-change-arrives/</guid><description>&lt;p&gt;One migration removed four vendor packages.&lt;/p&gt;
&lt;p&gt;Two payment drivers did not change. Their client interfaces did not change.
Their factory interfaces did not change. The concrete clients changed
substantially.&lt;/p&gt;
&lt;p&gt;Two application controllers did change. They had constructed a vendor
connection directly to manage webhooks. The replacement introduced a small
webhook-management role and moved the remote protocol behind it.&lt;/p&gt;
&lt;p&gt;The same migration produced both results. Some earlier seams absorbed the
change. Other paths exposed exactly where the vendor had leaked through.&lt;/p&gt;</description></item><item><title>The Type a Caller Receives Defines the Role</title><link>https://vacua.dev/thoughts/the-type-a-caller-receives-defines-the-role/</link><pubDate>Sun, 01 Nov 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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 retained 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. Its interface did not change. Its factory interface
did not change.&lt;/p&gt;</description></item><item><title>A Method Should Tell You What Changed</title><link>https://vacua.dev/thoughts/a-method-should-tell-you-what-changed/</link><pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/thoughts/a-method-should-tell-you-what-changed/</guid><description>&lt;p&gt;A balance reservation could be pending, captured, released, or expired. Only a
pending reservation could move to one of the other three states.&lt;/p&gt;
&lt;p&gt;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;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://vacua.dev/thoughts/construction-should-not-hide-durable-writes/</link><pubDate>Tue, 01 Sep 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;That state usually disappeared when a surrounding test transaction rolled
back. Tests without the transaction trait left it behind. Another test used
&lt;code&gt;TRUNCATE&lt;/code&gt; for cleanup, which committed the MySQL transaction and made earlier
writes survive the rollback.&lt;/p&gt;</description></item><item><title>Global State Makes Local Reasoning Impossible</title><link>https://vacua.dev/thoughts/global-state-makes-local-reasoning-impossible/</link><pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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. 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.&lt;/p&gt;</description></item><item><title>Framework Convenience Has a Boundary</title><link>https://vacua.dev/thoughts/framework-convenience-has-a-boundary/</link><pubDate>Wed, 01 Jul 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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. It was not yet
visible to the worker&amp;rsquo;s database connection.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</description></item><item><title>Dependencies Should Be Visible at Construction</title><link>https://vacua.dev/thoughts/dependencies-should-be-visible-at-construction/</link><pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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.&lt;/p&gt;</description></item><item><title>A Service Class Can Become a Procedural Junk Drawer</title><link>https://vacua.dev/thoughts/a-service-class-can-become-a-procedural-junk-drawer/</link><pubDate>Fri, 01 May 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;/p&gt;</description></item><item><title>A Primitive Can Hide a Domain Decision</title><link>https://vacua.dev/thoughts/a-primitive-can-hide-a-domain-decision/</link><pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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 function has changed the string&amp;rsquo;s shape. It has not answered the domain
question: is this a supported country identity in the representation this
boundary promises?&lt;/p&gt;
&lt;p&gt;A primitive becomes expensive when every caller must remember that answer.
The reason to introduce a domain type is not 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://vacua.dev/thoughts/boolean-parameters-hide-different-operations/</link><pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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 retained 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://vacua.dev/thoughts/invalid-state-should-be-difficult-to-construct/</link><pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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.&lt;/p&gt;
&lt;p&gt;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://vacua.dev/thoughts/a-class-name-should-describe-a-role/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://vacua.dev/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>