<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[// Of Tech and Man...]]></title><description><![CDATA[Pug's trysts, tribulations and tragedies with technology.]]></description><link>https://www.pugmarx.me</link><image><url>https://substackcdn.com/image/fetch/$s_!lW1Q!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08460d57-8153-4c58-93b8-6039f9d57234_1280x1280.png</url><title>// Of Tech and Man...</title><link>https://www.pugmarx.me</link></image><generator>Substack</generator><lastBuildDate>Tue, 19 May 2026 03:08:00 GMT</lastBuildDate><atom:link href="https://www.pugmarx.me/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[pugmarx]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[pugmarx@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[pugmarx@substack.com]]></itunes:email><itunes:name><![CDATA[pugmarx]]></itunes:name></itunes:owner><itunes:author><![CDATA[pugmarx]]></itunes:author><googleplay:owner><![CDATA[pugmarx@substack.com]]></googleplay:owner><googleplay:email><![CDATA[pugmarx@substack.com]]></googleplay:email><googleplay:author><![CDATA[pugmarx]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Building a RAG App on Strava Data]]></title><description><![CDATA[...or how I bit the AI bullet]]></description><link>https://www.pugmarx.me/p/building-a-rag-app-on-strava-data</link><guid isPermaLink="false">https://www.pugmarx.me/p/building-a-rag-app-on-strava-data</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Wed, 27 Aug 2025 15:18:42 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="6970" height="4649" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:4649,&quot;width&quot;:6970,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a person riding a bike with a gps app on the handlebars&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="a person riding a bike with a gps app on the handlebars" title="a person riding a bike with a gps app on the handlebars" srcset="https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1633230329619-70ae2e6d50bb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxzdHJhdmF8ZW58MHx8fHwxNzU2MzA4ODcwfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@willy_teee">Will Truettner</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><h2>Ears to the ground&#8230;</h2><p>Intrigued by the current AI trends across the industry, I was eager to try it out for <em>something</em> practical&#8212;to get a grasp on the concepts, terminologies, and see if I could leverage open source tools. The icing on the cake, I thought, would be improving on something I'd previously built.</p><h2>..a simple RAG application</h2><p>Let me talk about a very simple RAG<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> application that I ended-up building. Few years ago, I had played-around with Strava<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> APIs to fetch my activity data (from prehistoric era &#8212; when I was <em>active</em>) &#8212; the whole experience was fun as I&#8217;d experimented with a simple service (using (the now defunct) snips.ai , Raspberry Pi, and an Xmini). The application would:</p><ul><li><p>accept voice input like &#8220;how much did I <em>run </em>(/bike) last week (/month/year)&#8221; </p></li><li><p>summarise my run and biking activities&#8217; totals for a given duration, and then,</p></li><li><p>respond to be via audio.</p><p>(<a href="https://youtu.be/VFvpalq6r3A">Here&#8217;s a demo video</a> from back then)</p></li></ul><p>In this new Strava-RAG project, the aim was to cash-in on that past experience with Strava APIs, and see how it can be made better. I got a simple project outline, and capabilities generated using ChatGPT. </p><p>Admittedly, since I have a soft corner for PostgreSQL, I was also keen on utilising <em>its</em> vector database<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> extension.</p><p>The overall system goal was simple, and can be worded as:</p><blockquote><p>A system that can answer question in plain English about my (or any athlete&#8217;s) activities like &#8220;What year was I the <strong>most</strong> active in&#8221;, or something more complex like &#8220;Show me the activities where I performed like my <strong>best</strong> activity in 2019&#8221;, and so on&#8230;</p></blockquote><p>The following high-level functionalities were required:</p><ol><li><p><strong>Fetch: </strong>Authenticate and fetch activities from Strava</p></li><li><p><strong>Persist: </strong>Store activities in the PostgreSQL</p></li><li><p><strong>Query: </strong>Enable natural language querying on the stored activities using LLM</p></li></ol><p>Let&#8217;s dive in!</p><h3>Fetch: Authenticate, Authorize, and Fetch data from Strava</h3><p> Strava uses OAuth protocol for authentication, which turned out to be a <em>bit</em> more involved<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a> than what it sounds in theory, so let me lay it down simply.</p><p>Here&#8217;s the general flow:</p><ul><li><p>You register your app with Strava and get a <strong>Client ID</strong> and <strong>Client Secret</strong>.</p></li><li><p>You direct the user (yourself, in this case) to a special <strong>authorization URL</strong> that includes your Client ID, requested permissions (scopes), and a redirect URI.</p></li><li><p>On approval, Strava redirects back to your specified URI with an <strong>authorization code</strong>.</p></li><li><p>You exchange this code (along with your Client Secret) for an <strong>access token</strong> and <strong>refresh token</strong>.</p></li><li><p>You use the <strong>access token</strong> to fetch actual activity data. When it expires (typically after 6 hours), you use the <strong>refresh token</strong>to get a new access token without re-authorization.</p></li></ul><p>This boiled down to two basic functions:</p><pre><code># Get the token
get_token(auth_code) -&gt; access_token

# Use token to get activities
get_activities(access_token) -&gt; activities[]</code></pre><p>Note that the tokens expire - so this also involves an additional quirk of adding a <code>refresh_token</code> logic to the backend code.</p><p>The output of <code>get_activities</code> is an array of activities, which are then dumped to a JSON file.</p><h3><strong>Persist: Store activities in the PostgreSQL</strong></h3><p>Once the list of activities have been successfully fetched, the next step is to push them to the database. While doing that we also convert relevant information to <strong>embeddings</strong> to enable intelligent querying.</p><p>Following was the format of (would-be) embedded sentences.</p><blockquote><pre><code>{activity['name']} {activity['type']} {activity['distance']} meters in {activity['elapsed_time']} seconds"</code></pre></blockquote><p>All the activities were converted to the above format, and then I utilised the Python-provided <code>sentence-transformer </code>model to do the <code>string &#8594; embedding </code>transformation. For example, a sentence that is formed by replacing the placeholders in the above sentence would be:</p><blockquote><p><strong>morning cycling</strong> <em>ride</em> <em><strong>10034 meters</strong></em> in <strong>1934 seconds</strong> </p></blockquote><p>which would then get converted via <code>sentence-transformer</code> model to a 384-dimension vector like so:</p><blockquote><pre><code>[0.034342  0.0534622  .0324592  0.0441267  &#8230;] </code></pre></blockquote><p>each of which gets stored for each activity record.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WWel!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WWel!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WWel!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WWel!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WWel!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WWel!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg" width="728" height="93.86163522012579" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:164,&quot;width&quot;:1272,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:35676,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.pugmarx.me/i/169923704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a54e943-7289-43b1-a25e-2272617ec4b0_1272x164.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WWel!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WWel!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WWel!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WWel!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870c3051-2ccb-49be-9100-0aec6c759a8e_1272x164.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Fig1 - Sentence Transformation to a 384-dimension vector</figcaption></figure></div><p>It&#8217;s easy to write a simple iteration to do the same for the entire set of activities, and store it into a DB table. This table would then become our source of truth, and would be kept updated with new activities over time.</p><p>With this, I was done with the <em>heart</em> of the application. What remained was to enable querying in natural language, which could then leverage these embeddings.</p><h3><strong>Query: Enable natural language querying on the stored activities using LLM</strong></h3><p>I must admit that initially I took this step for granted, na&#239;vely assuming that the LLM <em>will take care of everything</em>. It wasn&#8217;t to be.</p><p>The platform of choice was <strong>Ollama</strong>, since I wanted something lightweight, and <em>local</em>. Based on ChatGPT&#8217;s recommendation, I went ahead to install <strong>Mistral</strong> on Ollama. As per the default configuration, the LLM was available via a (local) API call, for basic querying.</p><h4>Control Flow</h4><p>The overall control flow can be envisioned as:</p><ul><li><p>Accept user&#8217;s query</p></li><li><p>Process the query in the backend and return the query results</p><ul><li><p>Encode the query using the <code>sentence-transformer</code></p></li><li><p>Use the resultant embedding in the similarity-based query on the database</p></li><li><p>Return the query results</p></li></ul><p>These query results then become the additional (/augmented) context that has to be passed to the (Mistral) LLM</p></li><li><p>Pass the additional context, and the base prompt to Mistral. This prompt was of the form:</p><pre><code>You are an assistant which summarizes Strava activity data, based on the retrieved activity information below.

Retrieved Activity Context:
{<strong>additional_context</strong>}

Instructions:
...
... </code></pre></li></ul><p>The high-level flow can be illustrated like so:</p><pre><code>                              &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                              &#9474;     USER        &#9474;
                              &#9474;  Asks Question  &#9474;
                              &#9474;"Best 2023 runs?"&#9474;
                              &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                                        &#9474;
                                        &#9660;
                              &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                              &#9474;   WEB FRONTEND  &#9474;
                              &#9474; (HTML/CSS/JS)   &#9474;
                              &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                                        &#9474; HTTP POST
                                        &#9660;
                              &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                              &#9474;  FLASK API      &#9474;
                              &#9474;   (Python)      &#9474;
                              &#9474; /query endpoint &#9474;
                              &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                                        &#9474;
                                        &#9660;
                    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                    &#9474;           RAG ENGINE                &#9474;
                    &#9474;                                     &#9474;
                    &#9474;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;   &#9474;
                    &#9474;  &#9474;   RETRIEVE  &#9474;  &#9474;  GENERATE   &#9474;   &#9474;
                    &#9474;  &#9474;             &#9474;  &#9474;             &#9474;   &#9474;
                    &#9474;  &#9474; Find best   &#9474;  &#9474;   Ollama    |   |
                    &#9474;  &#9474;             |  |  + Mistral  |   |
                    &#9474;  &#9474; matching    &#9474;&#9472;&#9654;&#9474;   (LLM)     &#9474;   &#9474;
                    &#9474;  &#9474; activities  &#9474;  &#9474;             &#9474;   &#9474;
                    &#9474;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9474;
                    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                              &#9474;
                              &#9660;
                    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                    &#9474;   POSTGRESQL    &#9474;
                    &#9474;   DATABASE      &#9474;
                    &#9474;                 &#9474;
                    &#9474; &#8226; Activity data &#9474;
                    &#9474; &#8226; Vector search &#9474;
                    &#9474; &#8226; Smart filters &#9474;
                    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                              &#9474;
                              &#9660;
                        &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                        &#9474;   RESPONSE      &#9474;
                        &#9474;                 &#9474;
                        &#9474; "Here are your  &#9474;
                        &#9474; best runs:      &#9474;
                        &#9474; &#127939; Oct 1, 2023  &#9474;
                        &#9474; &#127939; Sep 28, 2023"&#9474;
                        &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                                  &#9474;
                                  &#9660;
                        &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
                        &#9474;     USER        &#9474;
                        &#9474;  Sees Results   &#9474;
                        &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</code></pre><h4>Prompt Refinement</h4><ul><li><p>Was this enough? Nope &#8212; the prompt did need refining to handle cases where an any infrequent activity was queried, for example &#8216;Weight Training&#8217;, etc.</p></li><li><p>Another refinement needed for the interpretation of &#8216;Best&#8217;. That is, while the &#8216;Best&#8217; for a given Run/Ride activity would be determined based on Speed, Duration, etc. &#8212; the same cannot be said for <em>other</em> types of activities. In the case of &#8216;Hiking&#8217; or &#8216;Weight Training&#8217;, for example, parameters to determine &#8220;Best&#8221; are quite different &#8212; e.g. &#8216;Elevation&#8217; in the case of Hiking. Similarly, there were other edge cases that required instruction (prompt) refinement, and logic-based handling!</p></li></ul><h2>A Basic UI</h2><p>Then I went ahead to add a simple, browser-based UI to the application, because until then it was all CLI-driven. This was trivial using Claude which created a basic (HTML/JS) UI, which could be seamlessly integrated to the existing Flask app.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0vCB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0vCB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 424w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 848w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 1272w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0vCB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png" width="1234" height="715" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:715,&quot;width&quot;:1234,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:102955,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.pugmarx.me/i/169923704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0vCB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 424w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 848w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 1272w, https://substackcdn.com/image/fetch/$s_!0vCB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc906aa8-6dbf-49da-bf67-609fbe27de6a_1234x715.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image showing a sample output on the UI</figcaption></figure></div><h2>Next steps..</h2><p>In subsequent posts, I want to build-up on this basic project and to mimic a production system as I think that&#8217;s a topic which requires adequate attention. In other words I want to focus on: <em>how does this all come together, and, more importantly, <strong>sustain over time</strong>, </em>as<em> </em>would in a real-world system!</p><p>You can find the above project <a href="https://github.com/pugmarx/strava-insights-rag">here</a>.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong>Retrieval Augmented Generation (RAG)</strong>: A system that enhances (augments) a language model&#8217;s responses by retrieving relevant information from an external source &#8212; database, documents, etc.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><strong><a href="http://strava.com">Strava</a></strong> is a very popular activity-tracking app. It&#8217;s also a social network of sorts for the users, in that users can subscribe to each other&#8217;s &#8220;activity feed&#8221;, and react on it</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>Vector database:</strong> A kind of database that stores text as numbers (called <strong>embeddings</strong>), making it possible to run mathematical operations &#8212; like measuring how similar two pieces of text are, by comparing their number arrays.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p><strong>Strava OAuth Flow</strong>: See a more technical overview <a href="https://github.com/pugmarx/strava-insights-rag/blob/main/docs/strava_oauth.md">here</a></p></div></div>]]></content:encoded></item><item><title><![CDATA[Lessons from a Simple, Unsupervised Anomaly Detection Use Case]]></title><description><![CDATA[When time-series models fall short, and why that's okay]]></description><link>https://www.pugmarx.me/p/takeaways-from-a-simple-unsupervised</link><guid isPermaLink="false">https://www.pugmarx.me/p/takeaways-from-a-simple-unsupervised</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 13 Jul 2025 14:44:00 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="2756" height="1838" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1838,&quot;width&quot;:2756,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;four airplanes flying in formation in the sky&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="four airplanes flying in formation in the sky" title="four airplanes flying in formation in the sky" srcset="https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1649034874998-245b023bb70d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2M3x8b3V0bGllcnN8ZW58MHx8fHwxNzU2MzA4OTg5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@koshaughnessy">K O'Shaughnessy</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>Anomaly detection is not a novel problem in Data Science and has been in common vocabulary owing to its diverse applicability &#8212; in finance (fraud detection) to infrastructure monitoring (outages, cost spikes, ..). Early in my career, I had the opportunity to work in the <em>Knowledge-based Computer Systems</em> group at NCST, Mumbai. Our use case was to monitor quotation outliers in imports, and our systems took hours to analyse few months&#8217; of data before flagging &#8220;undervalued&#8221; imports.</p><p>Two decades later, in a recent hackathon, we ended up processing millions of records in just minutes! Since we were dealing with timed events, and the initial hunch was that a multi-variate<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> time-series-based anomaly detection would be a natural fit. However, it wasn&#8217;t to be!</p><p>To our surprise, common anomaly detection models like <em>Isolation Forest</em> significantly outperformed the time-series one both in training <em>latency </em>and prediction <em>accuracy</em> for multi-variate use cases.</p><p>This could of course be subjective, and it seemed that the training <em>latency</em> aspect was influenced by the fact that <em>time-series-anomaly-detection </em>model appeared to rely only on process-level concurrency, which limited its scalability in distributed environments like Spark.</p><p>The prediction <em>accuracy</em> (or the lack of it) aspect can be blamed on the data, which, even though was timestamped, but the events were sporadic &#8212; sometimes clustered, and on other occasions there was a lull. This was of course not in-line with the frequency that the time-series-based model would have expected.</p><blockquote><p>Time-series-based models typically expect the data to be evenly distributed, and spikes would cause them to fumble. In other words, time-series based detection would work better in situations like sensor-based events, where one would expect a constant timestamped stream of data.</p></blockquote><p>Since the data was granular to the second, clustering of events posed another interesting challenge &#8212; that of data loss! This was because in order to leverage the model, an index was needed. In our case, an index that was purely based on the timestamp field would have resulted in <em>last-write-wins</em> scenario because the system considers the last event (or row) it&#8217;s processing for the index. This is, of course, a side effect of not maintaining a <em>millisecond</em> or <em>microsecond</em> granularity while storing event timestamps.</p><blockquote><p>To train a model using timestamped events for time-series anomaly detection, an important aspect is to maintain the data at the highest level of temporal granularity, to minimise data loss.</p></blockquote><p>In parallel, we were also exploring other models, like Isolation Forest, LOF, PCA, etc., with various levels of contamination. We observed that Isolation Forest outperformed all other models both during training as well as prediction. For training, the time-series model we used became exponentially slower when the training feature count was increased even by a small number, while retaining the sample size.</p><p>Isolation Forest, in Spark environment, not only could leverage the distribution, but does it much more efficiently &#8212; and at negligible costs!</p><p>Based on these results, our next step would be to further refine the model with <em>some</em> amount of labeled data, allowing us to improve the precision.</p><h3>Further reading</h3><ol><li><p>Isolation Forest: <a href="https://dl.acm.org/doi/10.1145/2133360.2133363">Paper</a></p></li><li><p>Time-series Anomaly Detection: <a href="https://dl.acm.org/doi/10.1145/3292500.3330680">Paper</a></p></li></ol><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Multivariate anomaly detection models evaluate <strong>patterns across multiple features</strong>, unlike traditional models that operate on a <strong>single metric (</strong>e.g. daily cost, or, single parameter threshold breach). This allows them to detect more complex, less obvious anomalies.</p></div></div>]]></content:encoded></item><item><title><![CDATA[The Elusive (talk of) Design Patterns]]></title><description><![CDATA[Remember the time when Design Patterns were in vogue?]]></description><link>https://www.pugmarx.me/p/the-elusive-discussion-of-design</link><guid isPermaLink="false">https://www.pugmarx.me/p/the-elusive-discussion-of-design</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 31 Mar 2024 10:41:50 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Remember the time when <a href="https://en.wikipedia.org/wiki/Design_Patterns">Design Patterns</a> were in <em>vogue? </em>Go to any interview, and one of the most popular direct or indirect questions was to test your knowledge on them. Some cheeky interviewers also asked questions like:</p><blockquote><p>&#8220;how many design patterns are you aware of &#8230; oh, those are just J2EE ones, what about the <em>core</em> ones&#8221;. </p></blockquote><p>While it may <em>sound</em> superficial, knowledge of design patterns, or even the mere pursuit of it, did encourage thinking beyond the obvious, if I may say so. It nudged the critical thinker in you! Subsequently, enlightened individuals like myself would have appreciated aspects such as <em>DB connection pools</em>, choice of class hierarchy in Java collections, or something as simple as the &#8216;Undo&#8217; command in any IDE that we were using, a bit better!</p><p>Anyhoo, the point of this post is to present an observation that modern day computing &#8212; say a typical web-based application development &#8212; doesn&#8217;t give you as many avenues to acknowledge the design patterns. Hence, I believe, a typical interview question of today, if it ventures into the archaic <em>design patterns </em>(GoF<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> or otherwise), would be frowned upon!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="3028" height="1980" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1980,&quot;width&quot;:3028,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;grayscale photography of group of men sitting and chatting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="grayscale photography of group of men sitting and chatting" title="grayscale photography of group of men sitting and chatting" srcset="https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1578593174141-c21837c761a7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxMXx8ZGluaW5nJTIwcGhpbG9zb3BoZXJzfGVufDB8fHx8MTc1NjMwOTA1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@austriannationallibrary">Austrian National Library</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>Much of this shift can be attributed to the changing paradigms of software development. The introduction of tools and techniques that abstract away the intricate details has, on one hand, accelerated application development significantly, but on the other hand, it has diverted the modern-day developer from the avenues or even the necessity to pursue such knowledge.</p><p>However, as old school as I may sound, I still believe that the knowledge and appreciation of GoF patterns, in this age of architectural blueprints and reference architecture, would certainly come in handy in some way or another &#8212; be it <em>cloud-native</em> development, or in developing homegrown greenfield app! It may not provide leverage in the interviews &#8212; but does give a developer an edge while designing and developing a system, or a part of it!</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Gang of Four &#8212; <a href="https://en.wikipedia.org/wiki/Erich_Gamma">Erich Gamma</a>, <a href="https://en.wikipedia.org/wiki/Richard_Helm">Richard Helm</a>, <a href="https://en.wikipedia.org/wiki/Ralph_Johnson_(computer_scientist)">Ralph Johnson</a>, and <a href="https://en.wikipedia.org/wiki/John_Vlissides">John Vlissides</a></p></div></div>]]></content:encoded></item><item><title><![CDATA[The 'Solution Architect' Conundrum]]></title><description><![CDATA[One of the lessons I learned from a brief stint as a &#8220;Cloud Solution Architect&#8221; is that it&#8217;s easy to get away with murder! And murder being the (anti-)pattern of providing a quick cloud-native &#8216;solution&#8217;, using the latest and greatest (read: what the cloud vendor want you to sell) out there &#8212; and then not being there to face the music, when things go awry!]]></description><link>https://www.pugmarx.me/p/the-solution-architect-conundrum</link><guid isPermaLink="false">https://www.pugmarx.me/p/the-solution-architect-conundrum</guid><pubDate>Sun, 17 Mar 2024 17:21:25 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="5472" height="3648" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3648,&quot;width&quot;:5472,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;person in black knit cap and gray sweater&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="person in black knit cap and gray sweater" title="person in black knit cap and gray sweater" srcset="https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1618590067824-5ba32ca76ce9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2fHxtYXNrfGVufDB8fHx8MTc1NjMwOTE1N3ww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@sammywilliams">Sander Sammy</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>One of the lessons I learned from a brief stint as a &#8220;Cloud Solution Architect&#8221; is that it&#8217;s easy to <em>get away with murder!</em> And <em>murder</em> being the (anti-)pattern of providing a <em>quick</em> cloud-native &#8216;solution&#8217;, using the latest and greatest (read: what the cloud vendor want you to sell) out there &#8212; and then not being there to face the music, when things go awry! <br>Of course, this varies from one organisation to another &#8212; but this is a general pattern observed in organisations that present (or make you perceive) cloud-native solutions as <em>the knights in shining armour</em> for the gullible <em>damsels in distress </em>organisations<em>.</em> The latter belong to two main categories:</p><ol><li><p>Technical organisations who were doing great in the past, but their stacks have become stale over time, and hence want to join the <em>cloud</em> bandwagon, and,</p></li><li><p>Non-technical organisations, who have been sold the idea of <em>cloud</em> being a cheap and quick answer to a &#8220;happily ever after&#8221;!</p></li></ol><p>In both these cases, sadly, by the time they realise that the picture is far less rosy than what it was <em>sold </em>for, it&#8217;s already too late! But the original intent of my post was more about the individuals who are bestowed with the responsibility of creating (and selling) their solutions. Often, the people at the receiving end are not savvy enough to understand the common pain points pertaining to cloud-based applications, to name a few:</p><ul><li><p>the hidden costs (e.g. bandwidth usage, fringe services, storage costs, etc.)</p></li><li><p>the cost of maintenance (e.g. upgrading the instance types, vulnerability fixes, etc.)</p></li><li><p>other convoluted arrangements &#8212; e.g. a little birdie told me about a public cloud vendor trying to sell a <code>Java</code>-based solution, where a simple <code>node.js</code> based application would do the job. Reason being: Java-based solution consume more compute!</p></li></ul><p>So, then, the integrity of the <em>solution architects</em> in-charge becomes crucial! Are they true architects or mere salespersons? Ethics and authenticity define their role beyond mere sales. A few things that could characterise it are:</p><ul><li><p>seeing the solution through, and then taking a pulse after about three months or so of the solution going into production about how things are</p></li><li><p>doing their due diligence before recommending the shiny new service on the cloud to your customers, and,</p></li><li><p>asking about the non-functional requirements, and seeing that those are met in the solutions that they present. These should definitely include the <em>running</em> cost of the cloud-based application under peak load.</p></li></ul><p>Now, since 'solution architects' may have limited influence on contracts and other legalities, the organisations engaging these cloud solution providers should ensure that their contracts include clauses expecting the following in a crisp and unambiguous language:</p><ul><li><p>A post go-live warranty period, and then follow-ups at regular intervals for pulse-checks and course-corrections, if required</p></li><li><p>Agreed cost predictions, and then clauses on how much threshold breach would require the cloud solution provider to bear the cost</p></li><li><p>Agreed performance expectations, and well-defined SLAs for breaches</p></li></ul><p>Bringing accountability would definitely minimise escape routes and raise the bar expected of cloud solution organisations and indirectly, their architects. This, in turn, would pave the way for more reliable agreements!</p>]]></content:encoded></item><item><title><![CDATA[Book review: Designing Data-Intensive Applications]]></title><description><![CDATA[Designing Data Intensive Applications (DDIA) is one of the most popular books in the present times. It&#8217;s hardly a surprise that it is so!]]></description><link>https://www.pugmarx.me/p/about-a-book-ddia</link><guid isPermaLink="false">https://www.pugmarx.me/p/about-a-book-ddia</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 03 Feb 2024 18:12:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jI8V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jI8V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jI8V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 424w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 848w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 1272w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jI8V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c87e1326-6fda-4316-8321-b7d5d49eec25_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jI8V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 424w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 848w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 1272w, https://substackcdn.com/image/fetch/$s_!jI8V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc87e1326-6fda-4316-8321-b7d5d49eec25_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">reading!</figcaption></figure></div><p><em><a href="https://dataintensive.net/">Designing Data Intensive Applications</a></em> (DDIA) is one of the most popular books in the present times<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. It&#8217;s hardly a surprise that it is so!</p><p><a href="https://martin.kleppmann.com">Dr. Martin Kleppmann</a> has been <em>that wise man</em> &#8212; by the medium of this book &#8212; to the many <em>grasshoppers</em> who, like yours truly, have been continuously witnessing the onslaught of technologies, frameworks, and techniques, over the years. While we were mostly awed by the <em>functionality</em>, we did not always get a chance to delve deeper. Even if we did, the knowledge gained was in pockets, and to fluctuating degrees.<br><br>It&#8217;s of course true, that with experience &#8212; active or passive knowledge gain &#8212; one does get an inkling of <em>how things work (or would be working) </em> in most of the cases. However, in almost all the cases, the hypothesis does require a validation, or a course-correction, or an &#8220;a-ha!&#8221; moment. <br>The beauty of DDIA is that it provides several avenues for the all of those.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t_SI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t_SI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 424w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 848w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 1272w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t_SI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic" width="187" height="246.4814814814815" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:605,&quot;width&quot;:459,&quot;resizeWidth&quot;:187,&quot;bytes&quot;:49622,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!t_SI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 424w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 848w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 1272w, https://substackcdn.com/image/fetch/$s_!t_SI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5f9a17f-ae9d-45d4-af66-a9a6d7a7319e.heic 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Yes, the book doesn&#8217;t of course answer <em>all</em> the questions, but one feels good after having gone through a few chapters, mainly because:</p><ul><li><p>there seems to be, in some sense, a feeling of <em>technical empathy</em>, that a modern day application developer or designer can resonate with, and, </p></li><li><p>the language used is easy enough, almost conversational &#8212; so the technical terms or jargons, wherever used, are more relatable and easy to consume.</p></li></ul><p>For instance, the explanation of topics like <em>data streaming</em>, and how the usual messaging solution used to facilitate streaming work. The author has ensured that the aspects like <em>event logs</em> are discussed in an apt depth, while talking about technologies that enable <em>event sourcing </em>(e.g. Kafka, etc.).<br><br>Such an arrangement enables the reader to continue further research on the topics that she is curious about. It provides her <em>just enough</em> breadth, and a direction, to make judicious technical choices &#8212; be it during development, or while deciding on the modern-day software components.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Many thanks to my friend Subodh C., for he was the one who introduced the book to me.</p></div></div>]]></content:encoded></item><item><title><![CDATA[E-ink and TOTP++]]></title><description><![CDATA[E-ink (or E-paper) displays are so cool!]]></description><link>https://www.pugmarx.me/p/e-ink-and-totp</link><guid isPermaLink="false">https://www.pugmarx.me/p/e-ink-and-totp</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Fri, 26 Nov 2021 21:41:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/p7uK1ESC24k" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>E-ink (or E-paper) displays are so cool! I love them! I got one recently, and decided to use it for my <a href="http://localhost:8888/wordpress/2021/11/05/making-totp-fun-ok-less-boring/">TOTP project</a>, instead of the very basic OLED that I'd put. While at it, and since this was a bigger display -- I decided to fill-up the rest of the space with <em>something</em>. The post-Covid reopening of my office paved the way for that <em>something</em>!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lJUP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lJUP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 424w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 848w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 1272w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lJUP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic" width="640" height="478" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f1af9d3-5cb0-4b60-8e28-1332df683475.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:478,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55107,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lJUP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 424w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 848w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 1272w, https://substackcdn.com/image/fetch/$s_!lJUP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f1af9d3-5cb0-4b60-8e28-1332df683475.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image showing TOTP, and travel times</figcaption></figure></div><p>Since this was my first week (first few days, to be precise) at work, and since they have a new location now -- I was apprehensive about the travel time (both <em>to</em> and <em>fro</em>). And, it so happens that TomTom offers pretty useful <a href="https://developer.tomtom.com/traffic-api">Traffic API</a>s -- not just for routing, but for real-time traffic updates as well!</p><p>Once this was in place, I used the rest of the space on the display to show the <em>O2H</em> and <em>H2O</em> travel times.</p><p>...and here's ze <s>video</s> proof!</p><div id="youtube2-p7uK1ESC24k" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;p7uK1ESC24k&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/p7uK1ESC24k?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Making TOTP fun ]]></title><description><![CDATA[(ok, less boring!)]]></description><link>https://www.pugmarx.me/p/making-totp-fun-ok-less-boring</link><guid isPermaLink="false">https://www.pugmarx.me/p/making-totp-fun-ok-less-boring</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Fri, 05 Nov 2021 00:45:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So TOTPs are irksome in that one needs to pull out the phone, open the app, and then enter the code at the prompt. Now, the naysayers may say: "ah, well, you could install a command-line utility, or use an online one", to which I'd say: "Suit yourself!". For me, it would still require me to have to type or execute or, as I was doing, open something like Google Authenticator on the phone!</p><p>Guess what, <em>the humble Pi</em> comes to the rescue here as well!</p><pre><code>Something I realised while I was working on this project was that TOTPs are just a result of interplay between a unique seed string (e.g. J8hYUkig78) and your system's/device's time. Basically, that's all there's to it!</code></pre><h3>Finding the seed!</h3><p>Usually this <em>seed</em> is embedded within the QR code that these apps want you to scan while setting up the MFA. So, as one can imagine, it's just a matter of interpreting the QR code, to extract this string.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gMd2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gMd2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gMd2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg" width="345" height="746.892177589852" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:473,&quot;resizeWidth&quot;:345,&quot;bytes&quot;:45215,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gMd2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gMd2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5297d3a-98c4-426c-adba-877343a19ef1_473x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Google Lens to the rescue!</figcaption></figure></div><p>The QR can be opened in the Google Photos app, and an Instagram-ish icon at the bottom invokes Google Lens. It would spit-out something like:</p><pre><code>otpauth://totp/someunique%3identification%3string?secret=J8hYUkig78&amp;issuer=AcmeTechCo</code></pre><p>As could be evident, we're only interested in the <code>secret</code> from the above. Once you have this seed string, you may want to pat yourself on the back, as you've solved the crux of the issue. Or, not just yet -- the source application would also want you to enter the TOTPs thus generated (using the desired TOTP generation program/app/utility, so as to validate them, and tie them back to your account). So you may have to do that. Once <em>that</em>'s done, you're all set!</p><p>Now, TOTP generation programs/apps/utilities are aplenty! A simple Google search led me to a <a href="https://github.com/pugmarx/ga-cmd">Python-based CLI program</a>. The same developer advised to use his updated <a href="https://github.com/arcanericky/totp">Go-based utility</a> instead, as it had a better local encryption (note that unencrypted local <em>seeds</em> are potential security risks!), apart from other features. The only thing missing was an ARM-based binary release of the utility (since I wanted to have it running on an RPi), and the developer was gracious enough to accept <a href="https://github.com/arcanericky/totp/releases/tag/v1.0.8">my contribution</a> for it!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dADE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dADE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dADE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dADE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dADE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dADE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg" width="825" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:825,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68122,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dADE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dADE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dADE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dADE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c09c84d-5801-476d-b7ef-f4597a609f83_825x510.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">it works!</figcaption></figure></div><p>So, once the <code>totp</code> utility was installed, it was just a matter of setting up a new config using the <code>seed</code>, and giving it a name, like so:</p><pre><code>totp config add target-app-name J8hYUkig78</code></pre><p>(There are of course other ways, so that the secret isn't echoed on the CLI/bash history.)</p><p>Once this is in place, TOTPs can be generated using the command:</p><pre><code>totp target-app-name</code></pre><p>The final step is to show this on a RPi display (<a href="https://github.com/adafruit/Adafruit_SSD1306">Adafruit SSD1306</a> in my case!). This was simple using the example programs provided by Adafruit. The only modification I did was enable Python-based invocation of the above <code>totp</code> command, and the refresh it every few seconds, to display the updated OTP.</p><p>Here's a short clip of the final outcome.</p><div id="youtube2-cN6okWngE88" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;cN6okWngE88&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/cN6okWngE88?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[RPi resurrection - Pt. III - Plex Media Server]]></title><description><![CDATA[The third good use one could put the Pi to, and use it thoroughly is as a Media Server.]]></description><link>https://www.pugmarx.me/p/rpi-resurrection-pt-iii-plex-media-server</link><guid isPermaLink="false">https://www.pugmarx.me/p/rpi-resurrection-pt-iii-plex-media-server</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 05 Sep 2020 21:37:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/cbc307ac-2a80-40d1-8a1b-dd6febc3c051_391x240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The third <em>good</em> use one could put the Pi to, and use it thoroughly is as a Media Server. And that's where <a href="http://www.plex.tv">Plex</a> comes into the picture! I know, it's nothing new -- since probably the inception of RPi, there have been numerous such apps and OSs which have done the same -- XBMC ports, Kodi, and likewise. But I have had mixed experiences with them -- beyond the initial "aha!", the experience wasn't what one could "delightful!" in the long run. I think the biggest hassle for me was loading the media, to start with. This was followed by other aspects, like account management, supported formats (or the lack of it), and what not.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qoS7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qoS7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 424w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 848w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 1272w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qoS7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic" width="571" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b7262db3-c22c-4974-98fb-5ceb701e8e1d.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:571,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69597,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qoS7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 424w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 848w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 1272w, https://substackcdn.com/image/fetch/$s_!qoS7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7262db3-c22c-4974-98fb-5ceb701e8e1d.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Plex interface on Chrome mobile browser</figcaption></figure></div><p>However, Plex seems to have upped the game several notches. Or maybe the people at Plex know how to impress this Netflix-addicted population &#8212; the ones who would want to be stream on <em>any</em>. device, support both app and browser based streaming, continue from where they left off, be able to load media directly, share their (in-house) media server with their friends/family, be able to restrict content per account, etc. (I am sure you see what I did there.)<br><br>Again, I will abstain from listing down the installation steps for Plex -- there are numerous websites that have those.</p><p>Loading the media just requires following a specific and simple directory format. A spare hard disk which could auto-mount could be attached to the RPi for it. Of course, the advantage of using portable media is that one could attach it to any other media source, and directly modify the media to be made available via Plex. Or, for the geeky ones -- a cron job could be written to rsync the media over ssh to this Pi.</p><p>There is however, restrictions on the "Free lunch". If one wants to use the Plex client app, one would have to pay a nominal fee. This, of course, enables a host of features not available otherwise.<br>The "Free" option, of course, is the browser based client, which can do everything that the app can do, albeit at a slightly less of a convenience. I am not complaining, though. ;)</p>]]></content:encoded></item><item><title><![CDATA[RPi resurrection - Pt. II - NFS]]></title><description><![CDATA[The second good use you could put your Pi, more so if you have unused external HDDs lying around, is to make an NFS out of it!]]></description><link>https://www.pugmarx.me/p/pi-resurrection-pt-ii-nfs</link><guid isPermaLink="false">https://www.pugmarx.me/p/pi-resurrection-pt-ii-nfs</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Fri, 17 Jul 2020 21:34:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!J6sR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The second good use you could put your Pi, more so if you have unused external HDDs lying around, is to make an NFS out of it! I'll spare mirroring the details here, there are many good references on how to go about creating a Samba server. For example, <a href="https://magpi.raspberrypi.org/articles/samba-file-server">this one</a>.</p><p>Since my laptop's storage is limited, oftentimes it started complaining, as soon as any space-consuming operation started. At that point, I often had to make some hard life choices! :), you know, of the <em>"shall I keep the big file or zap it!"</em> kinds.</p><p>On top of that, I am not sure about others, I have realised that OSX has made working with an external HDD as painful as possible! A FAT32 formatted HDD takes forever to be recognised! To top it all, there's that eternal irk of having to "safely removing the drive". I mean c'mon. Windows has done it -- how long will OSX take??</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J6sR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J6sR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 424w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 848w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 1272w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J6sR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic" width="408" height="190.71875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:359,&quot;width&quot;:768,&quot;resizeWidth&quot;:408,&quot;bytes&quot;:15375,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!J6sR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 424w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 848w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 1272w, https://substackcdn.com/image/fetch/$s_!J6sR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F023c0efb-5d16-4c54-8ab3-ed55c790b29a.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><figcaption class="image-caption">Mounting the NFS</figcaption></figure></div><p>Anyway, so those were the reasons. But, I guess, the basic reason was: because I wanted to. :-]</p><p>Once, your NFS is in the network, on Mac, it's just a few more steps to make your new storage available, and ready to use!</p><p>The HDD did need to be formatted, as the original format of FAT32 did not go too well with the Raspberry Pi OS (erstwhile called Raspbian Buster) -- in that the auto-mount used to fail. I formatted it as FAT to keep it OS-neutral, even though there are trade-offs but the benefits outweighed!</p><p>So, yes, that it.</p>]]></content:encoded></item><item><title><![CDATA[RPi resurrection - Pt. I - Pi-hole]]></title><description><![CDATA[Couple months ago, got to know about the Pi-hole project.]]></description><link>https://www.pugmarx.me/p/rpi-resurrection-pt-i-pi-hole</link><guid isPermaLink="false">https://www.pugmarx.me/p/rpi-resurrection-pt-i-pi-hole</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 12 Jul 2020 23:47:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b3d4ef79-de5a-478a-9773-307973796f95_507x274.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Couple months ago, got to know about the <a href="https://pi-hole.net/">Pi-hole</a> project. It's an ad-block server that can you can configure at the network level. That is, it can be configured as the DNS in your home router.</p><p>Of course, the benefit of network-wide ad-blocking is that it does its job in all of your home devices. If however, your router does not allow configuring a DNS -- then you'd have to configure the DNS on a per-device level. While it may sound painful, but trust me, it's worth it!</p><p>Thanks to online advertising -- reading even a simple news article has become painful. While a lot of people use ad-block plugins, these plugins are, limited to browsers. How do you deal with ads on devices where one does not use the browser -- e.g. while playing games, etc? That's where a network-level ad-blocking gets an upper hand!</p><p>Okay, I just realised that I haven't talked about why is "RPi" there in this post's title. The thing is, I came across a post on Pi-hole as I was looking for a better (read: any) use for a legacy Pi2 -- which was lying about mostly unused -- thanks to it being trumped by newer Pis that I got later.</p><p>Seems being a Pi-hole server is one of the best uses that I could've put it to! The admin console is a rich and responsive UI, which allows your to further tweak the Pi-hole server as per your needs, for example: for explicitly allowing/denying any ad server, blocking specific keywords, etc.</p><p>Anyway, as <a href="https://en.wikipedia.org/wiki/Apache_Indian">Apache Indian</a> would have put it: '<em>nuff</em> said! Do go ahead and try it out this amazing project and may you bask in the glory of an ad-free world! And oh, btw, one doesn't <em>really</em> need a RaspberryPi for Pi-hole -- you can potentially install it on <em>anything</em> -- <em>andd</em>...there's a <a href="https://hub.docker.com/r/pihole/pihole/">Docker image</a> as well!</p>]]></content:encoded></item><item><title><![CDATA["Apology-based computing"]]></title><description><![CDATA[I came across this phrase sometime back, and was instantly intrigued by it. So, like any good samaritan, let me share what I could make of it for the larger good of humankind! While I present my assessment, I'll also highlight aspects that make it very viable in most of the computing contexts. We will also delve into how this phrase, at some level, dabbles with the aspects of modern day computing like]]></description><link>https://www.pugmarx.me/p/apology-based-computing</link><guid isPermaLink="false">https://www.pugmarx.me/p/apology-based-computing</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 15 Feb 2020 23:17:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c743e22c-ef62-461c-ab2a-7684eefd144d_800x510.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DDc_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DDc_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DDc_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DDc_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!DDc_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a417777-9b99-41f5-8cb8-39cfdebace78_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">&#8220;we are sorry&#8221;</figcaption></figure></div><p>I came across this phrase sometime back, and was instantly intrigued by it. So, like any good samaritan, let me share what I could make of it for the larger good of humankind! While I present my assessment, I'll also highlight aspects that make it very viable in most of the computing contexts. We will also delve into how this phrase, at some level, dabbles with the aspects of modern day computing like <em><a href="https://en.wikipedia.org/wiki/Eventual_consistency">eventual consistency</a></em>, the tradeoff between <em>performance</em> and <em>correctness</em>, and <em><a href="https://en.wikipedia.org/wiki/Amdahl%27s_law">Amdahl's law</a></em>.</p><p>First, let's get to understand what the phrase means.</p><p>Let me make a claim: We come across apology-based computing in our day-to-day <em>digital</em> ongoings -- be it shopping on e-com websites, chatting using our phone apps, or general browsing.</p><h3>So what is it?!</h3><p>It merely points to the fact that in this age of highly <a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing">distributed systems</a><sup>1</sup>, in a majority of situations, it's <em>ok</em> for <em>messages</em> to be delayed, go undelivered, or, just go awry! Note that <em>messages</em> here imply any sort of communication between two or more systems<sup>1</sup>.</p><p>If this sounds a bit overbearing and leads to cringing of eyes, you might get want to recall situations in which you had to "refresh the page" in order to see an update, or those times when your text or chat messages did not deliver and a cute little (!) sign appeared beside it, or, (for the technically inclined) you had to explicitly <em>invalidate</em> a cache, so that updated data is reflected quickly. Also, recall the fact that you were sort of okay with it, and in a majority of such scenarios, did not complain.</p><p>And, that's precisely it!</p><p>Over-time, as the systems have grown, and as the apps have proliferated into almost all aspects of our life -- we're becoming more and more <em>OK</em>, when every once in a while, the systems do not behave as expected. This is not just philosophical -- we humans did not become more patient overnight! Rather, over the years, something interesting has happened -- our response as intermediate or ultimate users of these systems has evolved. So much so, that a seemingly bureaucratic statement that,</p><blockquote><p>It's easier to ask for forgiveness than to get permission.</p><p>https://en.wikiquote.org/wiki/Grace_Hopper</p></blockquote><p>has found a benign presence in computing and forgiveness or apology aspects have become the order of the day!</p><p>Why did this happen, you ask? Because there's no other option!</p><p>Something of this sort is what <a href="https://g.co/kgs/MNfX8M">David Ungar</a> has deliberated upon, and proved in his iconic talk titled "<em>Everything You Know (about Parallel Programming) Is Wrong!</em>" (see embedded video).</p><p>In this talk, in the light of the above quote, David Ungar highlights how the bias in computing is leaning (or should lean) more towards something he refers to as "<em>end-to-end nondeterminism"</em>, or "<em>race-and-repair"</em>, rather than <em>correctness</em>.</p><p><em>Correctness</em> or <em>Determinism</em> comes at a cost, and despite one's best efforts, we are limited by Amdahl's law -- when it's within the confines of a system, and aspects like <a href="https://en.wikipedia.org/wiki/CAP_theorem">CAP</a>, and other <a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing">distributed system vagaries</a>, the moment a process (transaction) crosses the boundaries of one system.</p><h3>So, what do we do?</h3><p>Well, distributed systems engineers are well-aware of the phenomenon that,</p><blockquote><p>Failure is a norm rather than an exception.</p></blockquote><p>which, if you come to think of it, is a paradigm shift from the conventional thinking where we, as programmers or system architects, were told to treat it as catastrophic! However, treating failures as a norm in modern-day computing leads us to something very practical -- something we call "<em>designing for failure"</em>. <br>That is to say, we need to build systems with better resiliency, quick failure detection, fault tolerance, and, with <em>CAP</em> in perspective -- be willing to compromise on <em>correctness</em> in favour of <em>availability</em>, by being <em>eventually</em> correct! That pretty much takes care of most of the scenarios. (Of course, we're not talking about mission critical systems or transactions -- where <em>correctness</em> and/or availability, whatever be the cost, is indispensable!)</p><p>So, going back to the aforementioned scenarios, what the systems are doing by making us 'refresh' the browser window or by making us log back in, is aligning to the <em>correctness</em> part of the application. The other option, of course, would have been a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503">503</a> or something like <a href="https://en.wikipedia.org/wiki/Blue_screen_of_death">this</a>, which leads to far more painful memories!</p><p><sup>1</sup>Systems = Processes</p>]]></content:encoded></item><item><title><![CDATA[Microsoft++]]></title><description><![CDATA[Although, maybe, I am obligated to be loyal to AWS for various reasons (my work is AWS-centric, plus the fact that it facilitated my foray in the cloud), but still, over the short span of time that I have been in this field -- I am getting increasingly impressed with Microsoft.]]></description><link>https://www.pugmarx.me/p/microsoft</link><guid isPermaLink="false">https://www.pugmarx.me/p/microsoft</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 30 Nov 2019 23:53:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!kw3w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kw3w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kw3w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 424w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 848w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 1272w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kw3w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kw3w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 424w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 848w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 1272w, https://substackcdn.com/image/fetch/$s_!kw3w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dee6971-adc4-41e1-b6d6-36786f0bd0a7_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Organised chaos</figcaption></figure></div><p>Although, maybe, I am obligated to be loyal to AWS for various reasons (my work is AWS-centric, plus the fact that it facilitated my foray in the <em>cloud</em>), but still, over the short span of time that I have been in this field -- I am getting increasingly impressed with Microsoft. Their documentation on aspects like <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/">cloud design patterns</a>, <a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-overview-microservices">microservices</a>, etc., is simply impeccable -- to say the least!</p><p>Moreover, since I am a huge fan of MOOC websites like Coursera, edX, etc, not very long ago, I came across a very nice course on edX, being offered by Microsoft, called <a href="https://courses.edx.org/courses/course-v1:Microsoft+DEVOPS200.9x+2T2019/course/">Architecting Distributed Cloud Applications</a>, which, again, I loved!</p><p>Apart from cloud-specific courses, they have a lot of other, general CS courses in the offing, which are also pretty good.</p><p>Overall, I think MS has come of age under Mr. Nadela, and is delivering how it should. Kudos to the teams, who keep such useful artefacts updated, as well as open to the world.</p><p>After all, the age of silos is long gone, and collaboration is the key!</p>]]></content:encoded></item><item><title><![CDATA[Hosting a WordPress blog on AWS (for free!)]]></title><description><![CDATA[Much of what I write is a compilation of what I found on the Internet, esp.]]></description><link>https://www.pugmarx.me/p/hosting-a-wordpress-blog-on-aws</link><guid isPermaLink="false">https://www.pugmarx.me/p/hosting-a-wordpress-blog-on-aws</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 06 Oct 2019 23:48:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6Sjh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6Sjh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6Sjh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 424w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 848w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 1272w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6Sjh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/060bd54c-d443-436d-bb96-7989eda3bc30_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6Sjh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 424w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 848w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 1272w, https://substackcdn.com/image/fetch/$s_!6Sjh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F060bd54c-d443-436d-bb96-7989eda3bc30_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">wordpress on aws</figcaption></figure></div><p>Much of what I write is a compilation of what I found on the Internet, esp. P&#275;teris &#325;ikiforovs's <a href="https://peteris.rocks/blog/unattended-installation-of-wordpress-on-ubuntu-server/">post</a>. I am indebted to him, as I heavily benefitted from his post while trying to get WP working. However, since that article is a bit dated, and cannot be used verbatim -- I decided to jot this post down.</p><p>It is assumed that one would be having the domain name handy (e.g. <code>myblogsite.com</code> or <code>foobar.io</code>), before proceeding with the installation. In this post, let's go ahead with <code>foobar.io</code>.</p><p>So, <code>foobar.io</code> will be hosted on WordPress, with MySQL database, and PHP getting served via ngnix. To enable HTTPS access, we'd use a certificate generated via <a href="https://letsencrypt.org/">Letsencrypt</a>.</p><p>On AWS, a <code>t2.micro</code> EC2 instance (free-tier eligible) would suffice for this set-up. Note that this instance is free only for 1yr, 750hrs a month, so, it would be a good idea to:</p><ol><li><p>Not run any other (EC2) instance in the same AWS account</p></li><li><p>Create an AMI, once the installation is over, so that we can port our installation to a new AWS account after the 1yr period, and not have to do all this over again</p></li></ol><p>Alright, so here we go!</p><p>I. Log-in to the instance, and start with creating environment variables, which'd used throughout the installation (Lines to modify are highlighted (optional), except. for line #1 (must-do!))</p><pre><code>WP_DOMAIN="foobar.io" ## *** Change this domain *** 
WP_ADMIN_USERNAME="admin"
WP_ADMIN_PASSWORD="admin"
WP_ADMIN_EMAIL="no@spam.org"
WP_DB_NAME="wordpress"
WP_DB_USERNAME="wordpress"
WP_DB_PASSWORD="wordpress"
WP_PATH="/Applications/MAMP/htdocs/wordpress"
MYSQL_ROOT_PASSWORD="root"</code></pre><p>II. Ensure that the environment is set (assuming that the above variables are put in a file called <code>setEnv.sh</code>)</p><pre><code>source ./setEnv.sh</code></pre><p>III. Install the software, viz. nginx, MySQL and PHP (but first remove the default apache server, and upgrade the packages)</p><pre><code>sudo apt remove apache2 # we dont want the default server
sudo apt update &amp;&amp; sudo apt upgrade -y # lets stay up to date
echo "mysql-server-5.7 mysql-server/root_password password $MYSQL_ROOT_PASSWORD" | sudo debconf-set-selections
echo "mysql-server-5.7 mysql-server/root_password_again password $MYSQL_ROOT_PASSWORD" | sudo debconf-set-selections
sudo apt install -y nginx php php-fpm php-mysql php-curl php-gd mysql-server</code></pre><p>IV. Configure MySQL</p><pre><code>mysql -u root -p$MYSQL_ROOT_PASSWORD &lt;&lt;EOF
CREATE USER '$WP_DB_USERNAME'@'localhost' IDENTIFIED BY '$WP_DB_PASSWORD';
CREATE DATABASE $WP_DB_NAME;
GRANT ALL ON $WP_DB_NAME.* TO '$WP_DB_USERNAME'@'localhost';
EOF</code></pre><p>V. Configure nginx</p><pre><code>sudo mkdir -p $WP_PATH/public $WP_PATH/logs
sudo tee /etc/nginx/sites-available/$WP_DOMAIN &lt;&lt;EOF

server {
  listen 80;
  server_name $WP_DOMAIN www.$WP_DOMAIN;
  root $WP_PATH/public;
  index index.php;
  access_log $WP_PATH/logs/access.log;
  error_log $WP_PATH/logs/error.log;
  location / {
   try_files \$uri \$uri/ /index.php?\$args;
  }
 location ~ \.php\$ {
 include snippets/fastcgi-php.conf;
 fastcgi_pass unix:/run/php/php7.2-fpm.sock;
 }
}
EOF

sudo ln -s /etc/nginx/sites-available/$WP_DOMAIN /etc/nginx/sites-enabled/$WP_DOMAIN
sudo systemctl restart nginx # if this fails, check the ngnix logs before continuing</code></pre><p>VI. Install Letsencrypt, and auto-install certs (Note that in the DNS config, the domain should already be pointing to the current server public IP, as Letsencrypt would be performing domain validation)</p><pre><code>cd ~
sudo apt install letsencrypt # we'll use it later, not right now
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
sudo ./letsencrypt-auto --nginx # respond to the prompts after this</code></pre><p>VII. Finally, install WordPress</p><pre><code>sudo rm -rf $WP_PATH/public/ # !!!
sudo mkdir -p $WP_PATH/public/
sudo chown -R $USER $WP_PATH/public/
cd $WP_PATH/public/

wget https://wordpress.org/latest.tar.gz
tar xf latest.tar.gz --strip-components=1
rm latest.tar.gz

mv wp-config-sample.php wp-config.php
sed -i s/database_name_here/$WP_DB_NAME/ wp-config.php
sed -i s/username_here/$WP_DB_USERNAME/ wp-config.php
sed -i s/password_here/$WP_DB_PASSWORD/ wp-config.php
echo "define('FS_METHOD', 'direct');" &gt;&gt; wp-config.php
sudo chown -R www-data:www-data $WP_PATH/public/</code></pre><p>VIII. At this point we should be able to browse to <code>foobar.io</code>, to continue with the WP installation wizard, using the credentials we configured in the first step (<code>$WP_ADMIN_USERNAME</code>, <code>$WP_USERNAME_PASSWORD</code>).</p><p>IX. Since Letsencrypt certs expire after a given period of time, we might want to automate the renewal by creating a cron job</p><pre><code>sudo tee /etc/cron.daily/letsencrypt &lt;&lt;EOF
letsencrypt renew --agree-tos &amp;&amp; systemctl restart nginx
EOF
sudo chmod +x /etc/cron.daily/letsencrypt</code></pre><p>That's it! One should be all set now!</p><p><strong>Troubleshooting</strong>:</p><p>If, for any reason, the WP password needs to be reset, the following can be done</p><pre><code>mysql -u $WP_DB_USERNAME -p$MYSQL_ROOT_PASSWORD -d$WP_DB_NAME &lt;&lt;EOF
UPDATE `wp_users` SET `user_pass`= MD5('yourpassword') WHERE `user_login`='yourusername';
EOF</code></pre>]]></content:encoded></item><item><title><![CDATA[Streaming (only) audio using an old AppleTV (and a few nuances, thereof)]]></title><description><![CDATA[Oh, the ever-unsettled human!]]></description><link>https://www.pugmarx.me/p/streaming-only-audio-using-an-old-appletv-and-a-few-nuances-thereof</link><guid isPermaLink="false">https://www.pugmarx.me/p/streaming-only-audio-using-an-old-appletv-and-a-few-nuances-thereof</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 17 Mar 2018 23:52:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dPNd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Oh, the ever-unsettled human!</strong></p><p>In this age of wireless everything, I chose to stay 'wired' for a long time, especially when it came to music. Reason: although I don't (can't) claim to be an <em>audiophile</em>, I do appreciate hi-fidelity (hi-fi) music. Hi-fi audio is soothing even at high amplitudes, and I think good tracks deserve a&nbsp;<em>listening</em>, and not just a&nbsp;<em>hearing</em>! In other words, I am not a <code>.mp3</code> guy, but more of a <code>.wav</code> (or <a href="https://en.wikipedia.org/wiki/FLAC"><code>.flac</code></a>, if you please) person. Uncompressed/lossless audio rules!<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dPNd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dPNd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 424w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 848w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 1272w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dPNd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/926344c4-5300-4279-9d29-4e55c4094969_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dPNd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 424w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 848w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 1272w, https://substackcdn.com/image/fetch/$s_!dPNd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F926344c4-5300-4279-9d29-4e55c4094969_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Cables cables everywhere!</figcaption></figure></div><p>My audio rig is a simple (non-wireless) <a href="http://www.norgeaudio.com/norge-1000-gold.html">amp</a> and a pair of <a href="https://www.reevoo.com/p/wharfedale-diamond-9-2">monitors</a>, and it pretty much serves my purpose.</p><p>The main issue, however, was -- the amp is about 9' (9 ft.) away from my music source(s). Which means that I had to make do with a 10' 3.5mm to <a href="https://www.lifewire.com/rca-cable-definition-1082146">RCA</a> audio cable, to stream audio from the laptop, phone, etc.</p><p>This, of course, worked like a charm in terms of music quality -- any loss in fidelity was too minor to be noticed -- but this arrangement wasn't very safe. I'd to be careful about myself, and more so for others, to avoid tripping over the lengthy cable that went almost diagonally across the room. (Honestly, I was more concerned about what a human, tripping over it, would take along, since the cable was attached to one of the many precious sources at any given point in time. One can easily infer that the priority <em>wasn't</em>&nbsp;on saving the human in such a scenario.)</p><p>Anyway, so a wire-free setup was, maybe not indispensable but good-to-have. I looked into a few options, the cheapest and quite common being a Bluetooth audio receiver. There are many available on Amazon, but from my previous experiences of Bluetooth receivers, I realized that one would have to compromise on sound quality. Now, there might be fancy receivers as well, but I did not want to have to spend a lot.</p><p><strong>The rise of the Phoenix...</strong></p><p>I did, however, have an AppleTV 2 which was gathering dust, mostly because over time smarter devices/options had replaced whatever little purpose it originally used to serve. The only use left for it was extending/mirroring the Mac screen wirelessly, which isn't really a jaw-dropping feature!</p><p>Point being, I was keen on&nbsp;making use of this, mostly useless, AppleTV in the eventual, wire-free setup. From previous experience, and a bit of Googling,&nbsp; it came down to two options:</p><ol><li><p>Use the HDMI output option of AppleTV and use the audio port of the&nbsp;<em>target</em> device</p></li><li><p>Use AppleTV's <a href="https://www.howtogeek.com/241828/what-is-the-optical-audio-port-and-when-should-i-use-it/">optical audio port</a>, <em>somehow</em>.</p></li></ol><p> The first option wasn't viable because even if there are devices which are capable of extracting the audio from an HDMI source, for example, display devices like monitors, televisions or projectors -- their sound processing is, as far as I expect, very rudimentary. So, again, there's a compromise on sound quality involved. I also found a bunch of cheap <a href="http://amzn.in/aNCosG8">HDMI audio "extractors"</a> -- but they did not look very different from the Bluetooth receivers I talked about earlier.</p><p>The second option&nbsp;seemed to&nbsp;be <a href="https://www.imore.com/how-hook-fourth-generation-apple-tv-optical-audio-speakers">far more popular</a>. <strong>If only</strong>&nbsp;the amp in question (or one's AV Receiver) had an optical audio in -- I'd have been all set. But there wasn't, and hence, I wasn't.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Unj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Unj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Unj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg" width="277" height="258" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:258,&quot;width&quot;:277,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-Unj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-Unj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b87f726-428d-42d1-9c26-cf126fcf6f8a_500x500.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"> Fiio D30K</figcaption></figure></div><p>The process thus needed a "bridge" step -- a&nbsp;<em>bridging</em>&nbsp;gadget was required to accept this optical audio from AppleTV, and then somehow, magically, let me hook up the amp. Enter: <strong><a href="http://amzn.in/i9p3PyD">Fiio D30K</a></strong>! This nifty little thing does exactly that. In other words, if the amp or AV Receiver has a simple RCA/3.5mm input, this optical-to-whatever converter device would do the trick of accepting the optical audio*, and providing the converted audio as RCA/3.5mm out. There are plenty of similar devices available on Amazon, but one might want to get a decent one. I have been happy with Fiio products over the years, so I went with this one.</p><p>Anyway, that pretty much completes the setup! Once this was in place, the audio could then be streamed to my audio rig, wirelessly! Yaay!</p><p><strong>Just when you think you're all set...</strong></p><p>There was a hitch! It was in the form of flaky audio, especially when the audio file was large. I correctly suspected that&nbsp;it was because now there was too much traffic on the Wifi network (streaming audio + regular Internet use), keeping in perspective the basic (<a href="https://www.netgear.com/support/product/WGR614v1.aspx">Netgear WGR614</a>) router that I had.</p><p>It was time to separate the concerns.</p><p>Fortunately, I had a spare Wifi router lying around, in which I set-up another network dedicated to audio streaming. This, however, posed two related challenges:</p><ul><li><p>How to stream audio from the Internet (as the Internet and audio streaming on were now on two difference WLANs)</p></li><li><p>How to connect the laptop (one of the main sources) to more than one (Wifi?) networks.</p></li></ul><p> The resolution was anyone's guess: one of the networks had to be wired!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SqRL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SqRL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 424w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 848w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 1272w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SqRL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic" width="429" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bdb67b34-2920-4600-b67a-2726daaa120c.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:429,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14213,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SqRL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 424w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 848w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 1272w, https://substackcdn.com/image/fetch/$s_!SqRL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdb67b34-2920-4600-b67a-2726daaa120c.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Router config</figcaption></figure></div><p>I chose to make the streaming network wired, as the router was right next to my desk. On my laptop, this enabled me to stay on (Internet) Wifi and streaming network, at the same time. As indicated in the picture, I needed to specify that this (audio streaming ethernet) is NOT the network to seek Internet requests from, and hence I did not specify a DNS for it. [Note: IP address specification as 'manual' doesn't have anything to do with the setup -- it's just there for sentimental reasons :).]</p><p><strong>Stirred, but not shaken...</strong></p><p>A "few minor" issues still remain, but I am happy with the overall setup now. These "few minor" issues are:</p><ul><li><p>There's often a few moments of audio lag when playing streaming video -- but that's not any related to this arrangement. I remember observing this lag even in a normal setup</p></li><li><p>Streaming from Andriod (or other non-iOS devices) requires special software/apps.</p></li></ul><p><em>[* FiioD30K accepts coaxial input as well]</em></p>]]></content:encoded></item><item><title><![CDATA[The 'L' in SOLID]]></title><description><![CDATA[Uncle Bob's aptly coined SOLID Design Principles form the basis of a robust software application.]]></description><link>https://www.pugmarx.me/p/the-l-in-solid</link><guid isPermaLink="false">https://www.pugmarx.me/p/the-l-in-solid</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sat, 17 Feb 2018 03:22:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZC7Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZC7Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 424w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 848w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 1272w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce201458-a388-4bd3-b4ff-59edb4f3f020_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 424w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 848w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 1272w, https://substackcdn.com/image/fetch/$s_!ZC7Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce201458-a388-4bd3-b4ff-59edb4f3f020_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">We need a substitution!</figcaption></figure></div><p><a href="https://en.wikipedia.org/wiki/Robert_C._Martin">Uncle Bob</a>'s aptly coined&nbsp;<a href="https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">SOLID</a> Design Principles form the basis of a robust software application. Today, I want to talk about one of those principles, the <a href="http://www.oodesign.com/liskov-s-substitution-principle.html">Liskov's Substitution Principle</a> (<em>LSP</em>) because it's easy to deviate from, and a few conscious design choices can prevent us from doing so.</p><p>In the simplest terms, <em>LSP</em> suggests that:</p><blockquote><p>Any change that makes a subtype not replaceable for a supertype, should be avoided.</p></blockquote><p> Suppose, we have a&nbsp;class hierarchy like so:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EYAN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EYAN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 424w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 848w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 1272w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EYAN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic" width="321" height="236" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:236,&quot;width&quot;:321,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3484,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EYAN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 424w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 848w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 1272w, https://substackcdn.com/image/fetch/$s_!EYAN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb2d97df6-6144-47fd-a286-c3d8a5b6a0d1.heic 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>At the first glance, the relationships here seem fine, but if we carry out an <em>IS-A</em> test, the issue becomes obvious: that Tea isn't necessarily a CaffeinatedDrink&nbsp;(for instance: there's <a href="https://www.twinings.co.uk/tea/decaffeinated-tea">decaf</a>!).</p><p>Thus, this design violates <em>LSP</em>, because it indicates that <em>all</em> Teas are Caffeinated Drinks. Now, a na&#239;ve approach would be to try to <em>retrofit</em> this design, to allow for decaf teas as well -- by adding a flag or suchlike -- but that would be clumsy!</p><p>There are several ways to deal with this anomaly, and the decision can be based on the stage of development we're at, along with other factors.&nbsp;So, let's continue with our example and see how can it be dealt with:</p><ol><li><p>We know for sure that we'd need to pull Tea out of this hierarchy. Though&nbsp;Coffee looks more justified there, we can pull that out as well, to keep things crisp (<em>and also because someone told you about 'Decaf Coffee' as well!</em>).</p><p>A better option, thus, seems to be:</p><ul><li><p>For common behaviour of Teas &amp; Coffees, introduce a Drink type</p></li><li><p>Both <code>Tea</code> and <code>Coffee</code> can then be subtypes of <code>Drink</code></p></li><li><p><code>Caffeinated</code> can just be an interface which is implemented as needed</p></li></ul></li></ol><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3bEG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3bEG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 424w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 848w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 1272w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3bEG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic" width="471" height="215" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:215,&quot;width&quot;:471,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5948,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3bEG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 424w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 848w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 1272w, https://substackcdn.com/image/fetch/$s_!3bEG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ba38bb0-07c6-44f8-bfc0-c0e0604a8857.heic 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Upon this change, we don't cringe anymore to say that <code>Tea</code> <em>IS-A</em> <code>Drink</code>, with <code>Caffeinated</code> behaviour. Whereas, a <code>DecafTea</code> differs from it. Another perspective could be, <code>Coffee</code> is substitutable both for <code>Drink</code> or <code>Caffeinated</code>, but <code>DecafTea</code> is substitutable ONLY for a&nbsp;<code>Drink</code>.</p><ol start="2"><li><p>Another approach is to follow <em>Effective Java</em> [<a href="http://a.co/h3sn1pZ">Bloch, 2017</a>, Item 18]: <em>Favor composition over inheritance</em>. With this, <code>Drink</code> becomes a <em>member</em> of <code>Tea</code> and <code>Coffee</code>, and <code>Caffeinated</code> (interface) is implemented by all but, say, <code>DecafTea</code>.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JGee!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JGee!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 424w, https://substackcdn.com/image/fetch/$s_!JGee!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 848w, https://substackcdn.com/image/fetch/$s_!JGee!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 1272w, https://substackcdn.com/image/fetch/$s_!JGee!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JGee!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic" width="459" height="293" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3e90871-eac4-44d4-9365-5586bd7a9b7d.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:293,&quot;width&quot;:459,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6065,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JGee!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 424w, https://substackcdn.com/image/fetch/$s_!JGee!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 848w, https://substackcdn.com/image/fetch/$s_!JGee!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 1272w, https://substackcdn.com/image/fetch/$s_!JGee!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3e90871-eac4-44d4-9365-5586bd7a9b7d.heic 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here, we do away with the class hierarchy, and directly use the concrete instances of individual drinks. However, we do keep the <code>Caffeinated</code> behaviour separated, and again, can safely say that <code>Tea/Coffee</code> <em>IS-A</em> <code>Caffeinated</code> drink. Moreover, we're also getting a more robust design because of disallowing (class-based-) inheritance.</p><p>How do we ensure we come-up with LSP-compliant design? Well, there are few simple things that can be borne in mind while working on class associations:</p><ul><li><p><strong>Intuition</strong>: Is it sounding right? [Example: Should <code>StudentEnrollment</code> really extend <code>Student</code>, when all it wants is to access some <code>Student</code> properties?]</p></li><li><p><strong>Concatenation test</strong>: Do the Parent and Child types sound right upon concatenation? [Example: While <code>Flyer</code>+<code>Bird</code> may sound correct, a <code>Flyer</code>+<code>Chicken</code> may not. So does '<code>Flyer</code>' need to be a class type or an interface type?], and finally and most importantly,</p></li><li><p><em><strong>IS-A</strong></em><strong> test</strong>: Is the <em>IS-A</em>&nbsp;condition holding good?</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Smarter ValueObjects & an (even more) elegant Builder]]></title><description><![CDATA[Value Objects (VOs) are prevalent and needed in traditional Java programming.]]></description><link>https://www.pugmarx.me/p/smarter-valueobjects-an-even-more-elegant-builder</link><guid isPermaLink="false">https://www.pugmarx.me/p/smarter-valueobjects-an-even-more-elegant-builder</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 04 Feb 2018 02:17:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fq5l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fq5l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fq5l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 424w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 848w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 1272w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fq5l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fq5l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 424w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 848w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 1272w, https://substackcdn.com/image/fetch/$s_!fq5l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26f88ad4-f63d-4f2b-bef6-21cd6e5916b3_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Smart Builder!</figcaption></figure></div><p><a href="https://martinfowler.com/bliki/ValueObject.html">Value Objects</a> (VOs) are prevalent and needed in traditional Java programming. They're almost everywhere --&nbsp; to hold information within a process, for message-passing, and various other areas.</p><p>Apart from having <code>getters</code> and <code>setters</code> for the properties, on several occasions, there's a requirement for these VOs to implement <code>equals()</code> and <code>hashCode()</code>. Developers usually hand-write these methods or use the modern IDE templates to generate them. This works fine initially or until there's a need to update the VOs with one or more additional properties.</p><p>With an update, the baggage that comes with new properties includes:</p><ul><li><p>a new set of <code>getters</code> and <code>setters</code>,</p></li><li><p>updates required to <code>equals()</code>, <code>hashCode()</code>, and,</p></li><li><p>update required to <code>toString(),</code>if needed</p></li></ul><p> This is, of course, cumbersome, error-prone, and the simple VO soon starts looking like an airplane cockpit!</p><p>Google's <a href="https://github.com/google/auto/tree/master/value">AutoValue</a> framework is a smart approach to address this issue. With just a couple of annotations, almost all of the "junk" is done away with, and the class becomes smarter -- any future property updates, including <code>getters</code>, <code>setters</code>, as well as <code>equals()</code>*, <code>hashCode()</code>** and <code>toString()</code> are all handled automagically!</p><p>The VO then just looks like a basic set of properties of the given type, like so:</p><pre><code>import com.google.auto.value.AutoValue;
@AutoValue abstract class CartItem { abstract int itemCode();

abstract int quantity();

abstract int price();

static CartItem create(int itemCode, int quantity, int price) { 
return new AutoValue_CartItem(itemCode, quantity, price); 
} 
} </code></pre><p>Note the default presence of a static factory method <code>create()</code>, as suggested in Effective Java [<a href="http://amzn.in/60Xx8o4">Bloch, 2017</a>], Item 2.</p><p>The use of this annotated VO would be no different from a typical one. For instance, the <code>CartItem</code> defined above would have a simple invocation like this:</p><pre><code>@Test
public void create() throws Exception {
    CartItem item1 = CartItem.create(10,33, 12);
    CartItem item2 = CartItem.create(10,33, 12);</code></pre><p>assertEquals(item1, item2); // this would be true } Apart from the default support for a static factory, AutoValue also supports Builder classes, within the VOs. Armed with this knowledge, let's take another jab at the example in&nbsp;<a href="http://tech.pugmarx.me/2018/01/24/the-elegance-of-builder-pattern/">my previous post</a> on Builders. We continue with the same Cake example and add the required annotations and modifiers. The updated version of the class would then be:</p><pre><code>import com.google.auto.value.AutoValue;
@AutoValue abstract class Cake { // Required params abstract int flour(); abstract int bakingPowder();

// Optional params abstract int eggs(); abstract int sugar(); abstract int oil();

static Maker builder(int flourCups, int bkngPwdr) { // return builder instance with defaults for non-required field return new AutoValue_Cake.Builder().flour(flourCups).bakingPowder(bkngPwdr).eggs(0).sugar(0).oil(0); }

@AutoValue.Builder abstract static class Maker { abstract Maker flour(int flourCups); abstract Maker bakingPowder(int bkngPwdr); abstract Maker eggs(int eggCount); abstract Maker sugar(int sugarMg); abstract Maker oil(int oilOz);

abstract Cake build(); } } </code></pre><p>Observe that:</p><ul><li><p>the member <code>Builder</code> class (named <code>Maker</code> here) just needs to be marked with <code>@AutoValue.Builder</code> annotation, and the framework takes care of everything else</p></li><li><p>in the parent class, we could also have had a no-arg <code>builder()</code> method but we specifically want to&nbsp;have only one way of building this class -- with the required params</p></li><li><p>as shown above, the optional parameters should be set to their default values since we want the flexibility of choosing only the relevant optional params. [With non-primitive members, <code>@Nullable</code> can be used.]</p></li></ul><p> Just to complete the discussion, here is an example of the ease with which this new <code>builder</code> can be invoked:</p><pre><code>@Test
public void makeCakes() {
// Build a cake without oil Cake cakeNoOil = Cake.builder(2, 3).sugar(2).eggs(2).build();

assertNotNull(cakeNoOil);

// Check that it has 0 oil assertEquals(0, cakeNoOil.oil()); // default

// Make cake with oil Cake cakeWOil = Cake.builder(2, 3).sugar(2).oil(1).eggs(2).build();

// Obviously, both the cakes are different assertNotEquals(cakeNoOil, cakeWOil); // valid

// Another cake that's same as cake w/ oil Cake anotherCakeWOil = Cake.builder(2, 3).sugar(2).oil(1) .eggs(2).build();

assertEquals(cakeWOil, anotherCakeWOil); // valid 
} </code></pre><p>There are <a href="https://github.com/google/auto/blob/master/value/userguide/index.md#how-do-i">many other fine-grained things</a> that can be done while using AutoValue, like specifying <code>getters</code> for specific properties or customizing <code>toString()</code>, etc.</p><p>It's impressive&nbsp;how AutoValue facilitates writing and static factory methods and builders quickly -- taking the headache out of defining and updating VOs.</p><p>[Full implementation of the abovementioned example is <a href="https://github.com/pugmarx/bettercode/tree/master/src/main/java/me/pugmarx/bettercode/autovalue">here</a>.]</p><p>Further reading:</p><ol><li><p><a href="https://github.com/google/auto/blob/master/value/userguide/builders.md">AutoValue with Builders</a></p></li><li><p><a href="https://projectlombok.org">Project Lombok</a> also addresses the VO update issue, along with other things</p></li></ol><p> * Effective Java [<a href="http://amzn.in/60Xx8o4">Bloch, 2017</a>], Item 10 ** Effective Java [<a href="http://amzn.in/60Xx8o4">Bloch, 2017</a>], Item 11</p>]]></content:encoded></item><item><title><![CDATA[About Books: Java]]></title><description><![CDATA[When I was writing the last post, I realized how much I used to be in awe of Effective Java [Bloch, 2017].]]></description><link>https://www.pugmarx.me/p/amazing-java-books</link><guid isPermaLink="false">https://www.pugmarx.me/p/amazing-java-books</guid><pubDate>Thu, 25 Jan 2018 15:12:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4d57ce3a-6468-490b-be2b-1ec2a189de53_240x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ScYG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ScYG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 424w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 848w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 1272w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ScYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic" width="240" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:240,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15702,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ScYG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 424w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 848w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 1272w, https://substackcdn.com/image/fetch/$s_!ScYG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a38b731-e793-4d4e-a16b-b2c2cbb915b3.heic 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Effective Java-Bloch</figcaption></figure></div><p>When I was writing the last post, I realized how much I used to be in awe of <em>Effective Java</em> [<a href="http://amzn.in/hWiF5dh">Bloch, 2017</a>]. It was a book that covered what no other did. It wasn't just&nbsp;<em>coding --</em>&nbsp;there're plenty of books where one could learn&nbsp;<em>"Your first Java Program"</em>&nbsp;and beyond,&nbsp;and throw an air punch. It wasn't also about language syntax and semantics -- <em>Java Complete Reference</em> [<a href="http://amzn.in/fUobqYK">Schildt, 2017</a>] fitted the bill there*, or OOPs (every other Java book started with OOPs concepts). Rather, <em>Effective Java</em> covered the basics of writing&nbsp;elegant&nbsp;Java code, and as a by-product, also underlined how easy was it to be swayed away by 'convention'. I wouldn't recommend it as a Java learner's <em>first</em> book. But it should very well be one's&nbsp;<em>second</em> Java book and the one that she/he would keep revisiting throughout the programming career.</p><p>Ever since I've picked it up again, it's become tough to keep it aside. With each of its topic, I realize, over time, how much have I drifted away from the delight of writing&nbsp;<em>good</em> code, and how much do I need to still learn. Go read it now if you haven't had a chance yet. What's more, the much-awaited 3/E, which covers Java 7, 8 and 9, is out now!</p><p>While on this topic, let me talk about another one of my favourite books on Java --&nbsp;<em>Thinking in Java&nbsp;</em>[<a href="http://amzn.in/5NJbTkg">Eckel, 1998</a>].</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HjZT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HjZT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HjZT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg" width="234" height="310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:310,&quot;width&quot;:234,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HjZT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HjZT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd41d7687-6a97-4664-8626-85ff92bf92fe_378x500.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">ThinkingInJava-Eckel</figcaption></figure></div><p> This is the book which I considered as a Java developer's Bible at one point in time. Since there are no new editions after the 4/E, the syntactical parts might be a bit obsolete now. But still, in my opinion, it's the best book to get one's (Java &amp; OOPS) fundamentals in place.</p><p>* <em>I have always found Java Complete Reference a bit too elaborate to my liking. Most of which is about the language syntax and semantics. All of that might have been useful in the early days of the Internet when it wasn't that easy to look up things online. But I doubt if that's needed now.</em></p>]]></content:encoded></item><item><title><![CDATA[The elegance of Builder pattern]]></title><description><![CDATA[Paraphrasing Josh Bloch in Effective Java [Bloch, 2017, Item 2]:]]></description><link>https://www.pugmarx.me/p/the-elegance-of-builder-pattern</link><guid isPermaLink="false">https://www.pugmarx.me/p/the-elegance-of-builder-pattern</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Wed, 24 Jan 2018 02:59:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XY2e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XY2e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XY2e!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 424w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 848w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 1272w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XY2e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/645eeae3-53b7-472e-a071-b1f08b4e646a_800x512&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XY2e!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 424w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 848w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 1272w, https://substackcdn.com/image/fetch/$s_!XY2e!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F645eeae3-53b7-472e-a071-b1f08b4e646a_800x512 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">elegant builder</figcaption></figure></div><p>Paraphrasing Josh Bloch in Effective Java [<a href="http://amzn.in/hLEDuE1">Bloch, 2017</a>, Item 2]:</p><p>While creating objects, in cases where the number of <em>optional</em> parameters of an object is considerable, say 4 or more, one might think of <em>static factory method</em>s [<a href="http://amzn.in/hLEDuE1">Bloch, 2017</a>, Item 1] as a solution -- but they're more suitable for a small set of parameters. When there are several optional params, static factories cannot be used as it's cumbersome to imagine and cater to&nbsp;<em>all</em> possible parameter combinations. Another approach that's proposed in such cases is using JavaBeans&nbsp;<a href="http://www.javapractices.com/topic/TopicAction.do?Id=84">but it has its own shortcomings</a>.</p><p>Therefore, we usually go with multiple (telescoping) constructors for such requirements. For example:</p><pre><code>public Cake(int oilTbsp, int flourMg){
  this(oilTbsp, flourMg, 0);
}
public Cake(int oilTbsp, int flourMg, int eggCount){ this(oilTbsp, flourMg, eggCount, 0); }

public Cake(int oilTbsp, int flourMg, int eggCount, int bakingPowderMg){ this(oilTbsp, flourMg, eggCount, bakingPowederMg); }

//...</code></pre><p>Such implementations, although purpose-serving, are a bit contrived in that the class client needs to tally the parameters accurately. Consider a large parameter list, and this would be an overkill.</p><p>A variation of Builder pattern [<a href="http://amzn.in/1ZJ58RE">Gamma, 1995</a>], is what Bloch suggests, for such cases. In it, a builder class is a static member of the class it builds, for example:</p><pre><code>public class Cake{
  //...
  private Cake(Builder builder){
    //...
  }</code></pre><p>public static class Builder{ //... } } Since the original constructor is hidden, the client first gets a reference to the Builder class --&nbsp;passing all the <em>required</em> params to its constructor or static factory. The client then calls setters on the returned builder object to set the <em>optional</em> parameters of interest. Finally, the client makes a call to the <code>build()</code> method to generate an <em>immutable</em> object. Since the builder setter methods return the builder itself, the invocations can be chained, like so:</p><pre><code>// Set only the parameters of interest
Cake cake = new Cake.Builder(350, 45).egg(2).sugar(240).cocoa(35)...build();</code></pre><p> As is apparent, this is intuitive as well as concise.</p><p>A builder can be further enhanced by enabling it to build more than one object, based on parameters. One has to be cautious, however, to disallow building an object of an inconsistent state. This can be ensured by validating the passed parameters as early as possible&nbsp;and throwing a suitable exception.</p><p>Builders can also be used to automate certain tasks&nbsp;and fill in the fields. For example, autoincrementing the object Id, etc.</p><p>As Josh Bloch advises, we should&nbsp;be using Builders as often as possible, especially in cases where the number of parameters is significant. They're a simple and elegant alternative to&nbsp;<em>telescoping constructors</em> or JavaBeans.</p><p>[Full&nbsp;implementation of the Cake builder example is <a href="https://github.com/pugmarx/bettercode/tree/master/src/main/java/me/pugmarx/bettercode">here</a>.]</p>]]></content:encoded></item><item><title><![CDATA[Tying snips.ai, Strava &#038; Google Speech Engine]]></title><description><![CDATA[So, this happened a couple months ago, and I had lots of fun doing it (watch the video):]]></description><link>https://www.pugmarx.me/p/tying-snips-ai-strava-google-speech-engine</link><guid isPermaLink="false">https://www.pugmarx.me/p/tying-snips-ai-strava-google-speech-engine</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Mon, 18 Dec 2017 20:15:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/VFvpalq6r3A" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So, this happened a couple months ago, and I had lots of fun doing it (watch the video):</p><div id="youtube2-VFvpalq6r3A" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;VFvpalq6r3A&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/VFvpalq6r3A?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>A detailed post would follow. (And yes, as mentioned in the video description, kindly ignore the choice of LED colours&nbsp;:)).</p>]]></content:encoded></item><item><title><![CDATA[Paxos (really) Simplified.]]></title><description><![CDATA[I was struggling to fully grasp Paxos, and came across this excellent explanation by Heidi Howard that simplifies it further. It's a good watch!]]></description><link>https://www.pugmarx.me/p/paxos-really-simplified</link><guid isPermaLink="false">https://www.pugmarx.me/p/paxos-really-simplified</guid><dc:creator><![CDATA[pugmarx]]></dc:creator><pubDate>Sun, 24 Sep 2017 00:49:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/s8JqcZtvnsM" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was struggling to fully grasp <a href="http://lamport.azurewebsites.net/pubs/paxos-simple.pdf">Paxos</a>, and came across this excellent explanation by <a href="https://twitter.com/heidiann360?lang=en">Heidi Howard</a>&nbsp;that simplifies it further.&nbsp;It's a good watch!</p><div id="youtube2-s8JqcZtvnsM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;s8JqcZtvnsM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/s8JqcZtvnsM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item></channel></rss>