<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Luis Orduz - Engineering</title><link href="https://luord.com/" rel="alternate"></link><link href="https://luord.com/feed/engineering.atom.xml" rel="self"></link><id>https://luord.com/</id><updated>2022-04-30T00:00:00-05:00</updated><icon>https://luord.com/assets/img/site/favicon.png</icon><entry><title>Domains of engineers and users</title><link href="https://luord.com/2022/04/30/domain/" rel="alternate"></link><published>2022-04-30T00:00:00-05:00</published><updated>2022-04-30T00:00:00-05:00</updated><author><name>Luis Orduz</name></author><id>tag:luord.com,2022-04-30:/2022/04/30/domain/</id><summary type="html">&lt;p&gt;Improving communication by writing code that describes the domain more closely.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I finished &lt;a href="https://luord.com/2022/03/31/abstracting/"&gt;my last post&lt;/a&gt; by mentioning how refactoring can help us achieve a code base where discussing
the code and discussing the domain model can be analogous or even entail similar discussions. That's
a topic that deserves a deeper looking into.&lt;/p&gt;
&lt;h2&gt;Experts on different domains&lt;/h2&gt;
&lt;p&gt;Imagine this conversation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;End user/Product owner: "We'd like for the URL of the Foo to have the provided identifier instead of these random characters."&lt;/p&gt;
&lt;p&gt;Engineer: "Oh, by default the framework adds a UUID as the primary key, which is also used for URLs; we'll update it."&lt;/p&gt;
&lt;p&gt;End user/Product owner: "Is that a front-end change or a back-end change?"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, now, that's an exaggeration as I've worked with plenty of users and product people who were well
versed enough on technical details—often because they &lt;em&gt;had&lt;/em&gt; to pick up the terminology—to understand what the developer was talking about
in that exchange, but I hope this gets the idea across: Too often, there's a gap in shared terminology that hinders the communication between
engineers and users.&lt;/p&gt;
&lt;p&gt;And here's an example in the other direction:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;End user/Product owner: "We need the aggregated results of the 'Foo' to be calculated from the monthly 'bar' instead of the weekly 'bar' from now on."&lt;/p&gt;
&lt;p&gt;Engineer: "Understood, I'll add it to the backlog and change it as soon as possible."&lt;/p&gt;
&lt;p&gt;Engineer (later, to another engineer or the tech lead): "Where the hell do the aggregated results value come from? Is that in the database? How is it called there? And what even is &lt;code&gt;bar&lt;/code&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that one is no exaggeration; I've had that exact conversation over the years, multiple times.
And I've been on both sides of that last question too. Of course, I've also been in projects where there was enough rapport between
the engineering and product teams for the engineer to simply ask the product owner right away... But the problem still persists,
only in the form of periodic repetitive conversations instead of the latency caused by the developer looking for someone who understands.&lt;/p&gt;
&lt;p&gt;The point is that engineers are experts on the domain of software engineering, while users
are experts on &lt;em&gt;their&lt;/em&gt; own domain, which they're hoping it gets easier to do with the
application/platform/system they bought/subscribed to, or hired the engineers to create/maintain.&lt;/p&gt;
&lt;h2&gt;Code and stories&lt;/h2&gt;
&lt;p&gt;Let's imagine that the user/product owner comes at us with the following requirement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a user, I want to store my "Foo" with the "jon", "doe",
"bar" and "baz" values. With the understanding that "baz" is seven times
"bar".&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After the appropriate back and forth, we come to the following &lt;a href="https://luord.com/2020/02/01/stories/"&gt;user story&lt;/a&gt;:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Store Foo&lt;/dt&gt;
&lt;dd&gt;Create a new model in &lt;code&gt;cool_framework&lt;/code&gt; used to store the periodic &lt;code&gt;Foo&lt;/code&gt; of the
user. The fields are &lt;code&gt;jon&lt;/code&gt; (unique string), &lt;code&gt;doe&lt;/code&gt; (integer, 100 by default),
and &lt;code&gt;bar&lt;/code&gt; (float, cannot be empty); &lt;code&gt;baz&lt;/code&gt; will be dynamically generated from &lt;code&gt;bar&lt;/code&gt;.
A UUID field will be included as primary key by the framework.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This user story is of course lacking a way for users to actually submit the data they want to store&lt;sup id="fnref:form"&gt;&lt;a class="footnote-ref" href="#fn:form"&gt;1&lt;/a&gt;&lt;/sup&gt;,
but it already has more than enough to show how the mismatch in communication starts: The user/product owner
could very well ask "what the hell is a UUID?"&lt;sup id="fnref:float"&gt;&lt;a class="footnote-ref" href="#fn:float"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I should point out that I am in no way condemning this approach. It's perfectly fine and how around ninety
percent of the projects I've worked on have looked.&lt;/p&gt;
&lt;p&gt;But now let's look at the code that fulfills the user story:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;cool_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;jon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;doe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FloatField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nd"&gt;@models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computed_property&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, this is perfectly fine; I currently work in projects whose code looks like this (with a
billion more fields and methods, of course), and there aren't problems, usually.&lt;/p&gt;
&lt;p&gt;The thing is while this code can trivially be discused among engineers, it can't be discussed
by or with users. By this point, both groups are essentially speaking different languages: The users
talk about whatever Foo is, how the "jon" group performed this week, when the "baz" could change, etc.;
the engineers, conversely, are talking about fields, properties, tables, migrations, etc.&lt;/p&gt;
&lt;p&gt;And this works! Most of my experience has had this kind of separation and the teams work and the product
is delivered. But what if the language gap could be narrowed?&lt;/p&gt;
&lt;h2&gt;A shared language&lt;/h2&gt;
&lt;p&gt;Let's rephrase that story:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Store Foo&lt;/dt&gt;
&lt;dd&gt;Create a class/type/struct&lt;sup id="fnref:class"&gt;&lt;a class="footnote-ref" href="#fn:class"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;code&gt;Foo&lt;/code&gt; where the user can store multiple instances of the following
values: &lt;code&gt;jon&lt;/code&gt; (type &lt;code&gt;ProvidedIdentifier&lt;/code&gt;), &lt;code&gt;doe&lt;/code&gt; (type &lt;code&gt;UserEstimation&lt;/code&gt;), and &lt;code&gt;bar&lt;/code&gt; (type &lt;code&gt;PeriodicResults&lt;/code&gt;);
&lt;code&gt;baz&lt;/code&gt; (type &lt;code&gt;AggregatedResults&lt;/code&gt;) is calculated from &lt;code&gt;bar&lt;/code&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;I just made up those types, but what's important here is that we're to assume that those types &lt;em&gt;mean something&lt;/em&gt; to
the users. Whatever is in "jon", the users usually call it the "provided identifier" of whatever Foo is; they know
that "bar" is the "periodic results" of whatever it is that the users do. Ditto for the other fields.&lt;/p&gt;
&lt;p&gt;With that understanding, let's rewrite the code to start illustrating why representing the domain more explicitly is important:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProvidedIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEstimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PeriodicResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AggregatedResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;FACTOR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PeriodicResults&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FACTOR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;jon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ProvidedIdentifier&lt;/span&gt;
  &lt;span class="n"&gt;doe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserEstimation&lt;/span&gt;
  &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PeriodicResults&lt;/span&gt;

  &lt;span class="nd"&gt;@property&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AggregatedResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AggregatedResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Is the code more verbose? In total, absolutely&lt;sup id="fnref:lesscode"&gt;&lt;a class="footnote-ref" href="#fn:lesscode"&gt;4&lt;/a&gt;&lt;/sup&gt; but it too became clearer
about the &lt;em&gt;what&lt;/em&gt; we're dealing with: We can assume that the signatures of those classes came from discussions with the users,
where they described what that data means in their business and how it's supposed to behave.
Sure, &lt;code&gt;doe&lt;/code&gt; is still a string, &lt;code&gt;bar&lt;/code&gt; a floating point value, etcetera, but now the developers can talk about
the code in terms similar to what the users talk about, which also means they'll talk to the &lt;em&gt;users&lt;/em&gt; in those terms too.&lt;/p&gt;
&lt;p&gt;Let's reimage the conversations from the start, but now with the assumption that all the code is like this:&lt;/p&gt;
&lt;p&gt;Well, for starters the first conversation no longer makes sense as another win from these changes is that we decoupled
our business logic from whatever framework we're using&lt;sup id="fnref:clean"&gt;&lt;a class="footnote-ref" href="#fn:clean"&gt;5&lt;/a&gt;&lt;/sup&gt;... As long as the framework treats the business code as source of truth anyway.&lt;/p&gt;
&lt;p&gt;As for the second,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;End user/Product owner: "We need the aggregated results of the 'Foo' to be calculated from the monthly 'bar' instead of the weekly 'bar' from now on."&lt;/p&gt;
&lt;p&gt;Engineer: "Just to make sure, &lt;code&gt;baz&lt;/code&gt; is the aggregated results and &lt;code&gt;bar&lt;/code&gt; is the periodic results, correct?"&lt;/p&gt;
&lt;p&gt;End user/Product owner: "Correct!"&lt;/p&gt;
&lt;p&gt;Engineer: "Cool, I'll get it done."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's be real, it's likely that the engineer still doesn't know what the hell these "results" are or why the users care about them, but now at least it might be easier
to pinpoint what needs to change without having to find someone who might be more familiar with the domain.&lt;/p&gt;
&lt;p&gt;Finally, it bears mentioning that this approach doesn't solve all communication problems: The engineers will never be domain experts on the users' domain, so there will
always be questions, specially when creating new features as the developers will need to ask what the new classes/types will need for fields and metadata&lt;sup id="fnref:terminology"&gt;&lt;a class="footnote-ref" href="#fn:terminology"&gt;6&lt;/a&gt;&lt;/sup&gt;;
as for the users/product owners, the decoupling with the framework and the increasing reliance on native language constructs means that the number of technical details they need
to have an idea about becomes smaller, but not zero.&lt;/p&gt;
&lt;p&gt;Regardless, giving the team (both involved users and developers) a way to more fluidly discuss the product is a huge step forward in my book.&lt;/p&gt;
&lt;h2&gt;A caveat&lt;/h2&gt;
&lt;p&gt;All of that is pretty nice; however, the boilerplate the users don't care about still needs to exist &lt;em&gt;somewhere&lt;/em&gt;. Maybe abstracted and hidden away in specific modules or even
internal libraries that have their own repos, but finding and correcting leaky abstractions is a neverending battle, so those
discussions that ideally should be among engineers only might find their way in conversations with the users.&lt;/p&gt;
&lt;p&gt;Imagine a world where we didn't need to abstract the boilerplate for every project; that it simply didn't exist.
In that ideal world, we could just drop some native classes/structs/etc that contain all the business logic and &lt;em&gt;only&lt;/em&gt; the business logic to some tool(s),
and the tool would automagically take care of all the wiring needed for the application to reach the end user.&lt;/p&gt;
&lt;p&gt;I've given a lot of thought to such an idea, and maybe it's a pipe dream, but stranger things happen in this industry all the time.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:form"&gt;
&lt;p&gt;Let's say a form in some web site/application.&amp;#160;&lt;a class="footnote-backref" href="#fnref:form" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:float"&gt;
&lt;p&gt;Or, for that matter "what are strings and floats?" You never know!&amp;#160;&lt;a class="footnote-backref" href="#fnref:float" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:class"&gt;
&lt;p&gt;Or whatever construct your favorite language uses to group data, if any!&amp;#160;&lt;a class="footnote-backref" href="#fnref:class" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:lesscode"&gt;
&lt;p&gt;But hey, the code of the &lt;code&gt;Foo&lt;/code&gt; class itself got simpler.&amp;#160;&lt;a class="footnote-backref" href="#fnref:lesscode" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:clean"&gt;
&lt;p&gt;Yay for clean architecture!&amp;#160;&lt;a class="footnote-backref" href="#fnref:clean" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:terminology"&gt;
&lt;p&gt;In other words, some conversations for the developers to make the business rules clearer, which they then can express in the code!&amp;#160;&lt;a class="footnote-backref" href="#fnref:terminology" title="Jump back to footnote 6 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="Engineering"></category><category term="software"></category><category term="development"></category><category term="engineering"></category><category term="refactoring"></category></entry><entry><title>Practical refactoring: Abstractions</title><link href="https://luord.com/2022/03/31/abstracting/" rel="alternate"></link><published>2022-03-31T00:00:00-05:00</published><updated>2022-03-31T00:00:00-05:00</updated><author><name>Luis Orduz</name></author><id>tag:luord.com,2022-03-31:/2022/03/31/abstracting/</id><summary type="html">&lt;p&gt;Improving code by way of writing specific and self-describing abstractions.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In &lt;a href="https://luord.com/2022/02/28/refactoring/"&gt;my last post&lt;/a&gt;, we did a basic rundown of a very convoluted short algorithm to make
more explicit what was actually happening in it. That by itself goes a long way in improving how readable the code is, and thus makes it easier to maintain.
I've seen small improvements like that be welcome enthusiastically among different teams, but we can go
further.&lt;/p&gt;
&lt;p&gt;I remember a project I worked on where there was basically no separation of concerns between request
handling boilerplate, database connection boilerplate and actual business logic; everything was handled
within the same functions. It was a nightmare. I was hired to create some new APIs, but it took me just a
week of trying to create new handlers like that to decide that such was no way to live. I took it
upon myself to refactor that code. It bears reasserting that when refactoring, it's good—and often enough—to first reach for the lowest hanging fruit and in that code the easiest improvement was, of course,
separating code that dealt with different things into different functions, and calling those new functions from the old ones.&lt;/p&gt;
&lt;p&gt;Now, the small script we're going through does really only one thing, but that doesn't mean we can't
divide some responsibilities by way of abstracting away some code &lt;strong&gt;not&lt;/strong&gt; directly related to that
one thing. If we do this, we improve the code in at least three ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The algorithm itself becomes more immediately obvious; it's easier to understand what the code does.&lt;/li&gt;
&lt;li&gt;By abstracting the supporting/boilerplate code, we make it possible to reuse those same structures
somewhere else.&lt;/li&gt;
&lt;li&gt;This gives us a opportunity to bring the code in step with the domain: using names specific to what
we're doing instead of talking exclusively about basic data types.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The abstractions&lt;/h2&gt;
&lt;p&gt;Something I didn't mention in my last post is that the small method we're refactoring is actually part of a basic
genetic algorith I implemented for fun. Well, genetic algorithms deal with populations, so what if
instead dealing with "lists" of "strings", we create an actual &lt;code&gt;Population&lt;/code&gt; class/data type that does
what we need our populations to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Population&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__iter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not complicated, now we know that a population is a list, but one that when iterated just produces
two sublists: the two halves of the original. We &lt;em&gt;can&lt;/em&gt; improve it further, but this is enough for
what we need&lt;sup id="fnref:premature"&gt;&lt;a class="footnote-ref" href="#fn:premature"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We have our population, but our genetic algorithm is not of random things; it specifically looks for
individuals that match a given root individual, with the purpose of each generation to be closer to that individual.&lt;/p&gt;
&lt;p&gt;Let's give it a try:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Individual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__and__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch_s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ch_o&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch_o&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For our "model"&lt;sup id="fnref:genetic"&gt;&lt;a class="footnote-ref" href="#fn:genetic"&gt;2&lt;/a&gt;&lt;/sup&gt; we know that we need a specific type of string that when compared with another will
return the number of identical characters. So we do just that: create a &lt;code&gt;str&lt;/code&gt; subclass that
can "intersect" with other strings of the same type, and give a numeric value representing how well
they match.&lt;/p&gt;
&lt;p&gt;With these two abstractions alone, our method improves considerably:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_best_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# self.population: Population[Individual]&lt;/span&gt;
    &lt;span class="c1"&gt;# self.root: Individual&lt;/span&gt;
    &lt;span class="n"&gt;winners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;ind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ind&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;winners&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;winners&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I &lt;em&gt;like&lt;/em&gt; it. We get each member of the winner pair from half of the population, and notice how the code
doesn't show things we don't need to know to understand the steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How do we get each half of the population? That doesn't matter for understanding the algorithm; we just
need to know that we're getting a half. How that half is gathered is up to the implementtion in &lt;code&gt;Population&lt;/code&gt;
which we can check if we need to, or we could change it if we have to: Like instead of appending the
odd-positioned items to a half and the even-positioned to the other half, we could just literally split it
at the central index.&lt;/li&gt;
&lt;li&gt;To get the winner we're comparing how well is the intersection between each individual and the root
(&lt;code&gt;self.root&lt;/code&gt;, renamed from &lt;code&gt;self.word&lt;/code&gt;&lt;sup id="fnref:word"&gt;&lt;a class="footnote-ref" href="#fn:word"&gt;3&lt;/a&gt;&lt;/sup&gt;, in this case). How is that intersection calculated/found?
That's entirely an implementation detail, which again we can check and/or change in &lt;code&gt;Individual&lt;/code&gt;
if we have to. We could even make individuals a different type instead of strings, make a comparison
appropriate for that type, and we wouldn't need to change &lt;strong&gt;anything&lt;/strong&gt; in this method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that, at the end of my previous post, I said that we could use abstractions instead of relying
on comments to tell us what the data types are supposed to represent. There are still comments in this
piece of code... but that comment is just to note &lt;em&gt;what&lt;/em&gt; abstractions we're using, and is there just
for description purposes. In the full code it would be completely redundant, since the types of
&lt;code&gt;self.population&lt;/code&gt; and &lt;code&gt;self.root&lt;/code&gt; would already be defined somewhere else. Likely at the top of the class
this method belongs to.&lt;/p&gt;
&lt;p&gt;Nonetheless, we could still make those comments explicit in the code&lt;sup id="fnref:jokes"&gt;&lt;a class="footnote-ref" href="#fn:jokes"&gt;4&lt;/a&gt;&lt;/sup&gt; by way of, say, making them
arguments to the method. However, &lt;em&gt;that&lt;/em&gt; would no longer be a refactoring&lt;sup id="fnref:zero"&gt;&lt;a class="footnote-ref" href="#fn:zero"&gt;5&lt;/a&gt;&lt;/sup&gt;... But we can cheat a little
bit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_winners&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Population&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Individual&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Individual&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;winners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;ind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ind&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;winners&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;winners&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_best_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_winners&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Basically, we create a new &lt;em&gt;function&lt;/em&gt; that isn't necessarily tied to a class (note the lack of &lt;code&gt;self&lt;/code&gt;) and we
call that function from our method. This gives us the potential advantage of reusing that very same winner finding
logic somewhere else if the opportunity arises.&lt;/p&gt;
&lt;h2&gt;Hindsight&lt;/h2&gt;
&lt;p&gt;So there you have it, we improved the code, considerably, just by moving a few lines around and making
what we're doing and using more explicit. It goes without saying that this would help us communicate between the developers &lt;em&gt;and&lt;/em&gt; with
the product team (if any) much more easily.&lt;/p&gt;
&lt;p&gt;Ideally, in a good codebase, discussing the code and discussing the
business model would entail very similar expressions, as the code would just be a literal (with caveats of course) representation
of that business model. Hopefully this post showed how such a thing could be achieved.&lt;/p&gt;
&lt;p&gt;This is a subject I really, really like and I'm hoping to keep writing about it.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:premature"&gt;
&lt;p&gt;When refactoring, it's easy to get lost making increasingly minute improvements. We always
gotta remember that premature optimization is the root of all evil and that abstractions can easily
become unnecessary indirections. This is a fine line, and sometimes I don't spot it, so it's important to
remember that the goal is to make the code &lt;em&gt;more&lt;/em&gt; maintainable instead of perfect from the get go (which is impossible anyway). In an
actual product, it also helps to remember what I think as the rule zero of Software Engineering:
"fight for the users". If something we're doing won't improve the users'
experience in any meaningful way, it might be best left alone.&amp;#160;&lt;a class="footnote-backref" href="#fnref:premature" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:genetic"&gt;
&lt;p&gt;This genetic algorithm in particular just iterates over populations that increasingly
resemble the root word.&amp;#160;&lt;a class="footnote-backref" href="#fnref:genetic" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:word"&gt;
&lt;p&gt;In my opinion, the naming of the root individual as &lt;code&gt;self.word&lt;/code&gt; was a code smell born out of
the usage of basic data types instead of proper abstractions. It was a way to hint at the developer that
the comparison was between strings. Using proper types/classes, we no longer need to do that.&amp;#160;&lt;a class="footnote-backref" href="#fnref:word" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:jokes"&gt;
&lt;p&gt;After all, "code is for what, tests are for why and comments are for jokes" (which is also a
joke... or is it?)&amp;#160;&lt;a class="footnote-backref" href="#fnref:jokes" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:zero"&gt;
&lt;p&gt;If "improve the easiest thing first" is rule one of refactoring, then "do &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt;, under any
circumstance, change the contract" is rule zero of refactoring. To put it another way: if we have a good
test suite, a proper refactoring shouldn't change the result of &lt;em&gt;any&lt;/em&gt; test. Anything other than that is
an actual change in the behavior of the application/system, and it better have been agreed upon.&amp;#160;&lt;a class="footnote-backref" href="#fnref:zero" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="Engineering"></category><category term="software"></category><category term="development"></category><category term="craftsmanship"></category><category term="engineering"></category></entry><entry><title>Practical refactoring: 'clever' code</title><link href="https://luord.com/2022/02/28/refactoring/" rel="alternate"></link><published>2022-02-28T00:00:00-05:00</published><updated>2022-02-28T00:00:00-05:00</updated><author><name>Luis Orduz</name></author><id>tag:luord.com,2022-02-28:/2022/02/28/refactoring/</id><summary type="html">&lt;p&gt;Practical example of the problems with clever code and the benefits of refactoring.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Look at this code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_best_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Divide population in half.&lt;/span&gt;
&lt;span class="sd"&gt;    Pick the word closest to the matching word in each half.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;population_half&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;population_half&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm not gonna deny it, I liked writing it, I like that it is technically a single function call&lt;sup id="fnref:class"&gt;&lt;a class="footnote-ref" href="#fn:class"&gt;1&lt;/a&gt;&lt;/sup&gt;,
the usage of lambdas and the built-in Python functions used for handling, well, functions and iterables.
What can I say? It makes me feel "clever" because technically it's code that requires certain level of
familiarity with the language.&lt;/p&gt;
&lt;p&gt;It's also a total mess. I literally spent an entire afternoon explaining this "short" piece of code
to an experienced engineer who had already invested a few months getting familiar with Python.&lt;/p&gt;
&lt;p&gt;This code is me at my most self-indulgent and I'm well aware I would never have written this outside
of a &lt;a href="https://gitlab.com/luord/prototype"&gt;prototype meant only for me to play around&lt;/a&gt;. Code like this is &lt;strong&gt;not&lt;/strong&gt; meant to live in a
system worked at by more than one developer. It'd be a nightmare to maintain, as only the one who wrote
it could possibly understand it. Hell, I wrote this and I had to struggle a bit puzzling what it actually
did.&lt;/p&gt;
&lt;p&gt;In short, this code is ripe for improvement, which is exactly what I'm gonna do.&lt;/p&gt;
&lt;h2&gt;Unclear iterations&lt;/h2&gt;
&lt;p&gt;The first thing that jumps at me upon seeing this code is that there are three nested iterations in it,
but it's very difficult to tell which one is which or where each one ends.
An easy first fix is then relying less on built-in functions and making the iterations more explicit
via &lt;code&gt;for&lt;/code&gt; statements.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_best_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Get the words closest to the target in each half of the population&lt;/span&gt;
    &lt;span class="n"&gt;winners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WinnerPair&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;similarity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;char_word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;char_target&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;similarity&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chard_word&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;char_target&lt;/span&gt;
            &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;winners&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;winners&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looks quite different, doesn't it? It would seem to someone completely unfamiliar with Python that
I changed more than replacing the function calls with &lt;code&gt;for&lt;/code&gt;s, but that's truly &lt;em&gt;all&lt;/em&gt; I did:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first function was &lt;code&gt;map&lt;/code&gt; which is doing &lt;em&gt;something&lt;/em&gt; to both halves of the population.&lt;/li&gt;
&lt;li&gt;The second function was &lt;code&gt;max&lt;/code&gt; which is picking the highest according to &lt;em&gt;something&lt;/em&gt; in each word in
the population.&lt;/li&gt;
&lt;li&gt;The third function was &lt;code&gt;sum&lt;/code&gt; which is actually calculating that previous "something": In this case,
how similar is the current word with the target word.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I then reused &lt;code&gt;max&lt;/code&gt;, but it's now clearer what maximum value of what it's being picked. I will not lie:
I hesitated with leaving the &lt;code&gt;sum&lt;/code&gt; as it was as I felt that with the other replacements it was clear enough,
but then I saw the opportunity to further clarify that we were comparing the current word with the target word.
On the other hand, I did leave &lt;code&gt;zip&lt;/code&gt; as it was, as that one &lt;strong&gt;is&lt;/strong&gt; clear enough to me.&lt;sup id="fnref:craft"&gt;&lt;a class="footnote-ref" href="#fn:craft"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Aside: Someone with some familiarity with algorithm analysis might see three nested &lt;code&gt;for&lt;/code&gt; loops and
pale at the "cubic" complexity, but this function isn't iterating over the population input
(let's call it "n") multiple times. It's instead iterating only once over the &lt;em&gt;total characters&lt;/em&gt; input
(let's say "m"). In short: This iteration only visits each character in the population once.&lt;/p&gt;
&lt;p&gt;The word list (but not each word) &lt;em&gt;is&lt;/em&gt; visited twice because of the &lt;code&gt;max&lt;/code&gt; function, but since two is a
constant, it remains of linear complexity.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Anyhow, those "straightfoward" changes are enough to at least being able to tell what the function
is doing line by line, but it can be better.&lt;/p&gt;
&lt;p&gt;As it is, we're doing a bunch of operations over basic data types with a comment explaining what
those data types are supposed to represent. We could instead explicitly define our own abstractions over
those data types and let those abstractions tell us what they can or can't do, or how they should be
used.&lt;/p&gt;
&lt;p&gt;But I feel like that is interesting enough for its own post, so see you &lt;a href="https://luord.com/2022/03/31/abstracting/"&gt;in the next part&lt;/a&gt;!&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:class"&gt;
&lt;p&gt;Well, a function call wrapped in a class instantiation, but who's nitpicking?&amp;#160;&lt;a class="footnote-backref" href="#fnref:class" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:craft"&gt;
&lt;p&gt;I firmly think that Software Engineering &lt;strong&gt;is&lt;/strong&gt; engineering, and I have no problem calling myself "engineer" over, say, "craftsman", but there &lt;em&gt;is&lt;/em&gt; a subjective factor to some decisions.&amp;#160;&lt;a class="footnote-backref" href="#fnref:craft" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="Engineering"></category><category term="software"></category><category term="development"></category><category term="craftsmanship"></category></entry><entry><title>Creating stories from requirements</title><link href="https://luord.com/2020/02/01/stories/" rel="alternate"></link><published>2020-02-01T00:00:00-05:00</published><updated>2020-02-01T00:00:00-05:00</updated><author><name>Luis Orduz</name></author><id>tag:luord.com,2020-02-01:/2020/02/01/stories/</id><summary type="html">&lt;p&gt;On crafting actionable development stories from requirements&lt;/p&gt;</summary><content type="html">&lt;p&gt;A few weeks ago, one of my best friends came to me with an idea for an application. Unlike most (if not all) of my ideas, this one I actually believe to have potential so I asked him to prepare a list of requirements we could use to at least have a rough goal to aim for with this application. He delivered, and I was presented a good enough list... And I did nothing with it, I've been letting it sit there, all unfulfilled potential. But that changes now!&lt;/p&gt;
&lt;p&gt;I've been working as a software engineer for a while and that experience, the &lt;a href="https://agilemanifesto.org/"&gt;manifesto&lt;/a&gt;, what I remember from my college studies and what I've read regarding system design all point to the same conclusion: Having a fixed list of tasks and dedicating too much time to it is pointless because requirements change like waves in the sea.&lt;/p&gt;
&lt;p&gt;However, I still feel it's important to have a set of stories to work on, both to have a general idea of what the system is and how it's supposed to work, as well as for documenting progress; nothing kills motivation like the feeling you're not actually advancing towards your goal. Besides, we control the requirements in this case and, even though waves are fickle, &lt;a href="https://oceanservice.noaa.gov/facts/conveyor.html"&gt;the global conveyor belt&lt;/a&gt; is still a thing. Much like with that &lt;abbr title="sorry"&gt;overly elaborate metaphor&lt;/abbr&gt;, my goal is not to describe how every single detail in the application is supposed to work, but more a general description of each functionality, and general details on how to implement it.&lt;/p&gt;
&lt;p&gt;With that in mind, I'll show how I approached turning that requirements list written in the "as a user" format into stories that developers can actually work on. I'll forgo things like "story points" or "acceptance criteria" because not only are those restrictive, I'm doing this as a developer for developers—all two of us—and what I care about is what to do and how to do it. And that's also the reason I won't be using stuff like certain project management tool that most software development teams are familiar with whose name I won't mention, at the risk of summoning giant lizards.&lt;sup id="fnref:jira"&gt;&lt;a class="footnote-ref" href="#fn:jira"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In fact, this is a good point to mention &lt;a href="https://sourcehut.org/"&gt;sourcehut&lt;/a&gt;, Drew DeVault's cool set of tools for creating software that's very reminiscent of how the biggest open source projects are maintained. I'm going to be creating these stories using sourcehut's todo, which is to say: simple issues.&lt;/p&gt;
&lt;p&gt;Without further ado, let's take one of the requirements as example, one that is universal enough that I can use without risking IP: user accounts. This is an abbreviation of my friend's requirement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a user I can create an account in the application, with an email or using social networks (an email should be sent introducing the platform upon registration).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As mentioned, my goal with the stories is knowing what to do and how to do it, from a developer's perspective. Part by part, this is the architecture&lt;sup id="fnref:arch"&gt;&lt;a class="footnote-ref" href="#fn:arch"&gt;2&lt;/a&gt;&lt;/sup&gt; that this requirement defines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;abbr title="excuse me being captain obvious"&gt;application has a domain&lt;/abbr&gt;, and this domain includes the entity User because that's what we want to create.&lt;/li&gt;
&lt;li&gt;The application has some sort of data layer that the application interfaces with to store this User data.&lt;/li&gt;
&lt;li&gt;The application has adapters that &lt;em&gt;interface&lt;/em&gt; with third parties to retrieve the user data, social networks in this case.&lt;/li&gt;
&lt;li&gt;The application has the &lt;strong&gt;use case&lt;/strong&gt; "create user" that is called in two different ways: using email or using the adapters mentioned above.&lt;/li&gt;
&lt;li&gt;The application has an &lt;em&gt;interface&lt;/em&gt; that the user employs to send the email and other data, or to trigger the retrieval from third parties.&lt;/li&gt;
&lt;li&gt;The application has a second &lt;strong&gt;use case&lt;/strong&gt;, which is to send an automated email to the, well, email provided by the user upon successful creation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that rough outline, we have an idea for two or three stories, because at this stage is better to restrict stories to the number of use cases, or to the number of times all use cases are instantiated across the application.&lt;sup id="fnref:cases"&gt;&lt;a class="footnote-ref" href="#fn:cases"&gt;3&lt;/a&gt;&lt;/sup&gt; Of course, stories ultimately can involve &lt;em&gt;editing&lt;/em&gt; use cases too; the point is that we should make the stories about the application business rules whenever possible.&lt;/p&gt;
&lt;p&gt;The stories mentioned here are deliberately vague on the tech stack because I want this to be applicable for as many developers as possible.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Create User from email&lt;/dt&gt;
&lt;dd&gt;Create an Use Case that accepts raw data as well as a data repository&lt;sup id="fnref:postgres"&gt;&lt;a class="footnote-ref" href="#fn:postgres"&gt;4&lt;/a&gt;&lt;/sup&gt; and creates an instance of the User domain model using the raw data. It then passes this data to the repository for creation.&lt;/dd&gt;
&lt;dd&gt;Create an interface adapter&lt;sup id="fnref:flask"&gt;&lt;a class="footnote-ref" href="#fn:flask"&gt;5&lt;/a&gt;&lt;/sup&gt; that receives data (including email) submitted by an user and passes it to the aforementioned Use Case, alongside the database repository.&lt;/dd&gt;
&lt;dd&gt;Create a view&lt;sup id="fnref:web"&gt;&lt;a class="footnote-ref" href="#fn:web"&gt;6&lt;/a&gt;&lt;/sup&gt; that allows the user to submit this data to the adapter.&lt;sup id="fnref:server"&gt;&lt;a class="footnote-ref" href="#fn:server"&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Create User from third party&lt;/dt&gt;
&lt;dd&gt;Create an interface adapter that gathers data (including email) about an user sent from third parties&lt;sup id="fnref:dance"&gt;&lt;a class="footnote-ref" href="#fn:dance"&gt;8&lt;/a&gt;&lt;/sup&gt;. It passes this data to the create user Use Case alongside the database repository.&lt;sup id="fnref:clean"&gt;&lt;a class="footnote-ref" href="#fn:clean"&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/dd&gt;
&lt;dd&gt;Create a view that allows the user to go through the third party communication cycle.&lt;/dd&gt;
&lt;dt&gt;Send email on user creation&lt;/dt&gt;
&lt;dd&gt;Edit Create User Use case so that, upon successful storage, calls a new Use Case.&lt;/dd&gt;
&lt;dd&gt;This new Use Case is in charge of sending the body and addressee of an email to an email sender interface adapter.&lt;sup id="fnref:flow"&gt;&lt;a class="footnote-ref" href="#fn:flow"&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/dd&gt;
&lt;dd&gt;Create an interface adapter that uses the data produced by the  use case and sends it to an external queue&lt;sup id="fnref:celery"&gt;&lt;a class="footnote-ref" href="#fn:celery"&gt;11&lt;/a&gt;&lt;/sup&gt;, in the form of a message, that should send the email.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;These stories are somewhat vague, and that is deliberate. I don't want to restrict—and not only because I would be restricting myself—and details of implementation are what code reviews and tests are for. The one exception I make on not specifying implementation is regarding the tech stack itself: it's such a big decision that all developers are benefitted if the stack is clear&lt;sup id="fnref:caveat"&gt;&lt;a class="footnote-ref" href="#fn:caveat"&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The stories should only define a rough end goal, and I believe the ones I wrote here achieve that. Anyhow, I feel that this is a good first step and a general description of how we'll be working on this project.&lt;/p&gt;
&lt;p&gt;This is going to be part of a series, an idea born from a &lt;a href="https://flaviocopes.com/blog-seo/"&gt;great article on blogging&lt;/a&gt; that I read the other day, which inspired me to write more and gave me the idea of how to actually write: I'm going to push myself into working on this application (and refactoring an older one, with another friend) so that I have material to write for this blog, and I can use the will to write for the blog as incentive to work on those projects, killing two birds with one stone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: Everything I'm writing in these series is my interpretation and general idea of a good process. Anything (or everything) I write might be entirely wrong and, in such event, I &lt;abbr title="read: beg"&gt;encourage&lt;/abbr&gt; you to correct me in the comments.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:jira"&gt;
&lt;p&gt;Just FYI, I don't hate it, it can be a great tool, as long as it isn't drowned in the bastardization of scrum, which I don't hate either... as long as it's used as a guideline instead of a forced two-week waterfall grind.&amp;#160;&lt;a class="footnote-backref" href="#fnref:jira" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:arch"&gt;
&lt;p&gt;I'm trying to describe this within the terms of the &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html"&gt;clean architecture&lt;/a&gt;. Results may vary.&amp;#160;&lt;a class="footnote-backref" href="#fnref:arch" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:cases"&gt;
&lt;p&gt;And debatably at any stage. I've seen projects where stories are created for everything, even changing the color of a button to a slightly differen shade of blue. YMMV on advantages and disadvantages of that practice.&amp;#160;&lt;a class="footnote-backref" href="#fnref:cases" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:postgres"&gt;
&lt;p&gt;I'm a postgreSQL user.&amp;#160;&lt;a class="footnote-backref" href="#fnref:postgres" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:flask"&gt;
&lt;p&gt;Probably a function that would retrieve the data in a given format, create a domain entity object from it that would then pass this object into the database adapter, probably a SQLAlchemy model. This function itself would be called from a &lt;a href="https://www.palletsprojects.com/p/flask/"&gt;flask&lt;/a&gt; endpoint.&amp;#160;&lt;a class="footnote-backref" href="#fnref:flask" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:web"&gt;
&lt;p&gt;I've worked as a full stack web developer almost exclusively, so in here I'm thinking of a form, created either from a JS framework or just a simple web form.&amp;#160;&lt;a class="footnote-backref" href="#fnref:web" title="Jump back to footnote 6 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:server"&gt;
&lt;p&gt;Of course, nobody is forced to use a client-server system, but it's what I'll use.&amp;#160;&lt;a class="footnote-backref" href="#fnref:server" title="Jump back to footnote 7 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:dance"&gt;
&lt;p&gt;Probably something like &lt;a href="https://flask-dance.readthedocs.io/en/latest/"&gt;flask-dance&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:dance" title="Jump back to footnote 8 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:clean"&gt;
&lt;p&gt;Remember not to pollute the business rules or the entities with data or steps specific to any third party.&amp;#160;&lt;a class="footnote-backref" href="#fnref:clean" title="Jump back to footnote 9 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:flow"&gt;
&lt;p&gt;Remember not to break flow of control, these business rules don't care how the database signals success or how the sender adapter sends the message. But that's an implementation detail that should be discussed in review.&amp;#160;&lt;a class="footnote-backref" href="#fnref:flow" title="Jump back to footnote 10 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:celery"&gt;
&lt;p&gt;Probably rabbitmq through Celery.&amp;#160;&lt;a class="footnote-backref" href="#fnref:celery" title="Jump back to footnote 11 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:caveat"&gt;
&lt;p&gt;Everything beyond that (meaning &lt;em&gt;how&lt;/em&gt; the stack is used) is outside the scope of the story. That's for tests and reviews.&amp;#160;&lt;a class="footnote-backref" href="#fnref:caveat" title="Jump back to footnote 12 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="Engineering"></category><category term="architecture"></category><category term="design"></category><category term="model"></category><category term="musings"></category><category term="craftsmanship"></category></entry></feed>