<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-09-29T22:43:40+02:00</updated><id>/feed.xml</id><title type="html">Opinions, opinions!</title><subtitle>A blog about the craft of coding</subtitle><author><name>Fabio Sangiovanni</name></author><entry><title type="html">About Scrum</title><link href="/agile/2025/09/29/about-scrum.html" rel="alternate" type="text/html" title="About Scrum" /><published>2025-09-29T00:00:00+02:00</published><updated>2025-09-29T00:00:00+02:00</updated><id>/agile/2025/09/29/about-scrum</id><content type="html" xml:base="/agile/2025/09/29/about-scrum.html"><![CDATA[<p>On 20/09/2025 I <a href="https://bsky.app/profile/sang.io/post/3lzbhg223pc22">posted</a>
on Bluesky:</p>

<blockquote>
  <p>Friendly reminder that scrum is bad, and that if you’re forcing scrum on your
team you should feel bad.</p>
</blockquote>

<p>I was then (rightfully) asked for what would be a more sensible approach:</p>

<blockquote>
  <p>Curious to hear about friendly approaches to leading teams that should make
us feel good.</p>
</blockquote>

<p>My answer:</p>

<blockquote>
  <p>I don’t lead teams, but I can tell you what worked for me as a dev: no
estimates, no ceremonies, no sprints, tasks as small as possible, async written
comms, thorough code reviews, changes shipped right after merge. Assume good
faith when people struggle to deliver (slackers will be slackers).</p>
</blockquote>

<p>The conversation went on:</p>

<blockquote>
  <p>Sounds good. Who writes the tasks and when? How do they decide if a task will
be worth the time it takes build?</p>
</blockquote>

<p>Absolutely reasonable (and important) questions!</p>

<p>This was my reply, which I think is the core takeaway from this blog post, and
probably a hill I will die on:</p>

<blockquote>
  <p>Q1: Everybody does, all the time. Product folks (if they’re technical
enough), devs, or both. But I think we have better tools than ceremonies for
this.
My preference goes to long form write-ups for discussion/decision making,
followed by the actual breakdown of tasks into cards. The analysis process
makes sense even as a meta-task on its own.</p>
</blockquote>

<blockquote>
  <p>Q2: I think this is not a good question we should ask ourselves, the reason
being: product development should follow a roadmap. It should not be led by
opportunity. It should be led by a vision.<br />
So, a better approach would be asking Product: “what are the non-negotiable
items devs must implement at this point in time?” The ball is in their court
now. It’s Product’s actual job to define priorities depending on what they
believe delivers the best value to users.<br />
What the dev team should get from them is work that must be done no matter
what. No need to weigh it against how much time it will take: it will be worth
doing *by definition*. Devs of course can be flexible and give some sort of
hints in terms of “it’s little work”/”it’s a lot of work”.<br />
But that should not be necessary.</p>
</blockquote>

<p>Until next time!</p>]]></content><author><name>Fabio Sangiovanni</name></author><category term="agile" /><summary type="html"><![CDATA[On 20/09/2025 I posted on Bluesky:]]></summary></entry><entry><title type="html">Action Mailer and Phlex views</title><link href="/rails/2025/09/29/action-mailer-and-phlex-views.html" rel="alternate" type="text/html" title="Action Mailer and Phlex views" /><published>2025-09-29T00:00:00+02:00</published><updated>2025-09-29T00:00:00+02:00</updated><id>/rails/2025/09/29/action-mailer-and-phlex-views</id><content type="html" xml:base="/rails/2025/09/29/action-mailer-and-phlex-views.html"><![CDATA[<p>I think I might have come up with a design to use Phlex views from Rails’
Action Mailer in a way I’m happy with. This turned out to be quite simple to
code in the end, but since ✨simplicity is hard✨, it took me a bit of
reasoning to get there, so I want to share the results!</p>

<p>First of all, the interface. I wanted to be able to define an email message
like this:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">Views::Mailers::Users::Welcome</span>
  <span class="kp">include</span> <span class="no">Mailable</span>

  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="nc">Html</span> <span class="o">&lt;</span> <span class="no">Phlex</span><span class="o">::</span><span class="no">HTML</span>
    <span class="k">def</span> <span class="nf">view_template</span>
      <span class="nb">p</span> <span class="p">{</span> <span class="s2">"Welcome, </span><span class="si">#{</span><span class="vi">@user</span><span class="p">.</span><span class="nf">name</span><span class="si">}</span><span class="s2">!"</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">class</span> <span class="nc">Text</span> <span class="o">&lt;</span> <span class="no">Phlex</span><span class="o">::</span><span class="no">HTML</span>
    <span class="k">def</span> <span class="nf">view_template</span>
      <span class="n">plain</span> <span class="s2">"Welcome, </span><span class="si">#{</span><span class="vi">@user</span><span class="p">.</span><span class="nf">name</span><span class="si">}</span><span class="s2">!"</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>I was inspired by <a href="https://camillovisini.com/coding/phlex-for-rails-emails-action-mailer-without-erb" target="_blank">this</a>
blog post at first, but my final implementation ended up diverging quite
heavily from what described there.</p>

<p>All mail parts are Phlex components namespaced to a single module representing
the whole message. This allows for convenient editing of content when composing
multipart emails.</p>

<p>On top of this, I wanted every method defined in the main module to be shared
and available to the nested component classes, to allow for DRYer code (super
useful e.g. for <code class="language-plaintext highlighter-rouge">initialize</code>). Lastly, I wanted every component to be rendered
inside a default (but overridable) implicit layout.</p>

<p>The other side of the message interface is the shape of methods available to
calling code (e.g. <code class="language-plaintext highlighter-rouge">ApplicationMailer</code> subclasses). My idea for that was
something like this:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">UsersMailer</span> <span class="o">&lt;</span> <span class="no">ApplicationMailer</span>
  <span class="k">def</span> <span class="nf">welcome</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="n">mail</span><span class="p">(</span><span class="ss">to: </span><span class="n">user</span><span class="p">.</span><span class="nf">email_address</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
      <span class="n">message</span> <span class="o">=</span> <span class="no">Views</span><span class="o">::</span><span class="no">Mailers</span><span class="o">::</span><span class="no">Users</span><span class="o">::</span><span class="no">Welcome</span>
      <span class="nb">format</span><span class="p">.</span><span class="nf">text</span> <span class="p">{</span> <span class="n">render</span> <span class="n">message</span><span class="p">.</span><span class="nf">text</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span> <span class="p">}</span>
      <span class="nb">format</span><span class="p">.</span><span class="nf">html</span> <span class="p">{</span> <span class="n">render</span> <span class="n">message</span><span class="p">.</span><span class="nf">html</span><span class="p">(</span><span class="n">user</span><span class="p">:)</span> <span class="p">}</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>No surprises here, just module methods named along the parts content type,
mirroring the ones exposed by the <code class="language-plaintext highlighter-rouge">format</code> parameter of the block. Behind the
scenes, they act as factories for mail part instances, so what they do is
basically just forward arguments and instantiate/return Phlex components.</p>

<p>Bonus point: the module exposes methods only for actually defined components
(e.g. no <code class="language-plaintext highlighter-rouge">Html</code> component &gt; no <code class="language-plaintext highlighter-rouge">html</code> method). I like to be strict.</p>

<p>So, how did everything fit together? Well, all of the above is implemented by a
&lt;30 lines concern that I called <code class="language-plaintext highlighter-rouge">Mailable</code>:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">Mailable</span>
  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>

  <span class="n">class_methods</span> <span class="k">do</span>
    <span class="kp">private</span>
      <span class="k">def</span> <span class="nf">const_added</span><span class="p">(</span><span class="n">const_name</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">unless</span> <span class="n">part</span> <span class="o">=</span> <span class="n">part_by_name</span><span class="p">(</span><span class="n">const_name</span><span class="p">)</span>
        <span class="n">part</span><span class="p">.</span><span class="nf">include</span> <span class="nb">self</span>
        <span class="n">define_singleton_method</span><span class="p">(</span><span class="n">const_name</span><span class="p">.</span><span class="nf">downcase</span><span class="p">)</span> <span class="p">{</span> <span class="o">|**</span><span class="n">kw</span><span class="o">|</span> <span class="n">part</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="o">**</span><span class="n">kw</span><span class="p">)</span> <span class="p">}</span>
        <span class="n">private_constant</span> <span class="n">const_name</span>
      <span class="k">end</span>

      <span class="k">def</span> <span class="nf">part_by_name</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">unless</span> <span class="nb">name</span><span class="p">.</span><span class="nf">in?</span> <span class="sx">%i[ Html Text ]</span>
        <span class="n">part</span> <span class="o">=</span> <span class="nb">const_get</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="kp">false</span><span class="p">)</span>
        <span class="n">part</span> <span class="k">if</span> <span class="n">part</span><span class="p">.</span><span class="nf">instance_of?</span><span class="p">(</span><span class="no">Class</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">part</span> <span class="o">&lt;</span> <span class="no">Phlex</span><span class="o">::</span><span class="no">HTML</span>
      <span class="k">end</span>
  <span class="k">end</span>

  <span class="kp">private</span>
    <span class="k">def</span> <span class="nf">around_template</span>
      <span class="n">part_name</span> <span class="o">=</span> <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">demodulize</span>
      <span class="n">layout</span> <span class="o">=</span> <span class="s2">"Components::Layouts::Mailers::</span><span class="si">#{</span><span class="n">part_name</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">constantize</span>
      <span class="n">render</span> <span class="n">layout</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="k">super</span> <span class="p">}</span>
    <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>It works by tapping into the <code class="language-plaintext highlighter-rouge">const_added</code> hook and doing some metaprogramming
magic in order to make the including module a little bit smarter.</p>

<p>As of now it supports plain text and html content types, but the general idea
can be extended. The code should be quite easy to follow, but if you have
questions, please <a href="https://bsky.app/profile/sang.io" target="_blank">ask</a>
away.</p>

<p>I’m really happy about how little code went into the concern, and how tidy and
self contained the overall solution feels like (at least to me).</p>

<p>I hope this helps! And if you have any opinions about this approach and want to
discuss them, I’m <a href="https://bsky.app/profile/sang.io" target="_blank">here</a> to
chat 🎉</p>

<p>Until next time!</p>]]></content><author><name>Fabio Sangiovanni</name></author><category term="rails" /><summary type="html"><![CDATA[I think I might have come up with a design to use Phlex views from Rails’ Action Mailer in a way I’m happy with. This turned out to be quite simple to code in the end, but since ✨simplicity is hard✨, it took me a bit of reasoning to get there, so I want to share the results!]]></summary></entry></feed>