<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="https://timmo.immo/rss-style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-gb">
  <id>https://timmo.immo/blog</id>
  <title>Timmo's Website</title>
  <updated>2023-03-02T00:00:00+01:00</updated>
  <subtitle>The website of Timmo Verlaan.</subtitle>
  <link href="https://timmo.immo/feed" rel="self" type="application/atom+xml"/>
  <link href="https://timmo.immo/blog" rel="alternate" type="text/html"/>
  <icon>/favicon.ico</icon>
  <author>
    <name>Timmo Verlaan</name>
    <email>timmo@timmo.immo</email>
    <uri>https://timmo.immo</uri>
  </author>
  <rights> © 2024 Timmo Verlaan </rights>
  <entry>
    <id>https://timmo.immo/blog/delete-file-after-sending</id>
    <link href="https://timmo.immo/blog/delete-file-after-sending" rel="alternate" type="text/html"/>
    <title>Delete File After Sending in Phoenix</title>
    <updated>2023-03-02T00:00:00+01:00</updated>
    <published>2023-03-02T00:00:00+01:00</published>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml"><p>
I have a project where the client can do a <code class="inline">POST</code> request to receive back a <a href="https://en.wikipedia.org/wiki/Tar_(computing)" title="Tar on Wikipedia">tarball</a>. On each <code class="inline">POST</code>, a new tarball will be generated so that after sending the tarball it can safely be removed. The tarball format is chosen to group a bunch of files in a single download. It’s a relatively small file, however, I want to avoid running out of disk space, because even small files accumulate over time. Given these constraints, I’ve tried a few different solutions I want to share with you. I think it can prove helpful in choosing the right solution for your specific use case.</p>
<h2>
Keep the file in memory</h2>
<p>
An easy way out of this pickle is to only write the contents of the file in memory. With Phoenix, you can use <a href="https://hexdocs.pm/phoenix/Phoenix.Controller.html#send_download/3" title="Documentation for send_download/3"><code class="inline">send_download/3</code></a> to send the filename plus binary contents. It would look something like this:</p>
<pre><code class="makeup elixir"><span class="c1"># %Plug.Conn{} = conn</span><span class="w">
</span><span class="n">send_download</span><span class="p" data-group-id="6856600748-1">(</span><span class="n">conn</span><span class="p">,</span><span class="w">
  </span><span class="p" data-group-id="6856600748-2">{</span><span class="ss">:binary</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;These are my file contents&quot;</span><span class="p" data-group-id="6856600748-2">}</span><span class="p">,</span><span class="w">
  </span><span class="ss">filename</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;the_filename.txt&quot;</span><span class="p" data-group-id="6856600748-1">)</span></code></pre>
<p>
There are a few caveats with this approach. Large binaries will be stored off-heap so it won’t be a problem for your process, but it still won’t scale to very large sizes or many concurrent requests. Another problem can be when you don’t control content generation. In my case, I depend on <code class="inline">:erl_tar</code> which only allows writing the archive to a file. I’d have to read the file after creation into memory, delete the file safely, and then send it to the client. Eg:</p>
<pre><code class="makeup elixir"><span class="n">binary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">File</span><span class="o">.</span><span class="n">read!</span><span class="p" data-group-id="0887500012-1">(</span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="0887500012-1">)</span><span class="w">
</span><span class="ss">:ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">File</span><span class="o">.</span><span class="n">rm!</span><span class="p" data-group-id="0887500012-2">(</span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="0887500012-2">)</span><span class="w">
</span><span class="n">send_download</span><span class="p" data-group-id="0887500012-3">(</span><span class="n">conn</span><span class="p">,</span><span class="w">
  </span><span class="p" data-group-id="0887500012-4">{</span><span class="ss">:binary</span><span class="p">,</span><span class="w"> </span><span class="n">binary</span><span class="p" data-group-id="0887500012-4">}</span><span class="p">,</span><span class="w">
  </span><span class="ss">filename</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;my_archive_20230302.tar.gz&quot;</span><span class="p" data-group-id="0887500012-3">)</span></code></pre>
<h2>
Delete the file after sending, or so I thought</h2>
<p>
This assumes Phoenix with <code class="inline">Plug.Cowboy</code> as the adapter. The idea is simple, you send the file, you delete the file, and you return <code class="inline">%Plug.Conn{}</code> in your controller.</p>
<pre><code class="makeup elixir"><span class="n">conn</span><span class="w"> </span><span class="o">=</span><span class="w">
  </span><span class="n">conn</span><span class="w">
  </span><span class="o">|&gt;</span><span class="w"> </span><span class="n">put_resp_header</span><span class="p" data-group-id="8830186476-1">(</span><span class="s">&quot;content-disposition&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">~s(attachment; filename=my_archive_20230302.tar.gz)</span><span class="p" data-group-id="8830186476-1">)</span><span class="w">
  </span><span class="o">|&gt;</span><span class="w"> </span><span class="n">send_file</span><span class="p" data-group-id="8830186476-2">(</span><span class="mi">200</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="8830186476-2">)</span><span class="w">

</span><span class="ss">:ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">File</span><span class="o">.</span><span class="n">rm!</span><span class="p" data-group-id="8830186476-3">(</span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="8830186476-3">)</span><span class="w">
</span><span class="n">conn</span></code></pre>
<p>
When I was testing this locally I found that sometimes I didn’t receive the file back. <em>What is going on here?</em></p>
<p>
Plug’s <a href="https://hexdocs.pm/plug/Plug.Conn.html#send_file/5" title="Documentation for send_file/5"><code class="inline">send_file/3</code></a> passes the command down the chain to Cowboy’s <code class="inline">:cowboy_req.reply/4</code> and there we find the culprit. The filename is passed from the ‘replying’-process (my controller) to the ‘requesting’-process (the request worker) with <code class="inline">send/2</code>. The function in <code class="inline">:cowboy_req</code> that does this is even called <a href="https://github.com/ninenines/cowboy/blob/2.9.0/src/cowboy_req.erl#L921" title="Link to cowboy_req cast/2"><code class="inline">cast/2</code></a>. This means that my controller is racing with the Cowboy process and when it’s faster the file is gone before it’s read and sent back.</p>
<p>
You can work around this by monitoring the requesting process and removing the file only when that process exits. This means you have to interface with Plug’s internals. You will also find that the ‘requesting’-process is reused and only recycled after a while. The code would look like this:</p>
<pre><code class="makeup elixir"><span class="p" data-group-id="7517829258-1">{</span><span class="c">_adapter</span><span class="p">,</span><span class="w"> </span><span class="n">adapter_data</span><span class="p" data-group-id="7517829258-1">}</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">conn</span><span class="o">.</span><span class="n">adapter</span><span class="w">
</span><span class="n">spawn</span><span class="p" data-group-id="7517829258-2">(</span><span class="k" data-group-id="7517829258-3">fn</span><span class="w"> </span><span class="o">-&gt;</span><span class="w">
  </span><span class="n">ref</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">Process</span><span class="o">.</span><span class="n">monitor</span><span class="p" data-group-id="7517829258-4">(</span><span class="n">adapter_data</span><span class="o">.</span><span class="n">pid</span><span class="p" data-group-id="7517829258-4">)</span><span class="w">

  </span><span class="k">receive</span><span class="w"> </span><span class="k" data-group-id="7517829258-5">do</span><span class="w">
    </span><span class="p" data-group-id="7517829258-6">{</span><span class="ss">:DOWN</span><span class="p">,</span><span class="w"> </span><span class="o">^</span><span class="n">ref</span><span class="p">,</span><span class="w"> </span><span class="ss">:process</span><span class="p">,</span><span class="w"> </span><span class="bp">_</span><span class="p">,</span><span class="w"> </span><span class="bp">_</span><span class="p" data-group-id="7517829258-6">}</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="ss">:ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">File</span><span class="o">.</span><span class="n">remove!</span><span class="p" data-group-id="7517829258-7">(</span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="7517829258-7">)</span><span class="w">
  </span><span class="k" data-group-id="7517829258-5">end</span><span class="w">
</span><span class="k" data-group-id="7517829258-3">end</span><span class="p" data-group-id="7517829258-2">)</span><span class="w">

</span><span class="n">conn</span><span class="w">
</span><span class="o">|&gt;</span><span class="w"> </span><span class="n">put_resp_header</span><span class="p" data-group-id="7517829258-8">(</span><span class="s">&quot;content-disposition&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">~s(attachment; filename=my_archive_20230302.tar.gz)</span><span class="p" data-group-id="7517829258-8">)</span><span class="w">
</span><span class="o">|&gt;</span><span class="w"> </span><span class="n">send_file</span><span class="p" data-group-id="7517829258-9">(</span><span class="mi">200</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="7517829258-9">)</span></code></pre>
<h2>
Delete the file after sending, Bandit-edition</h2>
<p>
<a href="https://hexdocs.pm/bandit/Bandit.html" title="Documentation for Bandit">Bandit</a> is a relatively new HTTP server that is compatible with Plug. I’ve checked the code path that deals with sending files and found out it does send the file from the process <em>I</em> am controlling. This means that my earlier code <strong>Just Works™️</strong> when I configure Bandit as my Phoenix adapter. No need for monitors or reading files into memory. In addition, the code now seems to do what I think it would be doing when reading it. For clarity the code looks like this:</p>
<pre><code class="makeup elixir"><span class="n">conn</span><span class="w"> </span><span class="o">=</span><span class="w">
  </span><span class="n">conn</span><span class="w">
  </span><span class="o">|&gt;</span><span class="w"> </span><span class="n">put_resp_header</span><span class="p" data-group-id="7812815853-1">(</span><span class="s">&quot;content-disposition&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">~s(attachment; filename=my_archive_20230302.tar.gz)</span><span class="p" data-group-id="7812815853-1">)</span><span class="w">
  </span><span class="o">|&gt;</span><span class="w"> </span><span class="n">send_file</span><span class="p" data-group-id="7812815853-2">(</span><span class="mi">200</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="7812815853-2">)</span><span class="w">

</span><span class="ss">:ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nc">File</span><span class="o">.</span><span class="n">rm!</span><span class="p" data-group-id="7812815853-3">(</span><span class="s">&quot;my_archive.tar.gz&quot;</span><span class="p" data-group-id="7812815853-3">)</span><span class="w">
</span><span class="n">conn</span></code></pre>
<p>
As I said, Bandit is relatively new and doesn’t (yet) have the proven track record of Cowboy, but it has a promising story and from what I gather works fine.</p>
<h2>
Alternatives?</h2>
<p>
Did I miss anything or do you have a different approach that deals with this situation? Do let me know! I’d be glad to hear about it.</p>
</div>
    </content>
    <summary type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">Sometimes you want to delete a file after sending it with Phoenix (or any other webframework). How can you do that reliably?</div>
    </summary>
  </entry><entry>
    <id>https://timmo.immo/blog/a-war-story</id>
    <link href="https://timmo.immo/blog/a-war-story" rel="alternate" type="text/html"/>
    <title>A War Story - From Failure to Success</title>
    <updated>2021-01-05T00:00:00+01:00</updated>
    <published>2021-01-05T00:00:00+01:00</published>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml"><p>
<em>This is the written form of a presentation I gave at <a href="https://www.codesync.global/conferences/code-beam-sto/">Code BEAM V 2020</a>. “BEAM” is the name of the <a href="https://en.wikipedia.org/wiki/BEAM%5F%28Erlang%5Fvirtual%5Fmachine%29">Erlang VM</a>.</em></p>
<p>
There are quite a few technical presentations at the Code BEAM conferences. Wonderful stories about advancing the BEAM, adding different languages to the BEAM, creating new libraries in the ecosystem. The list goes on and on. I’m sharing a different story with you. A war story. About going from failure to success.</p>
<h2>
Failures</h2>
<p>
It’s <strong>OK</strong> to have failures. It’s <strong>OK</strong> to not know how to go about it. There is no <strong>ready-to-use</strong> solution for your <strong>one-of-a-kind</strong> problem. Handling failure is about fault-tolerance as a team and as a company. We make mistakes and then we fix them. This is not the effort of a single person. It’s the effort of everyone involved. I’m sharing it from my perspective. There is no universal truth.</p>
<p>
I will not cover Erlang/Elixir anti-patterns or how to design applications for the BEAM. It is mostly about things you should try to avoid and how <em>we</em> went about solving our <em>own</em> issues.</p>
<h2>
Background</h2>
<p>
We needed to replace the existing communication platform (based on <a href="https://www.asterisk.org" title="Open Source PBX Software">Asterisk</a> and <em>a lot</em> of PHP scripts). A communication platform is a perfect fit for the BEAM. At least, that is what we thought. Picking the proper tools and software is a good start, but there is still a long road ahead.</p>
<p>
At that time we weren’t (yet) a group of experienced Erlang or Elixir developers. We designed and created lots of components based on how we thought the BEAM would behave and what we thought our users needed. We were doing a lot of tests and benchmarks to confirm our beliefs. But getting results from a <em>“lab”</em> environment can be quite different from the real world.</p>
<h2>
The First Two Years</h2>
<p>
We started building the platform in Elixir. The company already had some experience with Erlang and running the BEAM in production. I started at zero. I picked up the famous book of Joe Armstrong, <a href="https://pragprog.com/titles/jaerlang2/programming-erlang-2nd-edition/">Programming Erlang</a>. It immediately clicked for me. I recommend this book to anyone that wants to learn Erlang or get a basic understanding of the Erlang Runtime.</p>
<p>
At the time we favored Elixir over Erlang for its macro programming capabilities and its syntax. There is a lot more to it, but that would make a different story ;-).</p>
<p>
For the first two years, we’ve developed without any users using it. You can imagine that developing a platform for this long by a team for which Elixir is relatively new was a big gamble. I remember a couple of interesting conversations and promises about that with a key stakeholder during one of our Christmas gatherings.</p>
<p>
We created a proof of concept. Then we rewrote it. Then we rewrote it once more. Each iteration showed us something and improved our understanding of how to design it. After the third iteration, we were satisfied with the trade-offs we made. Although only the last iteration eventually was exposed to users.</p>
<h2>
Beta Launch</h2>
<p>
In the third year, we announced and launched the new platform. We had a big party and some really shiny features. It allowed our users to test-drive the platform. We got our first real user feedback. We also got asked for a ton of features we hadn’t ported yet from the old platform.</p>
<p>
The following year we continued developing the platform. Basic functionality broke at times, but we fixed each bug quickly. The shiny new features seemed to always work. Our users weren’t entirely happy about that balance as they wanted to run their businesses using this platform. Understandably, that requires basic functionality to be super stable.</p>
<h2>
Go-Live</h2>
<p>
After another year of development, we ripped off the beta label. Three years of development before we got actual businesses operating on our platform. This resulted in quite different feedback than in the year before the go-live. It didn’t take long before we got difficult and complex issues to solve.</p>
<p>
We soon realized that our technical design wasn’t lined up with the behavior that our users were expecting. It was even a bit unpredictable from their point of view. The behavior was intertwined with our technical design. This meant that altering the behavior required quite a big refactoring. We had some workarounds in place to reduce the friction for our users. It did require a month or two to make the technical design follow the expected behavior. After we migrated the data we finally got predictable behavior from our users’ perspective.</p>
<p>
While working these first months we also reflected on our development process. A lot of work has been done on “islands”. The level of shared understanding was therefore a bit low. We started doing more pairing and better peer review. Not only for approvals but for knowledge sharing. This helped everyone to better understand the patchwork we created.</p>
<h2>
An Eventful Year</h2>
<p>
The first months had settled in and we felt confident about our progress. We never predicted the year ahead of us would be so eventful.</p>
<p>
For resiliency reasons we were running multiple nodes and because information between nodes needed to be shared in realtime we were using Erlang clustering. After the initial period, we started getting single node outages. The VM was still running but our application didn’t function anymore. The other cluster nodes were not affected at the same time. Investigation showed that the application was no longer able to execute database queries. We updated our libraries. Reduced amount of queries. Upgraded Erlang. We’ve never found out the exact root-cause, but the issue did disappear eventually.</p>
<p>
During the same time, we had issues with a specific application in our <a href="https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html#umbrella-projects">umbrella application</a>. Technically it was a bug that started to appear under certain timing conditions. If we wouldn’t have developed on islands, we might have found it sooner. Unraveling the timing issue was a delicate task and revealed a couple of related bugs. By joining our efforts it became easier to manage and work through all these bugs.</p>
<p>
We made some obvious mistakes as well. Things that are easily preventable but given our focus just were not in the picture. Our database ran out of space unexpectedly, our HTTPS certificates expired and to top it all off we accidentally dropped all tables in our database. I hear you thinking: those are bad operations practices. You are right, but humans make mistakes and we learn from them. We switched our RDBMS and migrated to different certificate handling. Not because it was better, but because it allowed our infrastructure team to better support the architecture. I must add that <a href="https://hexdocs.pm/ecto/Ecto.html">Ecto</a> helped a lot in this transition as we didn’t have to change much.</p>
<p>
Switching the database was good from an operational perspective. There was a big difference in how both databases responded to our workload. We had to finetune a lot. This ranged from changing how we queried our data, adding indexes, increasing performance by adding hardware and caching data in the application.</p>
<p>
We were out of the woods for a little while. Our hard work was paying off and our customers were noticing it. The platform was still steadily growing. Three months passed without big issues.</p>
<p>
We had a couple of platform-wide outages. This meant our clustered platform had more impact than we wanted and wasn’t as resilient as we wanted it to be. It was kind of a snowball effect that happened. We made the snowball much smaller to decrease the impact and we took some precautions to prevent the snowball altogether. These two things combined allowed us to prevent and otherwise control the behavior. A big relief.</p>
<h2>
COVID-19</h2>
<p>
Things were looking promising. I guess we all felt that way. Especially in hindsight. The year could have been so much better.</p>
<p>
A part of our communication platform was a videoconferencing solution. It wasn’t widely used and we considered dropping it earlier. Suddenly it peaked in traffic and it was the new hotness of the platform. Unfortunately for us, we hadn’t been paying much attention to it. Bottlenecks became clear quite soon.</p>
<p>
We had a single process (<a href="http://erlang.org/doc/man/gen_server.html">gen_server</a>) for signaling from our application to our media gateway. It worked properly until then but it was not ready for scale. We refactored this into multiple processes to remove the bottleneck. If COVID-19 wouldn’t have happened this would probably not have happened.</p>
<h2>
What Have We Learned</h2>
<p>
Failures are easy. They come in many shapes and for many reasons. Each one of them requires a different solution. Working together allows you to find the best one. You have to do the work to solve them. There are no short-cuts to success!</p>
<p>
I feel very grateful for having been a part of an amazing team while facing these challenges. I’m thankful for the support I received from the team and the company.</p>
</div>
    </content>
    <summary type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">Developing a communication platform is a perfect fit for the BEAM. Learning its characteristics requires users and a significant load. We developed for three years before we added users.</div>
    </summary>
  </entry><entry>
    <id>https://timmo.immo/blog/protocols-beware</id>
    <link href="https://timmo.immo/blog/protocols-beware" rel="alternate" type="text/html"/>
    <title>Protocols Are Powerful but Beware</title>
    <updated>2020-11-07T00:00:00+01:00</updated>
    <published>2020-11-07T00:00:00+01:00</published>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml"><p>
Elixir protocols are a powerful mechanism for achieving polymorphism. But with great power comes <em>(you’ve guessed it)</em> great responsibility. I want to share my perspective on this as I’ve seen many useful and very complex protocol implementations.</p>
<p>
First of all, if you’re not familiar with protocols yet I recommend to check out the <a href="https://elixir-lang.org/getting-started/protocols.html" title="Protocols - the Elixir Lang Getting Started Guide">official getting started guide for protocols</a>. It’s a very good resource to learn about the topic and understand how it works. For the rest of this post, I’ll assume you understand what a protocol is and does. I don’t want to repeat the same material with the chance my version is a poor copy of the original. This also has the potential to leave you more clueless than when you started reading this article.</p>
<p>
The example given by the official guide is quite useful. It defines the protocol <code class="inline">Size</code> and it is defined like this:</p>
<pre><code class="makeup elixir"><span class="kd">defprotocol</span><span class="w"> </span><span class="nc">Size</span><span class="w"> </span><span class="k" data-group-id="3830966641-1">do</span><span class="w">
  </span><span class="na">@doc</span><span class="w"> </span><span class="s">&quot;Calculates the size (and not the length!) of a data structure&quot;</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">size</span><span class="p" data-group-id="3830966641-2">(</span><span class="n">data</span><span class="p" data-group-id="3830966641-2">)</span><span class="w">
</span><span class="k" data-group-id="3830966641-1">end</span></code></pre>
<p>
In fact, the intention of this protocol is not to <em>calculate</em> the size, but to return the pre-computed size of an element (). Perhaps, the protocol would be better named <code class="inline">PreComputedSize</code> to clarify intent, but it becomes a bit long and you could easily clarify intent in the module documentation. I think it is very important to describe what your protocol is supposed to do. I believe it makes it easier to add new implementations.</p>
<h2>
My Webshop</h2>
<p>
For the moment, let’s assume I own a webshop and I sell a couple of office supplies; paper &amp; paperclips. When I need to ship these items I need to know an approximation of the volume so I know how many I can pack in a box. I’ll define a protocol called <code class="inline">Product</code> with a function <code class="inline">volume/1</code> and implement the protocol for <code class="inline">%Paper</code> and <code class="inline">%Paperclips{}</code>. Here is it in code:</p>
<pre><code class="makeup elixir"><span class="kd">defprotocol</span><span class="w"> </span><span class="nc">Product</span><span class="w"> </span><span class="k" data-group-id="8341210848-1">do</span><span class="w">
  </span><span class="na">@doc</span><span class="w"> </span><span class="s">&quot;Calculates the volume of a data structure&quot;</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">volume</span><span class="p" data-group-id="8341210848-2">(</span><span class="n">term</span><span class="p" data-group-id="8341210848-2">)</span><span class="w">
</span><span class="k" data-group-id="8341210848-1">end</span><span class="w">

</span><span class="kd">defimpl</span><span class="w"> </span><span class="nc">Product</span><span class="p">,</span><span class="w"> </span><span class="ss">for</span><span class="p">:</span><span class="w"> </span><span class="nc">Paperclips</span><span class="w"> </span><span class="k" data-group-id="8341210848-3">do</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">volume</span><span class="p" data-group-id="8341210848-4">(</span><span class="n">paperclips</span><span class="p" data-group-id="8341210848-4">)</span><span class="w"> </span><span class="k" data-group-id="8341210848-5">do</span><span class="w">
    </span><span class="n">paperclips</span><span class="o">.</span><span class="n">volume</span><span class="w">
  </span><span class="k" data-group-id="8341210848-5">end</span><span class="w">
</span><span class="k" data-group-id="8341210848-3">end</span><span class="w">

</span><span class="kd">defimpl</span><span class="w"> </span><span class="nc">Product</span><span class="p">,</span><span class="w"> </span><span class="ss">for</span><span class="p">:</span><span class="w"> </span><span class="nc">Paper</span><span class="w"> </span><span class="k" data-group-id="8341210848-6">do</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">volume</span><span class="p" data-group-id="8341210848-7">(</span><span class="n">paper</span><span class="p" data-group-id="8341210848-7">)</span><span class="w"> </span><span class="k" data-group-id="8341210848-8">do</span><span class="w">
    </span><span class="n">paper</span><span class="o">.</span><span class="n">x</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">paper</span><span class="o">.</span><span class="n">y</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">paper</span><span class="o">.</span><span class="n">z</span><span class="w">
  </span><span class="k" data-group-id="8341210848-8">end</span><span class="w">
</span><span class="k" data-group-id="8341210848-6">end</span></code></pre>
<p>
As you can see, the data structure for paperclips has a volume key and the protocol can just return the value. For paper, it is a bit more complex as for the dimensions multiplication is required, but it’s not too hard. Now when I get a list of items I can easily calculate the volume and pack everything in one or multiple boxes:</p>
<pre><code class="makeup elixir"><span class="n">items</span><span class="w">
</span><span class="o">|&gt;</span><span class="w"> </span><span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p" data-group-id="5414821984-1">(</span><span class="o">&amp;</span><span class="nc">Product</span><span class="o">.</span><span class="n">volume</span><span class="o">/</span><span class="mi">1</span><span class="p" data-group-id="5414821984-1">)</span><span class="w">
</span><span class="o">|&gt;</span><span class="w"> </span><span class="n">pack_in_a_box</span><span class="p" data-group-id="5414821984-2">(</span><span class="p" data-group-id="5414821984-2">)</span></code></pre>
<p>
And voila! I can sell paper &amp; paperclips via my webshop and pack them in appropriate boxes.</p>
<h2>
Adding a Product (or Two)</h2>
<p>
But to my surprise, the webshop has been <strong>a great success</strong> and I want to add a product. This should be fairly easy as I defined a protocol and <code class="inline">Product</code> can simply be implemented for <code class="inline">%Pencil{}</code>. Here we go:</p>
<pre><code class="makeup elixir"><span class="kd">defimpl</span><span class="w"> </span><span class="nc">Product</span><span class="p">,</span><span class="w"> </span><span class="ss">for</span><span class="p">:</span><span class="w"> </span><span class="nc">Pencil</span><span class="w"> </span><span class="k" data-group-id="3779020335-1">do</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">volume</span><span class="p" data-group-id="3779020335-2">(</span><span class="n">pencil</span><span class="p" data-group-id="3779020335-2">)</span><span class="w"> </span><span class="k" data-group-id="3779020335-3">do</span><span class="w">
    </span><span class="n">pi</span><span class="p" data-group-id="3779020335-4">(</span><span class="p" data-group-id="3779020335-4">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nc">:math</span><span class="o">.</span><span class="n">pow</span><span class="p" data-group-id="3779020335-5">(</span><span class="n">pencil</span><span class="o">.</span><span class="n">r</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p" data-group-id="3779020335-5">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">pencil</span><span class="o">.</span><span class="n">h</span><span class="w">
  </span><span class="k" data-group-id="3779020335-3">end</span><span class="w">
</span><span class="k" data-group-id="3779020335-1">end</span></code></pre>
<p>
It’s probably a good idea not to calculate <code class="inline">pi/0</code> on the spot, but what if I would? From an implementation perspective, it seems to be correct. This is indeed how you calculate the volume for a pencil. However, suddenly my box packing code becomes slow at seemingly random moments. You can see that the protocol makes it easy to extend it. Though the complexity of the implementation may have unintended side-effects. We can still make it a bit worse though since I also plan on selling boxed pencils.</p>
<pre><code class="makeup elixir"><span class="kd">defimpl</span><span class="w"> </span><span class="nc">Product</span><span class="p">,</span><span class="w"> </span><span class="ss">for</span><span class="p">:</span><span class="w"> </span><span class="nc">Pencilbox</span><span class="w"> </span><span class="k" data-group-id="5224238654-1">do</span><span class="w">
  </span><span class="kd">def</span><span class="w"> </span><span class="nf">volume</span><span class="p" data-group-id="5224238654-2">(</span><span class="n">pencilbox</span><span class="p" data-group-id="5224238654-2">)</span><span class="w"> </span><span class="k" data-group-id="5224238654-3">do</span><span class="w">
    </span><span class="n">pencilbox</span><span class="o">.</span><span class="n">pencil_count</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nc">Product</span><span class="o">.</span><span class="n">volume</span><span class="p" data-group-id="5224238654-4">(</span><span class="n">pencilbox</span><span class="o">.</span><span class="n">pencil</span><span class="p" data-group-id="5224238654-4">)</span><span class="w">
  </span><span class="k" data-group-id="5224238654-3">end</span><span class="w">
</span><span class="k" data-group-id="5224238654-1">end</span></code></pre>
<p>
<strong>Whoa! MIND. BLOWN.</strong> We’re not only achieving polymorphism but <em>recursive polymorphism</em>. While I will admit straight away there might be good use-cases for this, I haven’t seen one yet. In most real-world scenarios where you write common business logic, you don’t need this level of complexity. It will be hard to reason or understand my simple box packing code because of the underlying complexity that is hidden from the view.</p>
<h2>
Here Is My Advice When You’re Considering Creating a Protocol</h2>
<ul>
  <li>
Describe your protocol as precise as you can  </li>
  <li>
Implementations for the same protocol should have similar complexity  </li>
  <li>
You probably don’t want recursive polymorphism  </li>
</ul>
<p>
One last piece of advice, which is only useful if your protocol needs some helpers, is to wrap access to your protocol in a module and provide the helper functions from there. It is similar to the <code class="inline">Enumerable</code> protocol and the <code class="inline">Enum</code> module.</p>
<p>
Did you enjoy this blogpost? Do you have something to add? Perhaps you have a different opinion you’d like to share? I’d love to hear and get some feedback! You can find my social/email at the top of the page!</p>
<p>
Thank you Alexey G. for proof-reading this post!</p>
</div>
    </content>
    <summary type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">Elixir protocols are powerful for achieving polymorphism. But with great power comes (you've guessed it) great responsibility. I want to share my perspective on this.</div>
    </summary>
  </entry><entry>
    <id>https://timmo.immo/blog/setup-procrastination</id>
    <link href="https://timmo.immo/blog/setup-procrastination" rel="alternate" type="text/html"/>
    <title>Setting up My Blog as Procrastination for Writing</title>
    <updated>2020-10-30T00:00:00+01:00</updated>
    <published>2020-10-30T00:00:00+01:00</published>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml"><p>
I’ve been meaning to set up my blog for about three years. This time around I decided to push through and instead of using the setup to delay my writing more, use it as a force. Once it’s up and published I no longer have to configure it, make it look a specific way, figure out some new tooling, it’s already there. Ready to publish. Now the site is up before the content is created, step one completed.</p>
<p>
Over the last 3-4 years, I have created and implemented my personal website at least 3 times. One of them was with <a href="https://gohugo.io/" title="Hugo Static Site Generator">Hugo</a>, a static site generator that allowed me to host it as a static site. I spend days on my own custom theme to make it look the exact way I wanted. I’m not a frontend software engineer so this produced numerous challenges. One reason to do this was that I didn’t like any of the existing themes, so I decided to create my own. <a href="https://getbootstrap.com/" title="Bootstrap CSS Framework">Bootstrap</a>, a quite popular CSS framework wasn’t really my thing. All bootstrap sites look the same to me. So I tried <a href="https://bulma.io/" title="Bulma CCS Framework">Bulma</a>, which I liked, but fiddled around with for far too long. I did like the result but it was a long and sometimes painful journey.</p>
<p>
Fast-forward a year, or maybe one-and-a-half, and I just ditched whatever I had created earlier. I wasn’t happy with it. Not the theme I created per se, but also the static site framework. I forgot a little how it worked and having every part created myself, it required understanding what’s what to be able to make a small change. In fact, I just should’ve started writing for the blog, I ended up re-learning the framework, finding a (newer) theme I liked better. But at the end of the day still didn’t get anything done. Yes, <strong>the title of this blog post is real</strong>. After ditching it I started over with a clean slate.</p>
<h2>
What Changed -or- Something Cool Happened</h2>
<p>
The <a href="https://twitter.com/josevalim" title="José Valim">creator</a> of <a href="https://elixir-lang.org/" title="Elixir Programming Language">Elixir</a> open-sourced a simple library called <a href="https://github.com/dashbitco/nimble_publisher/" title="Nimble Publisher">Nimble Publisher</a> that takes markdown files and compiles them to modules. <em>“Why is this cool?”</em> you might ask. While it is probably not as fast as a truly static site, it comes very close as Elixir (or Erlang) modules are loaded in memory. This means whenever you request a page, all the computer has to do is dump the right page from memory. The content still needs to be rendered on the server, which is not needed for a static site, but this should really be <em>plenty</em> fast. Another big plus, for both a static site generator and this solution, is that you can write your posts and pages in <a href="https://wikipedia.org/wiki/Markdown" title="Markdown on Wikipedia">Markdown</a>. You don’t need a database or fancy editor to make a good-looking page. A benefit of having Elixir backing my blog is that I’m familiar with it and I like to play with it. Instead of learning about some site generator, I’m just applying the things I’m already familiar with. Thankfully there was also a <a href="https://bernheisel.com/blog/moving-blog" title="'Moving the blog to Elixir and Phoenix LiveView' by David Bernheisel">very nice blog post</a> by <a href="https://twitter.com/bernheisel" title="David Bernheisel on Twitter">David Bernheisel</a> explaining how he set it up which I used as inspiration.</p>
<p>
All that was left was making it look good. Going through my old attempts didn’t make me happy, I forgot mostly how the frameworks worked and I was actually longing for something that felt more like a library. You just sprinkle a little of it and suddenly your website looks <em>good enough</em>. I read a couple of good things about <a href="https://tailwindcss.com/" title="Tailwind CSS">Tailwind CSS</a>. It’s a utility-first CSS framework. Going through the initial learning curve wasn’t too bad and I noticed I’m not constantly switching between HTML and CSS to prototype a webpage. I was just adding and removing classes in the HTML first and only later abstracted tiny parts of that away in CSS. A blogpost that helped me do it was <a href="https://www.swyx.io/why-tailwind/" title="Why Tailwind by @swyx">Why Tailwind</a> by <a href="https://twitter.com/swyx" title="@swyx on Twitter">@swyx</a>.</p>
<p>
I’m quite happy with the result and this blog post is proof that getting it out helped me write a post. I still need to fix some small things and tidy it up, but please let me know what you think and send me your feedback!</p>
</div>
    </content>
    <summary type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">I've been meaning to setup my blog for about three years. This time around I decided to push through and instead of delaying my writing even more, use it as a force. Once it's up and published I no longer have to configure it, make it look a specific way, figure out some new tooling, it's already there. Ready to publish.</div>
    </summary>
  </entry>
</feed>