<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>dnlserrano</title>
    <description>My blog</description>
    <link>https://dnlserrano.github.io//</link>
    <atom:link href="https://dnlserrano.github.io//feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Tue, 14 Jul 2026 02:46:40 +0000</pubDate>
    <lastBuildDate>Tue, 14 Jul 2026 02:46:40 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Monk scale</title>
        <description>&lt;p&gt;During early Ancient Greece (400s BC), whilst Hippocrates pioneered medicine and particularly skin disease categorisation, psoriasis (“psora”, for “itch”) and lepra (“lopos”, for “epidermis”) were often conflated. Much later during the Roman empire, another medical encyclopaedist by the name of Aulus Cornelius Celsus would go on to describe the skin condition and conflate it this time with impetigo, but still charaterise it by its tell tale signs to the human eye: “red skin covered with scales.”&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/thomas-fitzpatrick.jpg&quot; /&gt;
    &lt;i&gt;Dr. Thomas Fitzpatrick&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Some 2000 years later, Thomas Fitzpatrick was born. And with him, medicine would be changed forever. Fascinated by skin as an organ, Dr. Fitzpatrick is hailed as the father of modern dermatology. He would go on to develop PUVA treatment to combat psoriases and other skin disorders. As part of PUVA, he created what is known today as the &lt;a href=&quot;https://en.wikipedia.org/wiki/Fitzpatrick_scale&quot;&gt;Fitzpatrick scale&lt;/a&gt; of human skin phototypes. It described initially four and later six different skin tones by their level of ability to burn or tan. Put more technically, it is used to estimate the response of your skin type to UV (ultraviolet) light:&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/fitzpatrick-scale.png&quot; /&gt;
    &lt;i&gt;Fitzpatrick scale&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Dr. Fitzpatrick went on to develop some of the early modern sunscreens and his Fitzpatrick scale would most recently be used to create diverse and inclusive emoji modifiers to depict different human skin tones (instead of the standard universal smiley face yellow), with the five modifiers being a direct mapping of it, with types I and II merged together.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/emoji-modifiers.jpg&quot; /&gt;
    &lt;i&gt;Emoji modifiers based on the Fitzpatrick scale&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Different forms of categorising human skin tones have existed and they have been applied as part of different scientific studies. Research about &lt;a href=&quot;https://www.jstor.org/stable/j.ctt6wpzpb&quot;&gt;socio-economic impact&lt;/a&gt; of skin tone (fairness, opportunity), others around &lt;a href=&quot;https://en.wikipedia.org/wiki/Von_Luschan%27s_chromatic_scale&quot;&gt;anthropology&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Renato_Biasutti&quot;&gt;diversity across geographic areas&lt;/a&gt;. All of the proposed methods seem very much flawed from the get go, though. They all depend on subjective measurements that are bottlenecked by the human ability to distinguish skin tones or colors under a specific environment (e.g., dawn, day light, evening). In that regard, nothing beats the scientific accuracy of something like &lt;a href=&quot;https://en.wikipedia.org/wiki/Spectrophotometry&quot;&gt;spectrophotometry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As machine learning and AI models were growing in adoption, questions around fairness and bias &lt;a href=&quot;https://www.bbc.com/news/technology-33347866&quot;&gt;started to arise&lt;/a&gt; (2015). Early research focused on the well-established Fitzpatrick scale of six categories to stratify human skin tone, as that was at the time deemed a good enough proxy. &lt;a href=&quot;https://www.media.mit.edu/projects/gender-shades/overview/&quot;&gt;Gender Shades&lt;/a&gt; (2017) and other studies surfaced consistent issues, particularly on darker skin tones. E.g., women with type VI Fitzpatrick skin tone were incorrectly gendered by AI/ML systems in almost 50% of the cases, “the chances (…) of being correctly gendered [come] close to a coin toss”.&lt;/p&gt;

&lt;p&gt;Google’s response to the need for something better was the &lt;a href=&quot;https://skintone.google/get-started&quot;&gt;Monk Skin Tone Scale&lt;/a&gt;. In 2019, &lt;a href=&quot;https://www.ellismonk.com/&quot;&gt;Dr. Ellis Monk&lt;/a&gt; partnered with Google to develop a new scale comprised of 10 different skin tones with the intention of having “an inclusive skin tone scale.”&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/monk-scale.png&quot; /&gt;
    &lt;i&gt;Monk Skin Tone Scale&lt;/i&gt;
&lt;/center&gt;

&lt;blockquote&gt;
  &lt;p&gt;Data set construction, data annotation, and, ultimately, the algorithms that flow from these processes in facial recognition software, self-driving cars recogntion of their environments, automatic image classification in photos and videos (e.g., skin tone classification, gender classification, etc.), and more, are all vulnerable to biases when the standard of measurement for skin tone is not as inclusive as it could or should be.&lt;/p&gt;

  &lt;p&gt;– in &lt;a href=&quot;https://osf.io/preprints/socarxiv/pdf4c&quot;&gt;The Monk Skin Tone Scale (MST)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dr. Monk hits it right in the nail, going into detail of how models are trained and the influence of the quality of (ideally diverse) datasets on the final byproduct of such training, i.e., the model:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For example, lack of comprehensive and diverse training data sets (in terms of race and skin tone) is one leading explanation for facial recognition inaccuracies (e.g., spurious matches) and  inaccuracies  in  automated,  machine-learning  driven  image  and  video  classifiers. In short, similar issues plague both pulse oximetry and machine learning and artificial intelligence (e.g., light-sensing and computer vision).&lt;/p&gt;

  &lt;p&gt;– in &lt;a href=&quot;https://osf.io/preprints/socarxiv/pdf4c&quot;&gt;The Monk Skin Tone Scale (MST)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/ellis-monk.jpg&quot; /&gt;
    &lt;i&gt;Dr. Ellis Monk&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;We’ve come a long way since the Gender Shades study, and nowadays transparency and anti-bias are key components of reputational cost for companies working with artificial intelligence. And unlike greenwashing opportunities for off-loading carbon to remote locations (and usually not verifiable by end users), AI bias is often times visible first-hand on user’s devices and apps we use everyday. To that extent, &lt;a href=&quot;https://github.com/google-ai-edge/mediapipe/blob/master/docs/solutions/models.md&quot;&gt;model cards&lt;/a&gt;–standard practice to “describe a model DNA”, including its performance, details on how it has been trained, what and how it should be used for–have started to include ethical considerations, including bias and geographical information, as well as fairness evaluation results. E.g., precision and recall metrics for face detection for model &lt;a href=&quot;https://drive.google.com/file/d/1d4-xJP9PVzOvMBDgIjz6NhvpnlG9_i0S/preview&quot;&gt;MediaPipe BlazeFace Short Range&lt;/a&gt; are reported across gender, skin tone and subregion.&lt;/p&gt;

&lt;p&gt;At &lt;a href=&quot;https://onfido.com&quot;&gt;Onfido/Entrust&lt;/a&gt;, we’re doing something similar via our &lt;a href=&quot;https://onfido.com/wp-content/uploads/2022/09/building-ai-without-bias.pdf&quot;&gt;Building AI without bias&lt;/a&gt; whitepaper, and as we continue to ship new models to our customers. As part of our go/no-go decision to releasing a new model, we bake in several of these metrics.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/building-ai-without-bias.jpg&quot; /&gt;
    &lt;i&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://onfido.com/wp-content/uploads/2022/09/building-ai-without-bias.pdf&quot;&gt;Building AI without bias&lt;/a&gt; whitepaper by Onfido/Entrust&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;We shouldn’t stick to skin tone as part of bias though. As hinted at by the Gender Shades study, the Google model cards and the Onfido/Entrust report above, other examples of biases in AI/ML systems include, but are not limited to: age, gender, geography, culture, race (&lt;a href=&quot;https://skintone.google/recommended-practices&quot;&gt;not to be confused with skin tone&lt;/a&gt;), and other non-trivial dimensions relevant to your business, e.g., low-end vs. high-end devices, mobile device operating system and platform used (e.g., iOS, Android, Web browser).&lt;/p&gt;

&lt;h3 id=&quot;data-data-data&quot;&gt;Data, data, data&lt;/h3&gt;

&lt;p&gt;Data is crucial in the process of training the best, fairer models according to recommended practices of curating balanced datasets. Meta and Google are two of the companies pioneering here and putting their money where their mouth is, by open-sourcing datasets that directly contribute to a more just landscape of AI/ML development. With their release of the &lt;a href=&quot;https://skintone.google/mste-dataset&quot;&gt;MST-E dataset&lt;/a&gt; and the &lt;a href=&quot;https://ai.meta.com/datasets/casual-conversations-v2-dataset/&quot;&gt;Casual Conversations v2 dataset&lt;/a&gt;, both of these companies are doing their part to setting a higher bar for machine learning performance when it comes to diverse representation.&lt;/p&gt;

&lt;p&gt;An example of a past dataset available &lt;em&gt;in the wild&lt;/em&gt; is the &lt;a href=&quot;https://susanqq.github.io/UTKFace/&quot;&gt;UTKFace dataset&lt;/a&gt; from 2017, maintained by the University of Tennesse, Knoxville, USA. In it the differences in balancing between different skin tones is evident when comparing the Fitzpatrick scale to the Monk scale.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/monk-scale/utkface.png&quot; /&gt;
    &lt;i&gt;UTKFace dataset&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Using a script to approximate a person’s skin tone via the Monk Scale or Fitzpatrick Scale—by averaging color values to derive RGB representations with the help of the &lt;a href=&quot;https://github.com/ChenglongMa/SkinToneClassifier&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stone&lt;/code&gt;&lt;/a&gt; Python library—we quickly encounter issues with the Monk Scale, highlighting a lack of representation. For the Fitzpatrick Scale, identifying five faces per level is efficient, with the script completing in under a minute. However, with the Monk Scale, the process slows down dramatically, needing an escape hatch in the code to cap execution time at five minutes. This was tested on a standard M1 Mac with unoptimized, adapted &lt;a href=&quot;https://claude.ai/&quot;&gt;Claude-generated&lt;/a&gt; code. And the escape hatch? It skips the need for finding the total of 5 required faces for skin tone 10. Exactly what you’d want to avoid in a real-world AI/ML training scenario.&lt;/p&gt;

&lt;center&gt;
    &lt;a target=&quot;_blank&quot; href=&quot;/img/monk-scale/fitzpatrick-monk-samples.jpg&quot;&gt;
        &lt;img src=&quot;/img/monk-scale/fitzpatrick-monk-samples.jpg&quot; /&gt;
    &lt;/a&gt;
    &lt;i&gt;Fitzpatrick vs. Monk scale samples on the UTKFace dataset&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;You can also see various other issues with the approach. We did not ignore black and white images, and as such there are some matches for level 9 in the Monk scale that don’t make sense. Some additional mismatches on level 2. On the other hand, we can see the same black and white issue on the Fitzpatrick scale, but happening on levels 1 and 3, instead. Some pre-processing and additional cross-validation would be required to achieve production-grade data segmentation by considering a combination of, e.g., subregion, nationality, ethnicity.&lt;/p&gt;

&lt;p&gt;Plotting the bar charts for the ~23K data points in the UTKFace dataset for each of the skin tone scales under analysis yields the following:&lt;/p&gt;

&lt;center&gt;
    &lt;a target=&quot;_blank&quot; href=&quot;/img/monk-scale/fitzpatrick-monk-bar-chart.jpg&quot;&gt;
        &lt;img src=&quot;/img/monk-scale/fitzpatrick-monk-bar-chart.jpg&quot; /&gt;
    &lt;/a&gt;
    &lt;i&gt;Fitzpatrick vs. Monk scale bar chart on the UTKFace dataset&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;As you can see, Monk Scale is making even more evident the under-representation of darker skin tone individuals in this dataset.&lt;/p&gt;

&lt;p&gt;When analsing the Casual Conversations v2 dataset from &lt;a href=&quot;https://research.facebook.com/blog/&quot;&gt;Meta&lt;/a&gt;, one can observe a more bell-like curve typical of a normal distribution of skin tones. In absolute numbers though, we might get the feeling arguably more needs to be done.&lt;/p&gt;

&lt;center&gt;
    &lt;a target=&quot;_blank&quot; href=&quot;/img/monk-scale/casual-conversations-fitzpatrick-monk-bar-chart.jpg&quot;&gt;
        &lt;img src=&quot;/img/monk-scale/casual-conversations-fitzpatrick-monk-bar-chart.jpg&quot; /&gt;
    &lt;/a&gt;
    &lt;i&gt;Fitzpatrick vs. Monk scale bar chart on the Casual Conversations v2 dataset&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;In the aforementioned UTKFace dataset, we had ~0.01% of representation for individuals of the darkest skin pigment. In the Casual Conversations v2 dataset that goes to ~0.14%, i.e., not much better. Is this representative of the world population? I’m not sure, but a &lt;a href=&quot;https://www.perplexity.ai/search/what-s-the-average-percentage-6M6e3HInQtq_1N4SDRvk0w&quot;&gt;quick Perplexity search seems to point not&lt;/a&gt;. Even if so, we know population-like stratification might not suffice in making algorithms fair and unbiased, since neural nets are very data hungry.&lt;/p&gt;

&lt;p&gt;But you shouldn’t think this is the end of it and that companies are giving up. Indeed these datasets only form the basis of discussion and examples of best practices and recommendations. It is up to all of us in the AI/ML space to make the best out of pre-processing and training stages, incorporating these best practices and making sure our proprietary datasets, the ones our specific models feed off of, often times with millions more data points and terabytes (or even &lt;a href=&quot;https://seifeur.com/chat-gpt-4-data-size/&quot;&gt;petabytes&lt;/a&gt;) of data, are well balanced. And we should make sure we have the proper guard-rails in place to make incremental releases with ever-improving levels of fairness and anti-bias.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Races have never existed in nature. They were formulated by people who had opinions and very few facts.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://sites.psu.edu/ninajablonski/&quot;&gt;Dr. Nina Jablonski&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the 70s, Dr. Fitzpatrick was studying the effects of PUVA on different human skin tones, particularly white skin. Its eurocentricity lead to constructive and well-placed criticism. Today, we continue to face data imbalance issues in &lt;a href=&quot;https://www.jaad.org/article/S0190-9622(21)02649-9/fulltext&quot;&gt;dermatology research&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Machine learning will likely play a key role in the future of dermatology. Diligence and transparency are needed to prevent new health care disparities in patients with skin of color.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’ll have to continue to adapt our data science best practices in order to guarantee we rip the benefits of the tidal wave of artificial intelligence, from financial services to transportation, law, energy, scientific research, medicine and many other verticals.&lt;/p&gt;

&lt;p&gt;We keep pushing.&lt;/p&gt;

&lt;center&gt;
    &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/crD1tzTlE4o?si=FZtHAwt3Um07S9bQ&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/center&gt;
</description>
        <pubDate>Tue, 31 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2024/12/31/monk-scale.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2024/12/31/monk-scale.html</guid>
        
        
      </item>
    
      <item>
        <title>When machines could see you</title>
        <description>&lt;p&gt;In 2024, &lt;a href=&quot;https://en.wikipedia.org/wiki/Geoffrey_Hinton&quot;&gt;Geoffrey Hinton&lt;/a&gt; was awarded the Nobel Prize for his contributions to artificial intelligence. A key milestone in his work came in 2012 with AlexNet, a neural network he developed with his students &lt;a href=&quot;https://en.wikipedia.org/wiki/Alex_Krizhevsky&quot;&gt;Alex Krizhevsky&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Ilya_Sutskever&quot;&gt;Ilya Sutskever&lt;/a&gt;, sparking a revolution in AI. But more than a decade earlier, in 2001, &lt;a href=&quot;https://en.wikipedia.org/wiki/Paul_Viola&quot;&gt;Paul Viola&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Michael_Jones_(scientist)&quot;&gt;Michael Jones&lt;/a&gt; first gave machines the ability to see you in real-time.&lt;/p&gt;

&lt;p&gt;Seen in historical context, Viola-Jones is nothing short of a technological sleight of hand. While the individual pieces of its framework—Haar features, cascades, and classifiers—weren’t revolutionary on their own, the brilliance lay in how they were stitched together to outmaneuver the brutal hardware constraints of the early 2000s. It didn’t provide the level of generality of deep neural networks that were to come later, but it did something those networks couldn’t yet dream of: it unlocked real-time face detection, reshaping our interaction with machines. From cameras that automatically framed smiles to surveillance systems quietly scanning the streets, Viola-Jones wasn’t just a step forward—it was the moment machines began truly seeing us.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/viola-jones.jpg&quot; /&gt;
    &lt;i&gt;Faces detected by the Viola-Jones algorithm&lt;/i&gt;
&lt;/center&gt;

&lt;h1 id=&quot;a-pivotal-moment&quot;&gt;A pivotal moment&lt;/h1&gt;

&lt;p&gt;The Viola-Jones algorithm, as it came to be known, was introduced in the seminal paper &lt;a href=&quot;https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf&quot;&gt;“Rapid Object Detection using a Boosted Cascade of Simple Features”&lt;/a&gt; at the 2001 IEEE Conference on Computer Vision and Pattern Recognition (CVPR). A mix of academic researchers, industry professionals, and students were eagerly waiting, keenly interested in seeing how this breakthrough could push the boundaries of what computers were able to achieve. Most sitting in that conference room didn’t know at the time that they were witnessing first-hand an inflection point on machine perception. Ten years later, the Viola-Jones paper would be awarded the Longuet-Higgins Prize for most significant contributions to the Computer Vision field in that decade.&lt;/p&gt;

&lt;p&gt;The core intuition behind Viola-Jones is that certain facial regions, like the eyes, nose, and mouth, are critical for identifying human faces. In greyscale front-facing images (assuming lighting is balanced), you’ll notice key patterns: the eye-brow region tends to be darker at the top (brows) and lighter below (eyes), while the nose bridge appears brighter compared to the adjacent cheeks.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/haar.jpg&quot; /&gt;
    &lt;i&gt;Haar-like features type 2 and type 3&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Viola-Jones takes this intuition and encodes it into Haar-like features–simple contrast-based patterns that can capture these facial structures. Reminding you of convulotional neural networks yet? In fact, the algorithm uses around 6,000 Haar features and scans these across small sub-windows (24x24 pixels) throughout the image. Each Haar feature serves as a weak learner, detecting basic patterns, and when combined into an ensemble by running a variation of AdaBoost, a boosted feature learning algorithm, they form an effective classifier.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/cnn.jpg&quot; /&gt;
    &lt;i&gt;Convolutions from later deep learning methods &lt;br /&gt; Both convolutions and Haar-like features detect patterns and edges&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;To make the detection fast and scalable, Viola-Jones introduces a cascade of classifiers. Early stages quickly eliminate non-face regions, allowing more computational effort to focus only on promising, face-like areas. This cascading structure ensures the algorithm is both efficient and effective, making real-time face detection possible.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/cascade.jpg&quot; /&gt;
    &lt;i&gt;Cascading logic used by Viola-Jones to quickly discard non-face regions&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Its versatility and efficiency established Viola-Jones as a foundational technology that significantly shaped the landscape of machine vision in everyday devices.&lt;/p&gt;

&lt;h1 id=&quot;the-human-brain&quot;&gt;The human brain&lt;/h1&gt;

&lt;p&gt;The human brain identifies faces primarily through the Fusiform Face Area (FFA), a specialized region in the temporal lobe responsible for processing faces holistically. Rather than breaking a face into individual parts (like just the eyes or mouth), the FFA recognizes the entire face as a unified whole.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/brain.jpg&quot; /&gt;
    &lt;i&gt;Highlight of the FFA region of the human brain&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;This holistic perception explains the Thatcher Effect, where flipping only the eyes and mouth in an upside-down face makes it look relatively normal—but when viewed upright, the distortion becomes jarringly obvious. This suggests that our brains struggle to process faces correctly when they’re misaligned because we rely on seeing them in their full, integrated form.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/thatcher.jpg&quot; /&gt;
    &lt;i&gt;The Thatcher effect&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;To explore this ability, scientists created the Greebles test—a study that exposes participants to strange, face-like objects called Greebles. Initially, participants struggle to differentiate between Greebles, but with training, they develop a more specialized way of recognizing them, indicating that the FFA can adapt to learn new categories of visual expertise, not just human faces.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/greebles.jpg&quot; /&gt;
    &lt;i&gt;Greebles used for testing the ability of the FFA region to understand more than faces&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;In contrast, as revised above, Haar features, used in the Viola-Jones algorithm, take the opposite approach. They analyze faces in a piecewise manner by detecting simple patterns. This method allowed for early real-time face detection on low-power devices, but it lacked the holistic capacity seen in the FFA, making it prone to errors when encountering complex angles or lighting conditions that humans would easily overcome.&lt;/p&gt;

&lt;p&gt;This limitation foreshadowed the need for more sophisticated methods, such as Convolutional Neural Networks (CNNs), which mimic the brain’s hierarchical way of learning. CNNs don’t rely on hand-crafted features like Haar patterns but instead learn directly from vast datasets. Early layers of CNNs detect edges and textures, similar to Haar features, but deeper layers build complex, abstract representations of objects—just as the FFA captures the entirety of a face. This transition from manually designed features to data-driven learning revolutionized computer vision, allowing machines to not only detect but also understand faces with near-human precision, paving the way for modern facial recognition systems powered by deep neural networks.&lt;/p&gt;

&lt;h1 id=&quot;deep-neural-networks-revolution&quot;&gt;Deep neural networks revolution&lt;/h1&gt;

&lt;p&gt;In 2012, the SuperVision team, made up of 3 Canadian academic researchers submitted their contender model AlexNet to try and beat the competition on the ImageNet benchmark. Alex Krizhevsky, Ilya Sutskever and Geoffrey Hinton completely aced the test and beat the runner up by more than 10%. The formula to it? Deep neural networks trained on GPUs.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/alexnet.jpg&quot; /&gt;
    &lt;i&gt;AlexNet, the neural network that changed AI forever&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/gpu.jpg&quot; /&gt;
    &lt;i&gt;NVIDIA GeForce GTX 580 GPU&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;Another deep neural network-related Nobel laureat was announced in the same year as Geoffrey Hinton. &lt;a href=&quot;https://en.wikipedia.org/wiki/Demis_Hassabis&quot;&gt;Demis Hassabis&lt;/a&gt;, founder of Deepmind–developers of AlphaGo–won the Nobel prize in Chemistry for AlphaFold.&lt;/p&gt;

&lt;p&gt;The elephant in the room of this essay is &lt;a href=&quot;https://ai.meta.com/people/396469589677838/yann-lecun/&quot;&gt;Yann LeCun&lt;/a&gt; and his &lt;a href=&quot;https://en.wikipedia.org/wiki/LeNet&quot;&gt;LeNet-1&lt;/a&gt; model, which goes as far back as 1989. It was never applied to face detection with the same level of success as other deep neural networks, mainly due to computational resource constraints at the time. Only in the 2010s did neural networks get applied to faces in real-time scenarios (MTCNN, BlazeFace, etc.). Still, his work and that of &lt;a href=&quot;https://en.wikipedia.org/wiki/Yoshua_Bengio&quot;&gt;Yoshua Bengio&lt;/a&gt; were some of the most premonitory in the field, and it served to influence an entire industry.&lt;/p&gt;

&lt;p&gt;Today Geoffrey Hinton, Yann LeCun and Yoshua Bengio are considered the godfathers of the AI revolution.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.linkedin.com/in/violapaul/&quot;&gt;Paul Viola&lt;/a&gt; is currently a Distinguished Engineer at Microsoft, after having led Amazon Prime Air robotics and drone technology. &lt;a href=&quot;https://www.merl.com/people/mjones/#videos&quot;&gt;Michael Jones&lt;/a&gt; is a Senior Principal Research Scientist at Mitsubishi Electric Research Laboratories, researching the explainability of deep neural networks.&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/viola-jones-irl.jpg&quot; /&gt;
    &lt;i&gt;Paul Viola and Michael Jones&lt;/i&gt;
&lt;/center&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;center&gt;
    &lt;img src=&quot;/img/when-machines-could-see-you/lenna.jpg&quot; /&gt;
    &lt;i&gt;Lenna and her Haar features&lt;/i&gt;
&lt;/center&gt;
</description>
        <pubDate>Sun, 20 Oct 2024 00:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2024/10/20/when-machines-could-see-you.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2024/10/20/when-machines-could-see-you.html</guid>
        
        
      </item>
    
      <item>
        <title>Balinese dancers</title>
        <description>&lt;p&gt;When Balinese dancers are performing the Ramayana monkey chant dance, they move their hands and arms percussively.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/balinese-dancers/dance.gif&quot; /&gt;&lt;/center&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_Hash_Algorithms&quot;&gt;Secury Hashing Algorithms&lt;/a&gt; are a family of cryptographic hash functions published by &lt;a href=&quot;https://www.nist.gov/&quot;&gt;NIST&lt;/a&gt;, a United States laboratory for the promotion of American innovation and industrial competitiveness by means of… you guessed it, establishing standards.&lt;/p&gt;

&lt;p&gt;SHA-0 (fka “original SHA”), SHA-1, SHA-2 and SHA-3 are all a part of SHA. All of the algorithms up until SHA-3 (excluding), were designed by the &lt;a href=&quot;https://www.nsa.gov/&quot;&gt;NSA&lt;/a&gt; and all of them use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_construction&quot;&gt;Merkle–Damgård construction&lt;/a&gt; method for building collision-resistant hashes (1979). SHA-3 is different, though. Read on.&lt;/p&gt;

&lt;p&gt;The Merkle–Damgård construction used in SHA-{0,1,2} builds on one-way compression functions. It takes an initialization vector (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IV&lt;/code&gt;), each block of the input a multiple of a fixed number (e.g., 512) and an (MD-compliant) padding. Sequentially, it will take the last result with the next block to create another result (by means of some function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt;). Once the final block (plus necessary padding, if any) is reached, finalisation is performed to output the final hash.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/balinese-dancers/merkle-damgard.png&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Phase outs of SHA-{0,1} started happening only long after various attacks on these SHA variants came to light, especially flaws related to collision resistance – known M1 and M2, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;H(M1)=H(M2)&lt;/code&gt;, or pre-image resistance – given known hash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h&lt;/code&gt;, hard to find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;H(M1)=h&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In 2007, NIST announced an open competition for the creation of a better hash function, the natural successor to SHA-{0,1} and competitor to SHA-2 (many versions of it &lt;a href=&quot;https://www.federalregister.gov/documents/2007/11/02/E7-21581/announcing-request-for-candidate-algorithm-nominations-for-a-new-cryptographic-hash-algorithm-sha-3&quot;&gt;accepted as secure still today&lt;/a&gt;). Five years and &lt;a href=&quot;https://nvlpubs.nist.gov/nistpubs/ir/2012/NIST.IR.7896.pdf&quot;&gt;64 submissions&lt;/a&gt; later, 5 finalists were found:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;BLAKE (Aumasson et al.)&lt;/li&gt;
  &lt;li&gt;Grøstl (Knudsen et al.)&lt;/li&gt;
  &lt;li&gt;JH (Hongjun Wu)&lt;/li&gt;
  &lt;li&gt;Skein (Schneier et al.)&lt;/li&gt;
  &lt;li&gt;Keccak (Keccak team, Daemen et al.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2012, it was annouced that &lt;a href=&quot;https://www.nist.gov/news-events/news/2012/10/nist-selects-winner-secure-hash-algorithm-sha-3-competition&quot;&gt;the winner&lt;/a&gt; was &lt;strong&gt;&lt;a href=&quot;https://keccak.team/&quot;&gt;Keccak&lt;/a&gt;&lt;/strong&gt;. Keccak revealed “large security margin after significant cryptanalytic effort” and “much better throughput/area performance in hardware [than competitors]”.&lt;/p&gt;

&lt;style&gt;
.column {
  float: left;
  padding: 5px;
}

.row::after {
  content: &quot;&quot;;
  clear: both;
  display: table;
}

figure {
  text-align: center;
  font-style: italic;
  font-size: smaller;
  text-indent: 0;
  padding: 0.5em;
}
&lt;/style&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;a href=&quot;/img/balinese-dancers/energy-per-bit.png&quot; target=&quot;_blank&quot;&gt;
      &lt;img src=&quot;/img/balinese-dancers/energy-per-bit.png&quot; style=&quot;width:100%&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;a href=&quot;/img/balinese-dancers/area-kgate.png&quot; target=&quot;_blank&quot;&gt;
      &lt;img src=&quot;/img/balinese-dancers/area-kgate.png&quot; style=&quot;width:100%&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;a href=&quot;/img/balinese-dancers/throughput-per-area.png&quot; target=&quot;_blank&quot;&gt;
      &lt;img src=&quot;/img/balinese-dancers/throughput-per-area.png&quot; style=&quot;width:100%&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;And hence, Keccak became SHA-3.&lt;/p&gt;

&lt;p&gt;SHA-3, uses a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sponge_function&quot;&gt;sponge construction&lt;/a&gt; at its core. It is split into two phases: absorbing and squeezing. The absorbing phase runs for the whole padded blocks of input, interleaving applications of the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt;. Squeezing happens afterwards where bits of the resulting state are again interleaved with applications of the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; to generate an arbitrary number of output blocks.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/balinese-dancers/sponge.png&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Circling back to the title, the name “Keccak” &lt;a href=&quot;https://crypto.stackexchange.com/a/9975&quot;&gt;is a variant spelling of “Kecak”&lt;/a&gt;, the aforementioned type of &lt;a href=&quot;https://www.youtube.com/embed/ViKT5gPoZW8&quot;&gt;Balinese dance&lt;/a&gt;. The story goes that in Kecak dancers move their hands fast and it looks like they are chopping and mixing something, and this reminded one of the co-authors of the algorithm about an hash function.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/balinese-dancers/keccak-team.jpeg&quot; width=&quot;500&quot; /&gt;&lt;/center&gt;

&lt;p&gt;The Keccak team is Michaël Peeters, Guido Bertoni, Joan Daemen, Ronny Van Keer, Gilles Van Assche and Seth Hoffert.&lt;/p&gt;
</description>
        <pubDate>Wed, 22 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2022/06/22/balinese-dancers.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2022/06/22/balinese-dancers.html</guid>
        
        
      </item>
    
      <item>
        <title>New York city subway</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;14.23.34.42.50.59.66.72&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do you know what this sequence is?&lt;/p&gt;

&lt;p&gt;About the New York city subway and computer science data structures — a blog post on real life city navigation and abstractions for efficient search.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;If you live in New York city maybe you guessed it. The sequence represents a trip on the &lt;a href=&quot;https://www.nytimes.com/interactive/2019/12/02/nyregion/nyc-subway-map.html&quot;&gt;New York city subway&lt;/a&gt; (or so I’m told). Stops 14, 23, etc. up until 72.&lt;/p&gt;

&lt;p&gt;The New York city subway is a skip list: it works just like the simple efficient dynamic randomised search structure created by &lt;a href=&quot;https://en.wikipedia.org/wiki/William_Pugh_(computer_scientist)&quot;&gt;William Pugh&lt;/a&gt; (1989). When you hop into the NYC subway you may use the express line to jump over stations you know you’re not interested in stopping at.&lt;/p&gt;

&lt;p&gt;Going from 14 to 72 you may jump through 23, 50, 59, 66 by taking the express line. Now imagine doing that for linked lists for efficient searching. Instead of traversing the whole list, just jump through some elements on the list, like a pebble ricocheting over water.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/new-york-city-subway/pebble.png&quot; /&gt;&lt;/center&gt;

&lt;p&gt;The numbered boxes (in yellow) at the bottom represent the ordered data sequence. Searching proceeds downwards from the sparsest subsequence at the top (express lines) until consecutive elements bracketing the search element are found.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/new-york-city-subway/skip-list.png&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Pugh proved average &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;O(log n)&lt;/code&gt; complexity on both insertion and search with skipped elements being picked probabilistically, by means of randomising which nodes get promoted to an upper level, i.e., which nodes are “a part of the [multiple] express lines”.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/new-york-city-subway/paper.png&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Compared with other efficient search structures (e.g., &lt;a href=&quot;https://en.wikipedia.org/wiki/Treap&quot;&gt;treaps&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Red%E2%80%93black_tree&quot;&gt;red-black trees&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/B-tree&quot;&gt;b-trees&lt;/a&gt;), the randomized balancing of skip lists has been argued to be easier to implement than the deterministic balancing schemes used in the aforementioned data structures.&lt;/p&gt;

&lt;style&gt;
.column {
  float: left;
  padding: 5px;
}

.row::after {
  content: &quot;&quot;;
  clear: both;
  display: table;
}

figure {
  text-align: center;
  font-style: italic;
  font-size: smaller;
  text-indent: 0;
  padding: 0.5em;
}
&lt;/style&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:20%&quot;&gt;
    &lt;figure&gt;
      &lt;img src=&quot;/img/new-york-city-subway/treap.png&quot; style=&quot;width:100%&quot; /&gt;
      &lt;figcaption&gt;Treap&lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:40%&quot;&gt;
    &lt;figure&gt;
      &lt;img src=&quot;/img/new-york-city-subway/red-black-tree.png&quot; style=&quot;width:100%&quot; /&gt;
      &lt;figcaption&gt;Red-black tree&lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:40%&quot;&gt;
    &lt;figure&gt;
      &lt;img src=&quot;/img/new-york-city-subway/b-tree.png&quot; style=&quot;width:100%&quot; /&gt;
      &lt;figcaption&gt;B-tree&lt;/figcaption&gt;
    &lt;/figure&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Skip lists also prove especially useful in parallel computing, where updates to different parts of the list may be done independently, hence being a great fit for designing highly scalable lock-free efficient search structures.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lucene.apache.org/&quot;&gt;Lucene&lt;/a&gt;, &lt;a href=&quot;https://redis.io/&quot;&gt;Redis&lt;/a&gt; and Google’s &lt;a href=&quot;https://github.com/google/leveldb&quot;&gt;LevelDB&lt;/a&gt; all use skip lists in their implementation.&lt;/p&gt;

&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;img src=&quot;/img/new-york-city-subway/apache-lucene.png&quot; style=&quot;width:100%&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;img src=&quot;/img/new-york-city-subway/redis.png&quot; style=&quot;width:100%&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;column&quot; style=&quot;width:33.3%&quot;&gt;
    &lt;img src=&quot;/img/new-york-city-subway/leveldb.png&quot; style=&quot;width:100%&quot; /&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Credit for the NYC subway analogy goes to the brilliant Erik Demaine, a &lt;a href=&quot;https://ocw.mit.edu/courses/6-046j-introduction-to-algorithms-sma-5503-fall-2005/resources/lecture-12-skip-lists/&quot;&gt;professor of computer science at MIT&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Sun, 19 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2022/06/19/new-york-city-subway.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2022/06/19/new-york-city-subway.html</guid>
        
        
      </item>
    
      <item>
        <title>Okapi BM25</title>
        <description>&lt;p&gt;Just recently learned about Okapi BM25 as a better information retrieval function for search engines (after the widely used “vanilla” TF/IDF) and it is hands down the best comp sci name I’ve come across. “Okapi BM25”, damn. Some history.&lt;/p&gt;

&lt;p&gt;Karen Spärck Jones was a pioneering British computer scientist responsible for the concept of inverse document frequency (1972); Elasticsearch still uses TF/IDF as part of its full-text scoring algorithm as of today. it’s cheap to run and tremendously useful.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/okapi-bm25/karen-sparck-jones.jpeg&quot; /&gt;&lt;/center&gt;

&lt;p&gt;TF/IDF stands for “term frequency/inverse document frequency”. The main ideas behind it are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;A document is more relevant if the term we’re looking for appears often in that document (e.g., “quick” has a frequency of 2 in “the quick brown fox jumps over the quick dog”) but only a frequency of 1 in “the quick brown fox” (hence less relevant)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A document is more relevant if the term it is matching is not as common (e.g., documents with term “quick” will likely be more relevant than documents with more general term “the”); Another better example would be, if you have a blog about birds the word “sparrow” will be more relevant than “bird”, since it is likely many documents will mention “bird” (maybe all of them), but only one or two will mention [house] “sparrow” (cute Passer domesticus species)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;center&gt;&lt;img src=&quot;/img/okapi-bm25/house-sparrow.jpeg&quot; /&gt;&lt;/center&gt;

&lt;p&gt;The (simplified) formula as explained in &lt;em&gt;Elasticsearch: The Definitive Guide&lt;/em&gt; is as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;scoring(q, d) =
∑ (
    tf(t in d)
    idf(t)²
    t.getBoost()
    norm(t,d)
) (t in q)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;center&gt;&lt;img src=&quot;/img/okapi-bm25/elasticsearch-book.jpeg&quot; /&gt;&lt;/center&gt;

&lt;p&gt;Essentially, we score results of a query over a set of documents by taking into account the term frequency, inverse document frequency, boosting and field-length norm.&lt;/p&gt;

&lt;p&gt;By boosting, we can attribute more relevance to certain terms.&lt;/p&gt;

&lt;p&gt;Field-length norm indicates relevance of term in the context of that document – the bigger the document, the less relevant it is (e.g., if “quick” appears in text “quick: a novel” it is more relevant than it appearing in bigger text “the quick brown fox jumps over the lazy dog”).&lt;/p&gt;

&lt;p&gt;Enter Okapi BM25, the TF/IDF scoring default in Elasticsearch – whilst vanilla TF/IDF favours term frequency within a document and penalizes term prevalence across documents, Okapi BM 25 takes it further and weighs in average document size and term frequency saturation.&lt;/p&gt;

&lt;p&gt;Average document size is important as a way to normalize field-length norm. “quick” is only more relevant in “quick: a novel” if that’s the average size for our set of documents.&lt;/p&gt;

&lt;p&gt;Term frequency saturation is used to slow down (even set an upper bound) on term frequency relevance. “quick” is relevant if it appears twice in a document. it is more relevant if it appears 3 times. but appearing 4, 5+ times is not much more relevant between them. It’s saturated.&lt;/p&gt;

&lt;p&gt;The name of the actual ranking function is BM25 (for “best matching 25”). The fuller name, Okapi BM25, includes the name of the first system to use it, which was the Okapi information retrieval system, implemented at London’s City University in the 1980s and 1990s.&lt;/p&gt;

&lt;center&gt;&lt;img src=&quot;/img/okapi-bm25/london-city-university.jpeg&quot; /&gt;&lt;/center&gt;

</description>
        <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2022/06/10/okapi-bm25.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2022/06/10/okapi-bm25.html</guid>
        
        
      </item>
    
      <item>
        <title>Data Intensive maps</title>
        <description>&lt;p&gt;In &lt;a href=&quot;https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/&quot;&gt;Designing Data Intensive Applications&lt;/a&gt;, a great book by &lt;a href=&quot;https://twitter.com/martinkl&quot;&gt;Martin Kleppmann&lt;/a&gt;, each chapter starts out with a fun map. It’s a distinctive and fun way to break the usual boring tone of entering a new monotonically incrementing chapter.&lt;/p&gt;

&lt;p&gt;The book reads like a mix of a distributed systems bible and a (quite large) pocket book for the majority of your state-of-the-art solutions for designing applications for massive scalability. It lays out general concepts and then goes in deep on different specific technologies. I see myself returning to it in the future when I face some issue that I hope I’m smart enough to pattern-match with the kinds of problems at hand in the examples in the book.&lt;/p&gt;

&lt;p&gt;When I was reading through the pages of this great book about some of the major challenges of (and solutions to) designing highly scalable and reliable systems, I was always interested in zooming in the map ahead in my Kindle. Each map has quirky bits related to the chapter you’re about to dive into. Some that stuck were “Mount Consensus” in Chapter 5, “Harsh winds of reality” in Chapter 8; “Forest of logs” in Chapter 11. The list goes on. 😂&lt;/p&gt;

&lt;p&gt;Anyway, I went digging and Martin Kleppmann has actually &lt;a href=&quot;https://martin.kleppmann.com/2017/03/15/map-distributed-data-systems.html&quot;&gt;wrote about how the maps came about&lt;/a&gt;. He explains how the Tolkien-esque style design was eventually (consistently 😬) illustrated by Shabbir Diwan, Edie Freedman and Ron Bilodeau. Its final form was inspired by ideas from readers wanting to have auxiliary flowcharts or diagrams explaining the themes in question. From there, he started to think of mind maps, geographic maps, family trees and the “wonderful map of Iceland, with Mount Hekla spewing fire”. The end result is a fun visual collection of distributed systems concepts and technologies.&lt;/p&gt;

&lt;p&gt;You can easily find the individual maps online (Martin Kleppmann and O’Reilly were kind enough to have them up for free in high resolution). The JPEG in the cover of this blog post can be accessed (4.52 MB) &lt;a href=&quot;https://d3ansictanv2wj.cloudfront.net/ddia-poster-web-89b1c62f6eb4b57336c6cbe2148cc9a9.jpg&quot;&gt;here&lt;/a&gt;. There was even a &lt;a href=&quot;https://www.kickstarter.com/projects/1407076797/a-map-of-the-distributed-data-systems-landscape&quot;&gt;Kickstarter campaign&lt;/a&gt; for printing a poster of it!&lt;/p&gt;

&lt;p&gt;If you want to access a very unoptimised version of the same map that’s clickable to zoom on each chapter, you can do so (~15 MB) &lt;a href=&quot;/maps&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Good readings! 🤓&lt;/p&gt;
</description>
        <pubDate>Wed, 04 Nov 2020 23:50:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2020/11/04/data-intensive-maps.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2020/11/04/data-intensive-maps.html</guid>
        
        
      </item>
    
      <item>
        <title>Tensorflow Serving in Elixir</title>
        <description>&lt;p&gt;I’ve been learning more about &lt;a href=&quot;https://www.tensorflow.org/tfx/guide/serving&quot;&gt;Tensorflow Serving&lt;/a&gt; lately. If you are where I was at around a month ago and you’re not super familar with it, TF Serving is a “flexible, high-performance serving system for machine learning models, designed for production environments.” Follow a set of conventions (&lt;a href=&quot;https://en.wikipedia.org/wiki/Convention_over_configuration&quot;&gt;over configuration&lt;/a&gt;), and you can get a Tensorflow model up and running faster than ever before, powered by highly-optimised C++ code that runs blazingly fast. It is the &lt;em&gt;de facto&lt;/em&gt; way of isolating your TF models into a specific microservice capable of serving multiple versions of your trained algorithms.&lt;/p&gt;

&lt;p&gt;Provided you have a TF model, TF Serving allows you to expose either a REST or gRPC interface to interact with it. Given all the &lt;a href=&quot;https://www.youtube.com/watch?v=RoXT_Rkg8LA&quot;&gt;bespoken advantages of gRPC&lt;/a&gt;, I wanted to give calling TF Serving via gRPC from Elixir a try.&lt;/p&gt;

&lt;h1 id=&quot;serving-a-tensorflow-model&quot;&gt;Serving a Tensorflow model&lt;/h1&gt;

&lt;p&gt;Since I’m more interested in Computer Vision problems, I looked for something easy to start with. &lt;a href=&quot;https://tfhub.dev/&quot;&gt;Tensorflow Hub&lt;/a&gt; seemed like a good starting point, but I ended up going for a pre-trained handwritten digits model I found &lt;a href=&quot;https://github.com/EN10/KerasMNIST&quot;&gt;on GitHub&lt;/a&gt; in &lt;a href=&quot;https://en.wikipedia.org/wiki/Hierarchical_Data_Format&quot;&gt;HDF5&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.h5&lt;/code&gt;), trained on the well-known &lt;a href=&quot;https://en.wikipedia.org/wiki/MNIST_database&quot;&gt;MNIST dataset&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/tensorflow-serving-elixir/mnist.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In order to serve a model via Tensorflow Serving though, we need to use the &lt;a href=&quot;https://www.tensorflow.org/guide/saved_model&quot;&gt;SavedModel format&lt;/a&gt;, “a complete TensorFlow program, including weights and computation.” Converting a Keras-saved HDF5 model to the SavedModel format is as simple as &lt;a href=&quot;https://www.tensorflow.org/tutorials/keras/save_and_load#hdf5_format&quot;&gt;loading it&lt;/a&gt; and then &lt;a href=&quot;https://www.tensorflow.org/tutorials/keras/save_and_load#savedmodel_format&quot;&gt;saving it in the desired format&lt;/a&gt;. Once that’s done, we’re ready to run the model in TF Serving. In Python land:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tensorflow&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;cnn.h5&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;saved_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;digits/1/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then just run it with:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8501:8501 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8500:8500 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--mount&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bind&lt;/span&gt;,source&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/digits,target&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/models/digits &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MODEL_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;digits &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; tensorflow/serving &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, I’m exposing both the REST and the gRPC interfaces.&lt;/p&gt;

&lt;p&gt;If you go to &lt;a href=&quot;http://localhost:8501/v1/models/digits/versions/1/metadata&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8501/v1/models/digits/versions/1/metadata&lt;/code&gt;&lt;/a&gt;, you should see something like the following:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;model_spec&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;digits&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;signature_name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;signature_def&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;signature_def&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;__saved_model_init_op&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;__saved_model_init_op&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DT_INVALID&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tensor_shape&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unknown_rank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;NoOp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;serving_default&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;conv2d_1_input&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DT_FLOAT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tensor_shape&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unknown_rank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;serving_default_conv2d_1_input:0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dense_2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DT_FLOAT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tensor_shape&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;unknown_rank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
              &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;StatefulPartitionedCall:0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tensorflow/serving/predict&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should match what you see when inspecting the model via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;saved_model_cli&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;saved_model_cli show --dir digits/1 --tag_set serve --signature_def serving_default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The given SavedModel SignatureDef contains the following input(s):
  inputs[&apos;conv2d_1_input&apos;] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 28, 28, 1)
      name: serving_default_conv2d_1_input:0
The given SavedModel SignatureDef contains the following output(s):
  outputs[&apos;dense_2&apos;] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 10)
      name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output describes a model that takes a 28x28x1 matrix of floats as its input tensor tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conv2d_1_input&lt;/code&gt;, and it yields an output tensor of 10 floats tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dense_2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You might have concluded by now the model takes 28x28 black-and-white images, hence the extra 1-channel dimension for black or white (0 and 1, respectively); and it returns different confidence values for each digit from 0 to 9, hence the 10 floats output.&lt;/p&gt;

&lt;h1 id=&quot;tensorflow-serving-from-elixir&quot;&gt;Tensorflow Serving from Elixir&lt;/h1&gt;

&lt;p&gt;I wanted to experiment with gRPC and the Tensorflow Serving gRPC interface specifically. It was a great coincidence that shortly before I started with this experiment, the great Andrea Leopardi published a blog post about &lt;a href=&quot;https://andrealeopardi.com/posts/sharing-protobuf-schemas-across-services/&quot;&gt;“sharing Protobuf schemas across services.”&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the Elixir implementation of gRPC, I went with &lt;a href=&quot;https://github.com/elixir-grpc/grpc&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elixir-grpc/grpc&lt;/code&gt;&lt;/a&gt; by another great one from the Elixir community, &lt;a href=&quot;https://github.com/tony612&quot;&gt;Tony Han&lt;/a&gt;. Since he has done work on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elixir-grpc/grpc&lt;/code&gt;, associated with the feedback I had from the aforementioned post about &lt;a href=&quot;https://github.com/tony612/protobuf-elixir&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protobuf-elixir&lt;/code&gt;&lt;/a&gt;, choosing this project for generating Elixir code from Protobufs didn’t seem like a bad idea. And it wasn’t! More on that next.&lt;/p&gt;

&lt;p&gt;Tensorflow and TF Serving provide great out-of-the-box APIs for Python based on the different language-agnostic Protobufs. One such case of that are the &lt;a href=&quot;https://github.com/tensorflow/serving/blob/master/tensorflow_serving/apis/prediction_service_pb2_grpc.py&quot;&gt;prediction APIs&lt;/a&gt; based on &lt;a href=&quot;https://github.com/tensorflow/serving/blob/master/tensorflow_serving/apis/prediction_service.proto&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prediction_service.proto&lt;/code&gt;&lt;/a&gt;. After I &lt;a href=&quot;https://github.com/dnlserrano/tensorflow-serving-grpc-python-example/blob/master/tfserving_grpc_py/app.py&quot;&gt;experimented with it in Python&lt;/a&gt;, given the lower barrier for prototyping, I was confident it should be simple enough to replicate a similar interaction in Elixir (provided the gods of gRPC and HTTP/2 had worked in my favour - Tony Han, creator of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grpc-elixir/grpc&lt;/code&gt; and &lt;a href=&quot;https://github.com/essen&quot;&gt;Loïc Hoguin&lt;/a&gt;, creator of &lt;a href=&quot;https://github.com/ninenines/cowboy&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cowboy&lt;/code&gt;&lt;/a&gt; did 🙏).&lt;/p&gt;

&lt;p&gt;I generated the Elixir code given the necessary Protobufs (under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protos/&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;protoc &lt;span class=&quot;nt&quot;&gt;--elixir_out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;grpc:./lib/messages &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  protos/tensorflow/core/framework/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  protos/tensorflow_serving/apis/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  protos/google/protobuf/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; protos
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the end, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;messages/&lt;/code&gt; directory contained the generated Elixir code, with nice structs I could leverage to call TF Serving from Elixir. 😊&lt;/p&gt;

&lt;h1 id=&quot;media-manipulation-in-elixir&quot;&gt;Media manipulation in Elixir&lt;/h1&gt;

&lt;p&gt;Another challenge when doing Computer Vision with Elixir is the support for media manipulation. Resizing images, extracing frames from videos, etc. can either be done by shelling out to applications such as &lt;a href=&quot;https://imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; or &lt;a href=&quot;http://ffmpeg.org/&quot;&gt;FFmpeg&lt;/a&gt;, or by making use of &lt;a href=&quot;2019/03/10/elixir-nifs&quot;&gt;(dirty) NIFs&lt;/a&gt;, Ports, port drivers or &lt;em&gt;“equivalent alternatives”&lt;/em&gt; (out of scope of this blog post). TL;DR is I ended up using a mixture of both, because I was being hacky.&lt;/p&gt;

&lt;p&gt;For resizing my images to 28x28 1-channel black-and-white thumbnails, I ended up using &lt;a href=&quot;https://github.com/dnlserrano/tf-serving-elixir/blob/master/lib/tfserving/digits.ex#L12-L16&quot;&gt;ImageMagick’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;convert&lt;/code&gt;&lt;/a&gt;. ✨ Who would have said there were so many &lt;a href=&quot;https://en.wikipedia.org/wiki/Dither#Algorithms&quot;&gt;dithering algorithms&lt;/a&gt; out there?&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;convert&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sx&quot;&gt;~w(#{input_file} -resize 28x28^ -dither FloydSteinberg -set background white
  -flatten -colors 2 -gravity center -extent 28x28 #{output_file})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To translate the 1-channel pixels to arrays of 0, 1 values, I used &lt;a href=&quot;https://github.com/arjan&quot;&gt;Arjan Scherpenisse&lt;/a&gt;’s &lt;a href=&quot;https://github.com/arjan/pixels/&quot;&gt;pixels&lt;/a&gt; library (which I got to know in &lt;a href=&quot;https://fosdem.org/2020/schedule/event/beam_keep_calm_use_nerves/&quot;&gt;his awesome talk&lt;/a&gt; about Nerves). I’m sure I could have been smarter here (piping the resulting image somehow to the running Elixir process and avoiding having to use this NIF), but again… I was being scrappy. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pixels&lt;/code&gt; is awesome, by the way. It integrates with both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lodepng&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ujpeg&lt;/code&gt;. Thanks Arjan!&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixels_binary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pixels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pixels&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:erlang&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;binary_to_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixels_binary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pixels&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_every&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h1&gt;

&lt;p&gt;Having all of these bits and pieces in place, we’re left with sending the actual input to TF Serving and making sense of what it returns back. At this point we have the input (or kind of) and we have the Elixir structs to talk gRPC with TF Serving.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dnlserrano/tf-serving-elixir/blob/master/lib/tfserving/digits.ex#L31-L49&quot;&gt;Building the request&lt;/a&gt; entails defining a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PredictRequest&lt;/code&gt; containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inputs&lt;/code&gt; and a model specification (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;model_spec&lt;/code&gt;). As previously mentioned, the name of the input is in this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;conv2d_1_input&quot;&lt;/code&gt; and it is of type float (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:DT_FLOAT&lt;/code&gt;). The input is a 28x28x1 tensor with arbitrary number of batches (we use a batch of 1 image, but you could evaluate more in one single network call). The value itself, is passed as a vector of size 784 (28 * 28), named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input&lt;/code&gt; in the code that follows:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serving&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PredictRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;inputs:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;conv2d_1_input&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;dtype:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:DT_FLOAT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;tensor_shape:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorShapeProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;ss&quot;&gt;dim:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorShapeProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;size:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorShapeProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;size:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorShapeProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;size:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TensorShapeProto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;size:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
          &lt;span class=&quot;ss&quot;&gt;unknown_rank:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;float_val:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;model_spec:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serving&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ModelSpec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;digits&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We then &lt;a href=&quot;https://github.com/dnlserrano/tf-serving-elixir/blob/master/lib/tfserving/digits.ex#L51-L53&quot;&gt;make the request&lt;/a&gt; and &lt;a href=&quot;https://github.com/dnlserrano/tf-serving-elixir/blob/master/lib/tfserving/digits.ex#L55-L59&quot;&gt;return back the &lt;em&gt;guessed&lt;/em&gt; digit&lt;/a&gt;, for which the ML algorithm yielded the highest score:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GRPC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Stub&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tf_serving_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:8500&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serving&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PredictResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;outputs:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Tensorflow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Serving&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PredictionService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Stub&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;predict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;digit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dense_2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float_val&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try it out below!&lt;/p&gt;

&lt;div align=&quot;center&quot; style=&quot;padding-left: 75px&quot;&gt;
  &lt;iframe src=&quot;https://digits.dnlserrano.dev&quot; width=&quot;300&quot; height=&quot;300&quot; frameborder=&quot;0&quot;&gt;Browser not compatible.&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;What’s happening here is we’re providing a UI where the user can draw a digit, we then convert the content in the Canvas element to a Base64-encoded image that is sent to the backend. There, we resize it and apply it the need transformations before we send it to the TF Serving model. I then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Docker&lt;/code&gt;ized it and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt;d it. 👨‍🍳&lt;/p&gt;

&lt;p&gt;Find the whole code over at &lt;a href=&quot;https://github.com/dnlserrano/tf-serving-elixir&quot;&gt;my GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h1&gt;

&lt;p&gt;And that’s it. Elixir calling via gRPC a Machine Learning model deployed using Tensorflow Serving. It’s possible, today!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/tensorflow-serving-elixir/end.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sure, there are some rough edges. The support for gRPC isn’t 100% mature in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elixir-grpc/grpc&lt;/code&gt; (I had to point to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; branches for some of its dependencies in order to get the latest fixes for existing bugs). The “media manipulation ecosystem” in Elixir could also be better, but you can already go a long way (particularly with dirty NIFs). And if we push a bit, we can make it even better! ❤️&lt;/p&gt;

&lt;p&gt;Happy Tensorflow Serving… in Elixir! 🤖&lt;/p&gt;
</description>
        <pubDate>Sat, 28 Mar 2020 14:00:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2020/03/28/tensorflow-serving-elixir.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2020/03/28/tensorflow-serving-elixir.html</guid>
        
        
      </item>
    
      <item>
        <title>Behaviours and &lt;br/&gt;Dynamic dispatch</title>
        <description>&lt;p&gt;This is going to be a quick one.&lt;/p&gt;

&lt;p&gt;Recently I had the opportunity to take part in the refactoring of some Elixir code.&lt;/p&gt;

&lt;p&gt;It looked something like this. Picture more interesting (and complex) logic in place of the classic, old and honestly a bit boring beverages example:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Beverages&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll_up_sleeves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_beverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serve_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;happy days!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll_up_sleeves&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;rolling up sleeves to start working&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:coffee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;getting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gr of beautiful Peruvian coffee beans&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prepped with the finest coffee ☕️&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tea&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;getting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gr of mixed Indian Assam leaves, brisk Ceylon and bright Kenyan teas&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prepped with the finest teas 🍵&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serve_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ready to drink beverage &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wanted to move away from pattern-matching on atoms (first function argument) as a way of forking to different implementations &lt;strong&gt;in the same module&lt;/strong&gt;. &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_responsibility_principle&quot;&gt;Single responsibility principle&lt;/a&gt; anyone?&lt;/p&gt;

&lt;p&gt;Our goal was to split the logic in various different modules, given we would now need to add another beverage type: the lemonade. The logic inside each was also more complex than in this toy example.&lt;/p&gt;

&lt;p&gt;This split would make it easier to reason about each one of the beverage types and the way they differ from each other, while hopefully still keeping the comon logic in a central place that privileges &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;&lt;em&gt;dryness&lt;/em&gt;&lt;/a&gt;. Furthermore, having each of them as separate modules made it simpler to test them (each one in isolation) and also to extend them (both now given the new business requirements, and in the future in case yet another beverage comes along).&lt;/p&gt;

&lt;p&gt;Besides all this, we were also not following a specific way of properly documenting the fact that each of the specific beverage implementations needed to respect a well-defined interface. &lt;a href=&quot;https://www.youtube.com/watch?v=hTWKbfoikeg&quot;&gt;Smells like &lt;del&gt;teen spirit&lt;/del&gt;&lt;/a&gt; Elixir behaviours.&lt;/p&gt;

&lt;p&gt;After doing some quick research over the Elixir hexdocs and official pages, we eventually hit gold when we came across the recommended way of doing &lt;a href=&quot;https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#dynamic-dispatch&quot;&gt;“dynamic dispatching” in Elixir&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So we changed it to this:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@callback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;beverage_impl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll_up_sleeves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_beverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serve_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;happy days!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:coffee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coffee&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tea&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tea&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:lemonade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Lemonade&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll_up_sleeves&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;rolling up sleeves to start working&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serve_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ready to drink beverage &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;served&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Coffee&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@behaviour&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;getting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gr of beautiful Peruvian coffee beans&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prepped with the finest coffee ☕️&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Tea&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@behaviour&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;getting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gr of mixed Indian Assam leaves, brisk Ceylon and bright Kenyan teas&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prepped with the finest teas 🍵&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Lemonade&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;@behaviour&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NewBeverages&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;getting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ml of hearty lemon juice&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prepped with the finest lemon 🍋&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We making lemonade, boy.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; NewBeverages.prepare(:lemonade)
rolling up sleeves to start working
getting 32ml of hearty lemon juice
ready to drink beverage prepped with the finest lemon 🍋
{:ok, &quot;happy days!&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can easily extend lemonade to do crazy things other beverages don’t need to do as part of preparing your beverage (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepare_it/1&lt;/code&gt;) without polluting the “orchestrator module”. Buy one of those nice looking &lt;a href=&quot;https://www.amazon.com/s?k=lemon+metal+squeezer&quot;&gt;metal squeezers&lt;/a&gt;? Looking at you Rodrigo.&lt;/p&gt;

&lt;p&gt;You can take this further, and do something like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Template_method_pattern&quot;&gt;Template method pattern&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prepare_beverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;beverage_impl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll_up_sleeves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prepare_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_with_side&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beverage_impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_side&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preparation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_beverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serve_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beverage_with_side&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;happy days!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, the template defines two very general actions: randomising the quantity used to prepare your beverage (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;random_quantity/0&lt;/code&gt;) and serving the drink (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serve_it/1&lt;/code&gt;). Actually preparing it (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepare_it/1&lt;/code&gt;): beans for coffee, leaves for tea, etc., and getting a special side for each of these (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_side/1&lt;/code&gt;): chocolate brownie for coffee, butter cookie for tea, etc. is different for each, so each specific implementation should do its own thing.&lt;/p&gt;

&lt;p&gt;Functional purists may be crying right now, so I’ll stop. But you get the gist of it. This new design seems to, for now, have put us in a much better place. Leveraging language features such as &lt;a href=&quot;https://elixir-lang.org/getting-started/protocols.html&quot;&gt;Protocols&lt;/a&gt; and &lt;a href=&quot;https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#behaviours&quot;&gt;Behaviours&lt;/a&gt; has truly helped us maintain and improve our code for the long run.&lt;/p&gt;

&lt;p&gt;Code available &lt;a href=&quot;https://github.com/dnlserrano/beverages&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Thanks to Dino Costa for proposing the usage of a “factory” function (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beverage_for/1&lt;/code&gt;). Also, thanks to him, Daniel Caixinha, and Pedro Madeira for validating this design.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Merry Xmas! 🎄🎅&lt;/p&gt;
</description>
        <pubDate>Sat, 21 Dec 2019 15:43:22 +0000</pubDate>
        <link>https://dnlserrano.github.io//2019/12/21/behaviours-and-dynamic-dispatch.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2019/12/21/behaviours-and-dynamic-dispatch.html</guid>
        
        
      </item>
    
      <item>
        <title>Learning Rust</title>
        <description>&lt;p&gt;I’ve been learning Rust lately. I have also got back to playing futsal with some friends. Weirdly enough, these two worlds intersected a few weeks ago. This is a summary of my experience learning Rust. Starting with reading about it &lt;a href=&quot;https://blog.discordapp.com/using-rust-to-scale-elixir-for-11-million-concurrent-users-c6f19fc029d3&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://hub.packtpub.com/rust-is-the-future-of-systems-programming-c-is-the-new-assembly-intel-principal-engineer-josh-triplett/&quot;&gt;there&lt;/a&gt;, eventually getting &lt;a href=&quot;https://doc.rust-lang.org/book/&quot;&gt;the book&lt;/a&gt;, doing a Hackathon project at Onfido using it and then spinning up my own personal project as a way to further experiment with the language.&lt;/p&gt;

&lt;p&gt;At this point, Rust is a pretty well-known programming language. I remember one of the first blog posts that really caught my attention to the language appeared expectedly (given Rust started at Mozilla) in the &lt;a href=&quot;https://hacks.mozilla.org/2017/11/entering-the-quantum-era-how-firefox-got-fast-again-and-where-its-going-to-get-faster/&quot;&gt;Mozilla Hacks blog&lt;/a&gt;. In this blog post, Lin Clark introduced Project Quantum. Rust was said to be “a language that was free of […] data races”, and Servo (the browser engine developed in Rust at Mozilla) “made full use of […] fine-grained parallelism”.&lt;/p&gt;

&lt;p&gt;Ever since that &lt;em&gt;“first”&lt;/em&gt; blog post, lots of articles have been popping up more and more on popular aggregators like &lt;a href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; and &lt;a href=&quot;https://lobste.rs&quot;&gt;Lobsters&lt;/a&gt;. And everytime, I would get that &lt;a href=&quot;https://en.wikipedia.org/wiki/Fear_of_missing_out&quot;&gt;FOMO&lt;/a&gt; feeling. The tipping point came about when learning about more and more developers in the Elixir community which were looking at using Rust as a way to squeeze the most performance out of their resources. This was true for &lt;a href=&quot;https://www.youtube.com/watch?v=F86JXaCPowo&quot;&gt;Bleacher Report&lt;/a&gt;, &lt;a href=&quot;https://blog.discordapp.com/using-rust-to-scale-elixir-for-11-million-concurrent-users-c6f19fc029d3&quot;&gt;Discord&lt;/a&gt; and more recently &lt;a href=&quot;https://twitter.com/ElixirTalk/status/1164549459134439424&quot;&gt;SimpleBet&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;starting-out&quot;&gt;Starting out&lt;/h1&gt;

&lt;p&gt;To start out, I got the official book for &lt;a href=&quot;https://www.amazon.com/Rust-Programming-Language-Covers-2018/dp/1718500440&quot;&gt;“The Rust Programming Language”&lt;/a&gt; and started reading it at the end of my summer vacations. I stopped for a while, and started thinking about ways I could actually practice what the book was preaching. My initial idea was to implement the &lt;a href=&quot;https://en.wikipedia.org/wiki/Skip_list&quot;&gt;Skip List&lt;/a&gt; data structure in Rust. It’s one of the most fun data structures around so I was excited about reading &lt;a href=&quot;https://homepage.cs.uiowa.edu/~ghosh/skip.pdf&quot;&gt;the paper that originally introduced it&lt;/a&gt; and about the possiblity of implementing it in Rust, obviously!&lt;/p&gt;

&lt;p&gt;So I set out to do so. I installed &lt;a href=&quot;https://rustup.rs/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rustup&lt;/code&gt;&lt;/a&gt;, started playing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt; to generate my “Hello, world!” project and moved on to a bit of online tutorial reading about idiomatically writing singly linked lists in Rust. My co-worker &lt;a href=&quot;https://tomforb.es&quot;&gt;Tom Forbes&lt;/a&gt;, who had also been reading about Rust, warned me it might not be an easy task for a first project, given this sort of data structure implementation usually comes with lots of reference handling and non-trivial ownership transfers as a consequence of Rust’s ownership rules and borrow checking.&lt;/p&gt;

&lt;p&gt;Around the time this was happening, we had a Hackathon at &lt;a href=&quot;https://onfido.com&quot;&gt;Onfido&lt;/a&gt;. My colleague &lt;a href=&quot;https://caixinha.pt&quot;&gt;Daniel Caixinha&lt;/a&gt; suggested we tried to rewrite one of our machine learning services in Rust, as a way to experiment with the language. He got me and Tom excited, and soon we were a team of 8 trying to actually re-write two microservices instead of just one. Having that much people on board was great. We got to feel different pain points for people more used to one or more of VM-backed, garbage collected and dynamically typed languages. I definitely felt it too. In the end, we completed the re-write of one of the projects, which allowed us to experiment with &lt;a href=&quot;https://rocket.rs/&quot;&gt;Rocket&lt;/a&gt;, &lt;a href=&quot;https://serde.rs/&quot;&gt;Serde&lt;/a&gt; and the &lt;a href=&quot;https://github.com/tensorflow/rust&quot;&gt;Rust bindings for Tensorflow&lt;/a&gt;. It was a great feeling, to have a working version of something after only 2 days of hacking, when the majority of people had not yet programmed a single line of Rust.&lt;/p&gt;

&lt;p&gt;After the Hackathon, I got back to the singly linked lists. I was having to dwelve into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt;, lifetimes and lots of… I had been warned… ownership transfers. While it made sense, given the language’s constraints optimise for safety (while trying to not compromise on speed), it didn’t &lt;em&gt;feel right&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But I found hope.&lt;/p&gt;

&lt;p&gt;One of my personal Rust heroes is &lt;a href=&quot;https://github.com/BurntSushi&quot;&gt;Andrew Gallant&lt;/a&gt;. He’s the maintainer of a number of cool projects like &lt;a href=&quot;https://github.com/BurntSushi/ripgrep&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ripgrep&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/BurntSushi/xsv&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xsv&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/rust-lang/regex&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rust-lang/regex&lt;/code&gt;&lt;/a&gt;. These are projects &lt;a href=&quot;https://blog.burntsushi.net/ripgrep/&quot;&gt;known for their performance&lt;/a&gt;. As good candidates for role-model projects, I went looking around the code. It looked so much cleaner than the stuff I was having to do to make a singly linked list work. 😭 Was I looking at idiomatic Rust code? Error handling with the &lt;a href=&quot;https://doc.rust-lang.org/std/result/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt;&lt;/a&gt; type, structs with well-defined fields and mostly primitive or &lt;em&gt;user-defined&lt;/em&gt; types, almost never needing to explicitly set lifetimes (thanks to the &lt;a href=&quot;https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html?highlight=elision#lifetime-elision&quot;&gt;&lt;em&gt;lifetime elision rules&lt;/em&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h1 id=&quot;having-fun&quot;&gt;Having fun&lt;/h1&gt;

&lt;p&gt;Shortly after this, my co-worker Tom kept working on his &lt;a href=&quot;https://github.com/orf/git-workspace&quot;&gt;personal project&lt;/a&gt;, and he &lt;a href=&quot;https://tomforb.es/i-hope-i-hate-this-code-one-day/&quot;&gt;blogged&lt;/a&gt; about the sense of &lt;em&gt;fun&lt;/em&gt; one should thrive for when learning a new programming language.&lt;/p&gt;

&lt;p&gt;While skip lists are fun (I highly recommend you reading the paper about them, and particularly understanding the trade-offs when comparing to other data structures), it didn’t feel like the right project to continue pursuing in light of the conflicting goals I had: learning the language, not getting sucked into premature optimisation details, and &lt;em&gt;having fun&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;One way I was having fun outside of programming, was by playing &lt;a href=&quot;https://en.wikipedia.org/wiki/Five-a-side_football&quot;&gt;five-a-side football&lt;/a&gt; with a group of friends. We usually play at &lt;em&gt;Estádio Universitário de Lisboa&lt;/em&gt; and the way I book the football or futsal pitch is by filling in an online form that sends an e-mail with my booking details. Everytime, wherever I am, I need to fill in the same details over and over again. Other than the day, everything is exactly the same: my name, fiscal number, phone number, e-mail, booking hours, preferred pitch, etc. One way to automate this would be to get a template of the absolutely necessary information for a booking, and have a way to submit that info without needing to go about writing it all down in the form ourselves.&lt;/p&gt;

&lt;p&gt;I settled on inspecting the POST request. I now had a list of query string parameters where the information for the booking lived. I had the endpoint, and some headers and fields I wasn’t sure about. But that was enough to get going. I put my Rust belt on. Created some structs. Springled some &lt;a href=&quot;https://github.com/serde-rs/serde&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serde&lt;/code&gt;&lt;/a&gt; magic on top of them for easy serialisation to various different outputs (e.g., &lt;a href=&quot;https://github.com/serde-rs/json&quot;&gt;JSON&lt;/a&gt;, &lt;a href=&quot;https://github.com/samscott89/serde_qs&quot;&gt;query string&lt;/a&gt;, &lt;a href=&quot;https://github.com/dtolnay/serde-yaml&quot;&gt;YAML&lt;/a&gt;, etc.).&lt;/p&gt;

&lt;p&gt;At this point, I was ready to hard-code some values to fill in the structs, but I didn’t have a way to perform an actual request yet. And so, &lt;a href=&quot;https://github.com/algesten/ureq&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ureq&lt;/code&gt;&lt;/a&gt; came to the resque. When I started using it, async-await &lt;a href=&quot;https://blog.rust-lang.org/2019/11/07/Async-await-stable.html&quot;&gt;hadn’t landed in stable Rust yet&lt;/a&gt;, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ureq&lt;/code&gt; library provided an intuitive sync approach to HTTP request/response handling. It was also more than enough for what I was trying to achieve. Or so I thought…&lt;/p&gt;

&lt;p&gt;Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::net::ToSocketAddrs&lt;/code&gt; used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ureq&lt;/code&gt; might return IPv4 or IPv6 addresses, and because the website we try to contact is not fully IPv6 ready, I needed to hack a way to set a preferred IP version in order to prioritise IPv4 on demand. You can find that &lt;a href=&quot;https://github.com/dnlserrano/ureq/commit/289d7f592660d2fc954636b6ca85cf16c4c84ec0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When doing the first couple of live requests (I was using a &lt;a href=&quot;http://github.com/sinatra/sinatra&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sinatra&lt;/code&gt;&lt;/a&gt; Ruby mock server in the beginning), I couldn’t really get a successful e-mail back, although I was seeing 200 responses from the server. Apparently, the form just kept returning 200 even when it didn’t actually accept the request. After tinkering a bit with it, I understood this was actually a Drupal form that relied on two &lt;em&gt;“special”&lt;/em&gt; fields: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_build_id&lt;/code&gt;, which were fetched from the DOM of the retrieved form web page. This helps prevent basic replay attacks as well as &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross-site_request_forgery#Synchronizer_token_pattern&quot;&gt;CSRF&lt;/a&gt;. So now I needed to scrape the retrieved web page.&lt;/p&gt;

&lt;p&gt;For scraping, I resorted again to a well-supported library in Rust. &lt;a href=&quot;https://github.com/programble/scraper&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scraper&lt;/code&gt;&lt;/a&gt; did the trick here. It has a nice API for selecting DOM nodes and searching in them. This was all I needed to fetch the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;form_build_id&lt;/code&gt; from the DOM. Cool. Now I could make an actual request. After a few submissions with invalid input (e.g., phone numbers can’t have spaces in them), I got it working as expected.&lt;/p&gt;

&lt;p&gt;I brushed it up with a nice looking CLI with two distinct forms of input. You can either use a list of option arguments for each field to send as part of the request, or use a YAML configuration file with the necessary form data. For YAML deserialisation, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serde&lt;/code&gt; was used again, via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serde_yaml&lt;/code&gt;. CLI functionality was achieved using the well-known &lt;a href=&quot;https://github.com/clap-rs/clap&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clap&lt;/code&gt;&lt;/a&gt; Rust library, which even allows you to use a YAML file to describe your CLI options.&lt;/p&gt;

&lt;p&gt;I called it &lt;a href=&quot;https://github.com/dnlserrano/futbladaj&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;futbladaj&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are still lots of improvements to make. If I think I’ll keep having fun doing those, I might get back to this project. Otherwise, I’ll just move on to something else. It was fun and it served its purpose well. Rust is nice. It is fun. It’s also rigid but with that rigidity comes power. Power to not fail miserably in production. Power to perform. If not in Rust, at least in futsal!&lt;/p&gt;
</description>
        <pubDate>Mon, 11 Nov 2019 08:44:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2019/11/11/learning-rust.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2019/11/11/learning-rust.html</guid>
        
        
      </item>
    
      <item>
        <title>Mutation testing in Elixir</title>
        <description>&lt;p&gt;I first heard about mutation testing when attending a Ruby meetup here in Lisbon. At the time &lt;a href=&quot;https://twitter.com/tadassce&quot;&gt;Tadas&lt;/a&gt; presentation focused on &lt;a href=&quot;https://github.com/mbj/mutant&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mutant&lt;/code&gt;&lt;/a&gt;, &lt;strong&gt;the&lt;/strong&gt; Ruby library for mutation testing by &lt;a href=&quot;https://twitter.com/_m_b_j_&quot;&gt;mbj&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It sounded fun, but not very practical. I think that was because I didn’t understand the full scope, goals and philosophy, so to speak, of mutation testing. This post will help explore mutation testing and why Elixir is a good candidate to give it a shot. I will then introduce a library I’ve been working on &lt;a href=&quot;https://github.com/dnlserrano/exavier&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exavier&lt;/code&gt;&lt;/a&gt; which aims to provide a full-blown mutation testing framework for Elixir.&lt;/p&gt;

&lt;p&gt;It’s still very early days, it may be haunted by major design flaws, stupid ideas and this blog post also serves a bit as a call for help. If you’re out there reading and you think mutation testing might be your thing and you want to help, hit me up &lt;a href=&quot;https://twitter.com/dnlserrano&quot;&gt;@dnlserrano&lt;/a&gt; or &lt;a href=&quot;https://github.com/dnlserrano/exavier&quot;&gt;open a PR&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;what-is-mutation-testing&quot;&gt;What is mutation testing?&lt;/h1&gt;

&lt;p&gt;Let’s take a step back and discuss what exactly all of this is. Mutation testing is a software testing technique to ensure quality in your tests whereby one runs the same set of unit tests multiple times, making different small changes (mutations) to the source code under test each time. The intuition behind it is easy: if the same green tests you had continue to be green after you’ve applied the mutations on the original source code, then something must be off.&lt;/p&gt;

&lt;p&gt;Consider the following example of a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;even?/1&lt;/code&gt; function that checks if a given number is even:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We could think of adding this simple test:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorldTest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ExUnit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;async:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@subject&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorld&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;when even&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this, we’re testing a basic case of an even number. Good, right?&lt;/p&gt;

&lt;p&gt;Imagine we now &lt;em&gt;mutate&lt;/em&gt; the original code to the following:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# == replaced by &amp;gt;=&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our test will still be green given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rem(2, 2)&lt;/code&gt; yields &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;, which is of course still &lt;em&gt;greater than or equal to&lt;/em&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt;) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is what we refer to as a “live mutant”. Code is changed, but the test still passes. For each test you run, which will contain a given mutation, you register if it passed (generated a live mutant) or failed (mutant is successfully killed). This will be important for actually using mutation testing in practice, which I will address later on in this post.&lt;/p&gt;

&lt;p&gt;So, we changed our source code, and the test still passes. This says something about our tests. In this case in particular, it tells us we’re not considering a scenario in which, the remainder of 2 being greater than 0, the return of the function should be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;. If you think about what it means for a second, turns out we forgot to test the “odd cases”:&lt;/p&gt;

&lt;div class=&quot;language-elixir highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorldTest&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ExUnit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;async:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;@subject&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HelloWorld&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;when even&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;when odd&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;@subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;even?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now one of the tests would still pass, but we’d have a new one that would fail. Summing it up, we have:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 live mutant
    &lt;ul&gt;
      &lt;li&gt;test “when even”, still passed after mutation&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;1 killed mutant
    &lt;ul&gt;
      &lt;li&gt;test “when odd”, failed after mutation&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It means we had a killed mutant rate of 1/2, which translates to 50% “mutation coverage.”&lt;/p&gt;

&lt;h3 id=&quot;using-mutation-testing&quot;&gt;Using mutation testing&lt;/h3&gt;

&lt;p&gt;Using mutation testing “in the wild” can translate into running it as part of your CI pipeline on every merge to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;. Each time you do that, your unit tests will run. If they pass, you can run a mutation suite and verify that your mutation coverage is above a specified threshold (e.g., 70%). Other options may ideally be passed to the mutation testing framework you’re using so you can further tweak your mutation test suite. Such configurations might involve excluding certain files from mutation testing, only running it for specified folders, modifying the default threshold value, etc.&lt;sup&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;h1 id=&quot;exavier&quot;&gt;exavier&lt;/h1&gt;

&lt;p&gt;I’ve implemented the ideas of mutation testing into an Elixir library I called &lt;a href=&quot;https://github.com/dnlserrano/exavier&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exavier&lt;/code&gt;&lt;/a&gt;. Its goal is to provide a full-blown mutation testing framework (think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ex_unit&lt;/code&gt; for mutation testing with much worse performance and code quality at this point in time). You can run a set of mutation tests by doing:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mix exavier.test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The output would be something like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;......................

(...)

16) test when infinity (Elixir.HelloWorldTest)
  - if(y == :special) do
  -   :yes
  - else
  -   :no
  - end

  + if(true) do
  +   :yes
  + else
  +   :no
  + end

/Users/dnlserrano/Repos/exavier/test/hello_world_test.exs:10


22 tests, 6 failed (mutants killed), 16 passed (mutants survived)
27.27% mutation coverage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It reports each mutant that survived, alongside the mutation(s) made and shows an aggregate result of your mutation coverage.&lt;/p&gt;

&lt;h2 id=&quot;mutators&quot;&gt;Mutators&lt;/h2&gt;

&lt;p&gt;Mutators specify ways in which we can mutate the code. Currently we have 5 proof-of-concept mutators available in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exavier&lt;/code&gt;. It should be easy to extend the library with other mutators or even have the possibility (soon) to run your own custom mutators that live only inside your specific code-base, if that’s more your thing.&lt;/p&gt;

&lt;p&gt;Each mutator works by manipulating the &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;AST&lt;/a&gt; of Elixir code, and that’s part of what makes Elixir such a great fit for trying to develop such a testing framework in this language. Having so many powerful tools around code and the structure of that code in the language, it seemed like an amazing opportunity that shouldn’t be missed. That and the fact it was fun, of course.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The library still needs a lot of work, particularly around adding more mutators and configurations. The major points I’d like to see tackled in the short-time are available in the &lt;a href=&quot;https://github.com/dnlserrano/exavier#to-be-done&quot;&gt;“To be done”&lt;/a&gt; section of the project’s README.&lt;/p&gt;

&lt;p&gt;I’d love to have some feedback about how you’re using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exavier&lt;/code&gt; and what we can, hopefully together as a community, do to make it better.&lt;/p&gt;

&lt;p&gt;Happy mutations! 🖖&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; A very well-known issue in mutation testing is that of &lt;a href=&quot;http://pitest.org/quickstart/basic_concepts/#equivalent-mutations&quot;&gt;Equivalent Mutations&lt;/a&gt;, which is out of the scope of this blog post.&lt;/p&gt;
</description>
        <pubDate>Mon, 22 Jul 2019 12:19:00 +0000</pubDate>
        <link>https://dnlserrano.github.io//2019/07/22/exavier-mutation-testing-elixir.html</link>
        <guid isPermaLink="true">https://dnlserrano.github.io//2019/07/22/exavier-mutation-testing-elixir.html</guid>
        
        
      </item>
    
  </channel>
</rss>
