<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on Simple Made Daily</title>
    <link>https://caioferreira.dev/posts/</link>
    <description>Recent content in Posts on Simple Made Daily</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 07 May 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://caioferreira.dev/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Como estudar na área de Tecnologia?</title>
      <link>https://caioferreira.dev/posts/aprendendo-em-tech/</link>
      <pubDate>Tue, 07 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/aprendendo-em-tech/</guid>
      <description>Introdução Nos últimos dois meses, várias pessoas me procuraram para saber como organizo meus estudos. Decidi, então, compilar um resumo das minhas técnicas e métodos. Espero que encontrem aqui ferramentas úteis!
Método de estudos Primeiramente, o método de estudo é algo muito pessoal. As práticas que mencionarei tiveram êxito para mim. A primeira e mais importante tarefa é começar a desenvolver o seu método:
Com que frequência você deseja estudar? Como você vai organizar o conhecimento?</description>
      <content:encoded><![CDATA[<h2 id="introdução">Introdução</h2>
<p>Nos últimos dois meses, várias pessoas me procuraram para saber como organizo meus estudos. Decidi, então, compilar um resumo das minhas técnicas e métodos. Espero que encontrem aqui ferramentas úteis!</p>
<h2 id="método-de-estudos">Método de estudos</h2>
<p>Primeiramente, o método de estudo é algo muito pessoal. As práticas que mencionarei tiveram êxito para mim. A primeira e mais importante tarefa é começar a desenvolver o seu método:</p>
<ul>
<li>Com que frequência você deseja estudar?</li>
<li>Como você vai organizar o conhecimento?</li>
<li>Que tipo de conteúdo mais te engaja (livros, vídeo aulas, cursos, etc)?</li>
<li>Quanta estrutura você quer dar aos seus estudos? Você funciona melhor com um planejamento detalhado e prazos, ou prefere algo mais flexível, seguindo os tópicos que mais capturam sua atenção no momento?</li>
</ul>
<p>Posso falar por mim: estudo todos os dias, leio artigos ou livros sempre que possível. Organizo meu conhecimento em notas no <strong>Obsidian</strong>, usando uma estrutura que combina o <strong>Zettelkasten</strong> com o método <strong>PARA</strong>. Prefiro, de longe, conteúdos escritos e não gosto de fazer grandes planejamentos; estudo o que tenho vontade, quando tenho vontade.</p>
<h2 id="fontes-de-informação">Fontes de informação</h2>
<p>Explico que existem duas formas de desenvolvimento: horizontal e vertical.</p>
<ul>
<li>O desenvolvimento horizontal foca em criar uma base de conhecimento em um universo, abrangendo vários tópicos. Isso não implica em um estudo superficial, mas em entender como os tópicos se relacionam em um contexto. Dependendo do universo, como em segurança da informação, esse tipo de estudo pode ser um dos mais desafiadores.</li>
<li>O desenvolvimento vertical, por sua vez, foca em aprofundar um tópico e aprender o máximo possível sobre ele. O objetivo é estudar como se quisesse se tornar um especialista naquele assunto.</li>
</ul>
<p>Na carreira, é essencial manter ambos os tipos de desenvolvimento ativos. Para isso, recomendo realizar uma curadoria constante de diferentes tipos de fontes de informação:</p>
<ul>
<li><strong>Newsletters</strong> (horizontal): para mim, são a melhor ferramenta para um profissional de tecnologia hoje. Faço uma seleção contínua das newsletters que assino. Ao receber uma nova publicação, seleciono links relevantes, adiciono-os à minha lista de leitura e os consumo gradualmente.</li>
<li><strong>Feeds de agregadores</strong> (horizontal): sites como Hackersnews e Lobste.rs têm feeds RSS organizados por tema. Funcionam como uma linha do tempo de uma rede social, com muito ruído, mas ocasionalmente você encontra artigos úteis. É crucial saber gerenciar fontes de alto e baixo ruído para que não interfiram na eficiência do seu consumo.</li>
<li><strong>Blogs/Publicações especializadas</strong> (vertical): prefiro separar essas fontes dos livros tradicionais porque geralmente são mais sucintas, têm mais exemplos e exercícios práticos e são mais atualizadas que os livros. Exemplos incluem: <a href="https://quii.gitbook.io/learn-go-with-tests">https://quii.gitbook.io/learn-go-with-tests</a> e <a href="https://www.fuzzingbook.org/">https://www.fuzzingbook.org/</a>.</li>
<li><strong>Papers</strong> (vertical): começar a ler artigos acadêmicos pode ser desafiador, mas é um passo importante para conhecer a vanguarda do desenvolvimento. Quanto antes começar, melhor.</li>
<li><strong>Livros e cursos</strong> (vertical): geralmente os utilizo para tópicos que evoluem mais lentamente, como algoritmos, matemática e criptografia. Nessas áreas, você encontrará publicações excelentes e ainda relevantes.</li>
</ul>
<p>Com o tempo, ao consumir conteúdos, você acumulará links, artigos, livros, ferramentas: todo tipo de referência. Cuide bem dessa coleção, escolha uma forma de organização, um aplicativo, o que preferir. Essa coleção se tornará seu jardim, de onde extrairá insumos para produzir conhecimento.</p>
<p>No entanto, é fundamental <strong>não cair</strong> na <a href="https://zettelkasten.de/posts/collectors-fallacy/">falácia do colecionador</a>, onde simplesmente acumulamos pilhas de materiais e nos damos por satisfeitos.</p>
<h2 id="organizando-conhecimento">Organizando conhecimento</h2>
<p>Um dos meus maiores arrependimentos foi não ter começado a manter notas organizadas mais cedo! Minha maior recomendação aqui é a simplicidade. É muito fácil cair na armadilha de otimizar a estrutura das suas notas e ser extremamente produtivo (eu já caí várias vezes), criando um sistema demasiadamente complicado.</p>
<p>Algumas coisas que faço para estruturar o conhecimento são:</p>
<ul>
<li>Notas de Literatura: fazer notas detalhadas e com suas próprias palavras custa muito tempo, então eu criei um tipo diferente de nota chamada Nota de Literatura. Nela, deposito informações relevantes da fonte de informação, por vezes copiando diretamente, outras vezes reescrevendo.
<ul>
<li>Assistente AI: embora o objetivo seja produzir notas mais simples e rápidas, a AI tem me ajudado a produzir notas de literatura com qualidade de notas de assunto. Eu uso um prompt que gera uma lista de perguntas sobre os tópicos abordados na fonte de informação. Isso tem tornado minhas notas de literatura muito mais eficientes. O prompt que uso está disponível publicamente: <a href="https://github.com/caiorcferreira/prompts/blob/main/patterns/thought_provoking.md">thought_provoking prompt</a>.</li>
</ul>
</li>
<li>Notas de Assunto (Subject/Evergreen notes): essas notas são o personagem principal. Elas têm uma vida longa, são atualizadas sempre que aprendo algo novo sobre o assunto e são escritas com minhas próprias palavras. A técnica mais importante para mim aqui é escrever como se estivesse explicando para outra pessoa.</li>
</ul>
<h2 id="aplicando-seu-método">Aplicando seu método</h2>
<p>Existem ainda desafios únicos durante o andamento dos seus estudos. Uma pergunta recorrente é: eu sei qual assunto quero estudar, sei qual tipo de estudo (horizontal ou vertical) e tenho uma curadoria de fontes - <strong>como escolher qual material consumir?</strong> Nesse caso, meu método é:</p>
<ol>
<li>Seleciono material relacionado ao tema que já possa ter armazenado;</li>
<li>Uso <a href="https://pt.linkedin.com/pulse/entendendo-o-google-hacking-na-pratica-e-otimizando-suas-janones">DORKs</a> para pesquisar material no histórico de newsletters e feeds de agregadores.</li>
<li>Dedico um tempo navegando entre os diferentes materiais selecionados, até encontrar um (ou alguns) com um bom equilíbrio entre didática e cobertura do assunto.</li>
</ol>
<p>Isso acaba levando a um começo mais lento, porém, na minha experiência, ele se paga, pois me dá a oportunidade de selecionar materiais de estudo de maior qualidade.</p>
<p>Outro desafio comum é: <strong>como tirar dúvidas sobre o assunto?</strong> Atualmente, LLMs têm sido de grande ajuda para mim (desde que você saiba como lidar com possíveis alucinações). Buscar por comunidades dedicadas ao assunto no Reddit também costuma ter bons resultados.
No entanto, a melhor ferramenta é sua própria coleção de materiais: busque por outros materiais que possam explicar o mesmo conceito a partir de diferentes pontos de vista. Eventualmente, você irá montar um quebra-cabeça, com uma compreensão muito mais abrangente do assunto.</p>
<h2 id="conclusão">Conclusão</h2>
<p>A jornada do aprendizado é profundamente pessoal e varia de indivíduo para indivíduo. As estratégias e métodos que compartilhei refletem minhas experiências e preferências pessoais. É importante encontrar o equilíbrio entre a absorção de informação e a criação de conhecimento, evitando a armadilha de apenas acumular referências.</p>
<p>Lembre-se de que a chave para um estudo eficaz não está apenas em selecionar os materiais certos, mas também em como você organiza e interage com essas informações ao longo do tempo. Espero que este guia sirva como um ponto de partida para você desenvolver um método de estudo que seja tão único e eficaz quanto suas próprias aspirações e necessidades de aprendizado.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to learn in the Tech field?</title>
      <link>https://caioferreira.dev/posts/learning-in-tech/</link>
      <pubDate>Tue, 07 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/learning-in-tech/</guid>
      <description>Introduction Over the past two months, several people have approached me to find out how I organize my studies. Therefore, I decided to compile a summary of my techniques and methods. I hope you find useful tools here!
Study Method Firstly, the study method is something very personal. The practices I will mention have been successful for me. The first and most important task is to start developing your method:</description>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Over the past two months, several people have approached me to find out how I organize my studies. Therefore, I decided to compile a summary of my techniques and methods. I hope you find useful tools here!</p>
<h2 id="study-method">Study Method</h2>
<p>Firstly, the study method is something very personal. The practices I will mention have been successful for me. The first and most important task is to start developing your method:</p>
<ul>
<li>How often do you want to study?</li>
<li>How will you organize knowledge?</li>
<li>What type of content engages you the most (books, video lessons, courses, etc)?</li>
<li>How much structure do you want to give your studies? Do you function better with detailed planning and deadlines, or do you prefer something more flexible, following the topics that capture your attention at the moment?</li>
</ul>
<p>I can speak for myself: I study every day, read articles or books whenever possible. I organize my knowledge in notes in <strong>Obsidian</strong>, using a structure that combines the <strong>Zettelkasten</strong> method with the <strong>PARA</strong> method. I vastly prefer written content and do not like making extensive plans; I study what I want, when I want.</p>
<h2 id="sources-of-information">Sources of Information</h2>
<p>I explain that there are two forms of development: horizontal and vertical.</p>
<ul>
<li>Horizontal development focuses on creating a knowledge base in a universe, covering various topics. This does not imply superficial study but understanding how the topics relate in a context. Depending on the universe, such as in information security, this type of study can be one of the most challenging.</li>
<li>Vertical development, on the other hand, focuses on deepening a topic and learning as much as possible about it. The goal is to study as if you wanted to become an expert on that subject.</li>
</ul>
<p>In your career, it is essential to keep both types of development active. For this, I recommend constantly curating different types of information sources:</p>
<ul>
<li><strong>Newsletters</strong> (horizontal): for me, they are the best tool for a technology professional today. I make a continuous selection of the newsletters I subscribe to. When I receive a new publication, I select relevant links, add them to my reading list, and consume them gradually.</li>
<li><strong>Feed aggregators</strong> (horizontal): sites like Hackersnews and Lobste.rs have RSS feeds organized by theme. They work like a social network timeline, with a lot of noise, but occasionally you find useful articles. It is crucial to know how to manage high and low noise sources so that they do not interfere with the efficiency of your consumption.</li>
<li><strong>Blogs/Specialized publications</strong> (vertical): I prefer to separate these sources from traditional books because they are usually more concise, have more examples and practical exercises, and are more up-to-date than books. Examples include: <a href="https://quii.gitbook.io/learn-go-with-tests">https://quii.gitbook.io/learn-go-with-tests</a> and <a href="https://www.fuzzingbook.org/">https://www.fuzzingbook.org/</a>.</li>
<li><strong>Papers</strong> (vertical): starting to read academic papers can be challenging, but it is an important step to know the cutting edge of development. The sooner you start, the better.</li>
<li><strong>Books and courses</strong> (vertical): I generally use them for topics that evolve more slowly, such as algorithms, mathematics, and cryptography. In these areas, you will find excellent publications that are still relevant.</li>
</ul>
<p>Over time, as you consume content, you will accumulate links, articles, books, tools: all kinds of references. Take good care of this collection, choose a way to organize it, an app, whatever you prefer. This collection will become your garden, from which you will draw inputs to produce knowledge.</p>
<p>However, it is crucial <strong>not to fall</strong> into the <a href="https://zettelkasten.de/posts/collectors-fallacy/">collector&rsquo;s fallacy</a>, where we simply accumulate piles of materials and are satisfied with that.</p>
<h2 id="organizing-knowledge">Organizing Knowledge</h2>
<p>One of my biggest regrets was not starting to keep organized notes earlier! My biggest recommendation here is simplicity. It&rsquo;s very easy to fall into the trap of optimizing your note structure and being extremely productive (I&rsquo;ve fallen several times), creating an overly complicated system.</p>
<p>Some things I do to structure knowledge are:</p>
<ul>
<li>Literature Notes: making detailed notes in your own words takes a lot of time, so I created a different type of note called Literature Note. In it, I deposit relevant information from the source of information, sometimes copying directly, other times rewriting.
<ul>
<li>AI Assistant: although the goal is to produce simpler and quicker notes, AI has helped me produce literature notes with the quality of subject notes. I use a prompt that generates a list of questions about the topics covered in the source of information. This has made my literature notes much more efficient. The prompt I use is publicly available: <a href="https://github.com/caiorcferreira/prompts/blob/main/patterns/thought_provoking.md">thought_provoking prompt</a>.</li>
</ul>
</li>
<li>Subject Notes (Subject/Evergreen notes): these notes are the main character. They have a long life, are updated whenever I learn something new about the subject, and are written in my own words. The most important technique for me here is writing as if I were explaining to someone else.</li>
</ul>
<h2 id="applying-your-method">Applying Your Method</h2>
<p>There are still unique challenges during the course of your studies. A recurring question is: I know which subject I want to study, I know what type of study (horizontal or vertical) and I have a curation of sources - <strong>how to choose which material to consume?</strong> In this case, my method is:</p>
<ol>
<li>I select material related to the theme that I may have already stored;</li>
<li>I use <a href="https://pt.linkedin.com/pulse/understanding-google-hacking-in-practice-and-optimizing-your-janones">DORKs</a> to search for material in the newsletters&rsquo;s archives and feed aggregators.</li>
<li>Then, I dedicate some time navigating among the selected materials, until I find one (or a few) with a good balance between didactic approach and subject coverage.</li>
</ol>
<p>This ends up leading to a slower start, but in my experience, it pays off, as it gives me the opportunity to select higher quality study materials.</p>
<p>Another common challenge is: <strong>how to ask questions about the subject?</strong> Currently, LLMs have been of great help to me (as long as you know how to deal with possible hallucinations). Searching for dedicated communities on Reddit often yields good results.
However, the best tool is your own collection of materials: look for other materials that may explain the same concept from different perspectives. Eventually, you will put together a puzzle, gaining a much more comprehensive understanding of the subject.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The learning journey is deeply personal and varies from individual to individual. The strategies and methods I have shared reflect my experiences and personal preferences. It&rsquo;s important to find a balance between absorbing information and creating knowledge, avoiding the trap of just accumulating references.</p>
<p>Remember, the key to effective study is not only in selecting the right materials but also in how you organize and interact with these materials over time. I hope this guide serves as a starting point for you to develop a study method that is as unique and effective as your own learning aspirations and needs.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Notes on Dos and Don&#39;ts of Machine Learning in Computer Security</title>
      <link>https://caioferreira.dev/posts/notes-on-do-and-donts-of-ml-in-security/</link>
      <pubDate>Thu, 22 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/notes-on-do-and-donts-of-ml-in-security/</guid>
      <description>Following the subject from my last post, Reflections about Supervised Learning on Security, I put down some more thoughts about the implementation of learning-based systems in the Security domain.
This is my extension to the problems and recommendations presented on the paper Dos and Don&amp;rsquo;ts of Machine Learning in Computer Security (Quiring, et al, 2022). I encourage you to also read the paper, as it&amp;rsquo;s excellent and provide a lot of insights about how to better build machine learning models.</description>
      <content:encoded><![CDATA[<p>Following the subject from my last post, <a href="https://caioferreira.dev/posts/reflections-supervised-ml/reflections-supervised-learning-in-security/">Reflections about Supervised Learning on Security</a>, I put down some more thoughts about the implementation of learning-based systems in the Security domain.</p>
<p>This is my extension to the problems and recommendations presented on the paper <a href="https://mlsec.org/docs/2022-sec.pdf">Dos and Don&rsquo;ts of Machine Learning in Computer Security</a> (Quiring, et al, 2022). I encourage you to also read the paper, as it&rsquo;s excellent and provide a lot of insights about how to better build machine learning models.</p>
<h2 id="machine-learning-workflow">Machine learning workflow</h2>
<h3 id="data-collection">Data collection</h3>
<blockquote>
<p>Pitfalls: Sampling Bias, Label Inaccuracy</p>
</blockquote>
<p>As with any other domain, Security is also heavily affected by bad data quality used for training. However, it&rsquo;s often worse in Security because data acquisition of adversary activity is usually hard and the method&rsquo;s used to acquire it will bring a bias.</p>
<p>Let&rsquo;s take for example a honeypot implemented with a vulnerable Apache Server. Even though there are lots of bad actors in the wild, if you have a medium size environment, the volume of data they will produce attacking your honeypot will not come near to the volume of packets in your production network. Also, the TTPs you are going to see will come from threat actors that are used to leverage Apache Server in their kill chain, possibly leaving other adversaries, that may be focusing on other types of vectors, producing different kill chains, out of the dataset.</p>
<p>On top of that, we are usually dealing with lots of examples. So, if we have label inaccuracies, dealing with them is a lot harder. We can&rsquo;t just apply common techniques like using the mode to fill in, because in the Security domain, the adversary is actively trying to mimic the distribution of the benign cases. Therefore, we have very little space for noise, as it would blur even more the distinction between the outcomes.</p>
<p>Besides the recommendations presented in the article, I would add two more:</p>
<ul>
<li>User open and disseminate datasets whenever possible. Dataset sharing is still uncommon on the Security community. We already have some network and host datasets, but there are many more assets nowadays (logs for cloud, kubernetes, CDN, CI/CD, etc), and the technologies on networks and hosts are constantly evolving, posing the need for these datasets to be always updated.</li>
<li>Use a model design that depends less on adversary data, such as we discussed in the previous article. This will reduce the dependency on the adversary behavior and increase the sources of data available to use.</li>
</ul>
<h3 id="model-design-and-implementation">Model design and implementation</h3>
<blockquote>
<p>Pitfalls: Data Snooping, Spurious Correlations, Biased Parameter Selection</p>
</blockquote>
<p>Developing machine learning models is no easy task. Feature engineering, hyperparameters optimization, data preparation and appropriate splitting for validation (to avoid snooping). These are just some of the challenges when taking on this endeavor.</p>
<p>Using tools like AutoML may help reduce the burden, however it won&rsquo;t take care of everything. In the end, you still need to understand your data characteristics and how it&rsquo;s related to your problem.</p>
<p>But, some of these relations and characteristics tend to repeat. Time relations, for example, are extremely common and important in a lot of Security problems. Therefore, I suggest that every Security team doing machine learning on its own to think about how they can extract such aspects into reusable components.</p>
<p>This has the added benefit to scale the impact of the Security members that are more focused on implementing machine learning. Our domain has many different areas and enabling other teams and specialists to more easily and correctly implement models, even as proofs of concept, expands the possibilities of what the enterprise can achieve.</p>
<p>In the end, the best strategy to best tackle all these challenges and be more prepared to handle pitfalls it to avoid jumping to complex and sexy algorithms from the start, such as neural networks.</p>
<p>Using explanation techniques, as the article suggests, can help you catch spurious correlation, but even better is to use simpler and more understandable methods. I have been seen great results with simple probabilistic methods, such as with Histogram Based Outlier Score. Using a simple algorithm, we can immediately see what is driving its decision and catch faster these types of pitfalls.</p>
<h3 id="performance-evaluation">Performance evaluation</h3>
<blockquote>
<p>Pitfalls: Inappropriate Baseline, Inappropriate Performance Measures, Base Rate Fallacy</p>
</blockquote>
<p>As I mentioned before, most times when building a learning-based system in Security, we end up with imbalanced and noisy datasets. This demands a special care when choosing performance metrics to best reflect our model. Building on the previous suggestion, having common components that use by default metrics more fit to most problems in Security, such as precision, recall, and MCC, would reduce the change of human error.</p>
<p>However, even after computing correct performance metrics, we still need good baselines to compare them against. The article suggest goods options such as using simple methods or automated machine learning. I would add that comparing it with vendor products can also be a good experiments to fully understand the impact of replacing the third party with the model.</p>
<h3 id="operation">Operation</h3>
<blockquote>
<p>Pitfalls: Lab-Only Evaluation, Inappropriate Threat Model</p>
</blockquote>
<p>Although the article identifies Lab-Only Evaluation as a common pitfall in Security model, I think this is one of the topic where in Security we have the most options to address.</p>
<p>First, we could use adversary emulation frameworks and popular attack toolkits to launch real world attacks against a test environment and study the model response. Another option would be to set up a honeypot and evaluate how well the model performed by comparing to a human analyses of the evidences afterward.</p>
<p>This type of experiment would also feed back into our threat model. Given the hype of AI in latest months, more knowledge is beings shared and produced about security best practices and process for machine learning, however we can still consider the topic in its infancy.</p>
<p>One special vector that we are still starting to discuss is supply-chain attacks to models, specially with the popularization of transfer-learning for more complex algorithms such as LLMs.</p>
<p><a href="https://www.splunk.com/en_us/blog/security/paws-in-the-pickle-jar-risk-vulnerability-in-the-model-sharing-ecosystem.html">Splunk showed</a> that more than 80% of HuggingFace&rsquo;s models use pickle-serialized code, which is vulnerable to arbitrary code execution and code injection, although it&rsquo;s not possible to say if any of them is malicious.</p>
<p>But, besides low-level vulnerabilities like this, a transfer-learning based model can also inherit the biases (intentionally placed or not) from the original model. That is, if the original model is vulnerable to an adversarial example, there is a high risk that your new model is going to also be.</p>
<p>This opens the possible for two types of attacks:</p>
<ol>
<li>Malicious models: base models shared with adversarial examples trained, such as a large batch of images with a negative label having a red square, making the model learn that any image with a red square should be classified as negative, therefore implanting a bypass. This type of attack would be extremely difficult to detect.</li>
<li>Cross model generalization: as <a href="https://arxiv.org/pdf/1312.6199.pdf">Szegedy, et all points in their paper</a>, different models may be susceptible to the same adversarial examples, even when having different hyperparameters. In this case, the models would not even be so different, therefore an adversary could search for adversarial examples against the original model and just use them in the target system, with a high chance of success.</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, implementing learning-based systems in the security domain presents challenges that require careful consideration. Addressing issues related to data collection, model design and implementation, performance evaluation, and operational considerations is crucial. By implementing these recommendations and reflecting on these problems, organizations can enhance their capabilities in security.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Reflections about Supervised Learning on Security</title>
      <link>https://caioferreira.dev/posts/reflections-supervised-ml/reflections-supervised-learning-in-security/</link>
      <pubDate>Sun, 23 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/reflections-supervised-ml/reflections-supervised-learning-in-security/</guid>
      <description>Supervised learning is a technique that aims to learn a hypothesis function $h$ that fits a behavior observed in the real-world, which is governed by an unknown function $f$.
To learn this function, we use a set of example data points composed of inputs (also called features) and outcomes (sometimes called labels). These example data points were sampled from the real world behavior, i.e. from the function $f$, at some time in the past.</description>
      <content:encoded><![CDATA[<p>Supervised learning is a technique that aims to learn a hypothesis function $h$ that fits a behavior observed in the real-world, which is governed by an unknown function $f$.</p>
<p>To learn this function, we use a set of example data points composed of inputs (also called features) and outcomes (sometimes called labels). These example data points were sampled from the real world behavior, i.e. from the function $f$, at some time in the past. Our goal is that we can extract knowledge from the past to figure out this behavior on unseen data beforehand. This knowledge extracted from the set of examples is materialized on the $h$ function.</p>
<p>In Security, we can imagine some examples where this would be useful, like trying to learn if an HTTP request contains malicious payload or if some set of bytes is a malware or not. However, supervised learning is less often used in Security than in many other domains.</p>
<p>This happens because the principles of the supervised learning theory conflicts with the nature of Security, limiting its application. But, by understanding these principles, it is also possible to see how to best apply this technique and how it can maybe useful.</p>
<h2 id="stationary-assumption">Stationary assumption</h2>
<p>The most important principle in supervised learning is the stationary assumption. When the data that represents the real world behaviors follows the stationary assumption, it means that predicting the behavior using past example is approximately correctly.</p>
<p>The <strong>stationary assumption</strong> states that the behavior that is being learned don&rsquo;t change through time. This has some important consequences:</p>
<ol>
<li>We expect that each data point is independent of each other. This is important, because if there were causal effects between data points, then the features of a data point $x_1$, caused by $x_0$, would vary with a probability that is a combination of the probability distribution and the effect of $x_0$, hence the distribution would not remain the same over time, because $x_0$ and $x_1$ would vary in different ways. For example, in a box with 1 blue ball and 2 red balls, the probability of picking a blue ball when drawing the first one from the box is 33% while a red one would be 66%. However, if the first one is indeed blue, then the probability of drawing a red ball as the second one is 100%. The first data point (blue ball draw) changed the probability of the second data point.</li>
<li>We expect that each data point is identically distributed, i.e. each data point should be drawn from the same probability distribution. We could learn the shopping behavior using data ranging from Black Friday to New Years, however the users&rsquo; behavior in this time is completed different from the rest of the year, therefore the data used to learn has a different probability distribution than the unseen data on which we are going to make predictions.</li>
</ol>
<p>Any dataset that follows these two characteristics is said to hold the i.i.d assumption (independent and identically distributed). The importance for our training datasets on supervised learning to be i.i.d is because it connects the past to the future, without it, any inference made on the available data would be invalid.</p>
<p>Understand the i.i.d assumption is specially important for Security Machine Learning because it is one of the areas where causality and behavior shifts are most present. So, exactly how this assumption affects our ability to do Supervised Machine Learning?</p>
<h3 id="causality">Causality</h3>
<p>Many threat behaviors have causal nature, and therefore we should have a lot of care when preparing our datasets and choosing our validation methods.</p>
<p>A good example is malware classification, where you could have many samples from various families from different years. Each family generation influences each other, and sometimes they have similar characteristics.
A special bad situation that could happen with this is that during splitting of the dataset between training and testing, without taking into account the time relation of the families and samples, then you could end up training the model with future information and testing against past samples. This would produce falsely accurate results that would not generalize in the real-world.</p>
<p>There are ways to deal with this, but it depends on the type and strength of the causal relation between the data. For this case, ensuring that the newest malware samples are used for test should be enough.</p>
<h3 id="behavior-shift">Behavior Shift</h3>
<p>We could create a model to learn a threat behavior like the profile of a botnet, however once we started responding effectively to it, adversaries would adapt and our model would become useless because the new botnets would have a totally different behavior.
This would be the case of a change in the probability distribution from which the features are drawn, leading to the break of our assumption.</p>
<h2 id="looking-to-the-other-side">Looking to the other side</h2>
<p>Although causality may be addressed by good data preparation, preventing a model to be become outdated due to behavioral shift is almost impossible. However, this problem isn&rsquo;t a new one in Security, such that one best practices is to instead of trying to detect and block malicious action, defenders should define what a legitimate system behavior looks like and block everything else. This can be summarized as: allow lists are more secure than block lists.</p>
<p>We can apply the same philosophy for supervised learning, by modeling profiles of legitimate behavior, which usually are more stable. Then, new data points are classified against these multiple profiles models, finally a meta-classifier is used to choose which one is the best fit or if it&rsquo;s an outlier. This has the ability to catch any new threat behavior that deviates from the know legitimate profiles.</p>
<p>This combination of multiple models is called ensemble learning, which has shown to improve models performance, like when comparing a Decision Tree model to a Random Forest one.</p>
<p>As a side bonus, since this way of building models does not depend on knowing threat behaviors, it avoids the common problem in mining data for Security that usually produce highly unbalanced datasets. We can train each profile using the true positive data of others profiles as false examples, assuming each profile is mutually exclusive.</p>
<p>The main challenge in this approach is that adversaries may try to mimic legitimate profiles, however like the authors of Notos showed, this can be useful sometimes, as in their cases this would imply in adversaries using a more stable network infrastructure that would be easily defeated by static block lists. Therefore, forcing adversaries to mimic a legitimate profile could also reduce their capabilities.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, supervised learning is a powerful technique that allows us to extract knowledge from past data points to predict future behavior. However, in the Security domain, applying it comes with unique challenges due to the presence of causality and behavior shifts. Understanding the stationary assumption and the importance of having an i.i.d dataset is crucial, because it informs us how to best prepare our datasets, choose our validation methods and, most importantly, what are the best behaviors to be modeled using supervised learning.</p>
<p>Luckily, by modeling profiles of legitimate behavior, and catching new threat behavior that deviates from known legitimate profiles, we can build intelligent allow lists.</p>
<p>While there are challenges, with proper preparation and understanding, supervised learning can be a valuable tool in Security.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://astrolavos.gatech.edu/articles/Antonakakis.pdf">Notos: Building a Dynamic Reputation System for DNS</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Implementing a safe and sound API Key authorization middleware in Go</title>
      <link>https://caioferreira.dev/posts/golang-secure-api-key-middleware/</link>
      <pubDate>Sat, 05 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/golang-secure-api-key-middleware/</guid>
      <description>How to design a more secure API Key handling in Go</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="./cover.jpg" alt=""  />
</p>
<p>A common requirement that I face on multiple projects is to safeguard some API endpoints to administrative access, or to provide a secure way for other applications to consume our service in a controlled and traceable manner.</p>
<p>The usual solution for it is API Keys, a simple and effective authorization control mechanism that we can implement with a few lines of code. However, when doing, so we also need to be aware of threats and possible attacks that we may suffer, specially due to the usual privileges that these keys provides.</p>
<p>Therefore, we are going to analyze common points of concern and design a solution that improve our security posture while keeping it simple.</p>
<h2 id="api-keys-threats">API Keys threats</h2>
<p>There are two main concerns when implementing an API Key authorization scheme: <strong>key provisioning</strong> and <strong>timing attacks</strong>. Let&rsquo;s review each threat before designing solutions to address them.</p>
<h3 id="key-provisioning">Key Provisioning</h3>
<p>The key storage is directly related to how applications expect these secrets to be provided to them. Environment variables are the most common solution used on modern services since they are widely supported and don&rsquo;t incur a high reading cost (in contrast to files) allowing for dynamic changes to be easily detected.</p>
<p>However, the way developers usually define the environment variables are through scripts or configuration files, for example using a <a href="https://kubernetes.io/docs/concepts/configuration/secret/">Kubernetes Secret</a> manifest. This introduces a serious threat of API Keys being committed to git repositories, which in the event of data leakage from the internal VCS management system would expose these credentials.</p>
<p>Note: remember that once committed, even if the keys are deleted from the source files, the information is already on the repository history and is easily searchable with tools like <a href="https://github.com/trufflesecurity/truffleHog">TruffleHog</a>.</p>
<p>Therefore, <strong>please do not commit your API Keys to git</strong>!</p>
<h3 id="timing-attacks">Timing Attacks</h3>
<p>Once your application is configured with the available API Keys, you need to verify that the end-user provided key (let&rsquo;s call this the <em>user key</em>) is correct. Doing so with a naive algorithm, like using == operator, will make the verification end on the first incorrect character, hence reducing the time taken to respond.</p>
<p>A timing attack takes advantage of this scenario by trying to guess the correct characters of a secret based on how long the application took to respond. If the guess is right, the response will take slightly longer than if it&rsquo;s wrong.</p>
<p>Naturally, since equality checks are orders of magnitude faster than the network roundtrip, this type of attack is extremely difficult to perform because it depends on a statistical analysis of many response samples. By looking at the time distribution produced by two different characters, one can infer that if they are different, inferring that the greater one is the correct value. For an extensive discussion of statistical techniques to help perform this attack see <a href="https://www.blackhat.com/docs/us-15/materials/us-15-Morgan-Web-Timing-Attacks-Made-Practical-wp.pdf">Morgan, Morgan 2015</a>.</p>
<h2 id="middleware-design-and-implementation">Middleware design and implementation</h2>
<p>Having these threats in mind, we can design a suitable solution. Let&rsquo;s start with the most simple API Key middleware implementation possible and iterate from it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ApiKeyMiddleware</span><span class="p">(</span><span class="nx">cfg</span> <span class="nx">conf</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">logger</span> <span class="nx">logging</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">handler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apiKeyHeader</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeyHeader</span> <span class="c1">// string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">apiKeys</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeys</span> <span class="c1">// map[string]string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="nx">reverseKeyIndex</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">key</span> <span class="o">:=</span> <span class="nx">apiKeys</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">reverseKeyIndex</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="p">=</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">next</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">apiKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">bearerToken</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="nx">apiKeyHeader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;request failed API key authentication&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid API key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">_</span><span class="p">,</span> <span class="nx">found</span> <span class="o">:=</span> <span class="nx">reverseKeyIndex</span><span class="p">[</span><span class="nx">apiKey</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">found</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">hostIP</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">					<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;failed to parse remote address&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">					<span class="nx">hostIP</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span>
</span></span><span class="line"><span class="cl">				<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;no matching API key found&#34;</span><span class="p">,</span> <span class="s">&#34;remoteIP&#34;</span><span class="p">,</span> <span class="nx">hostIP</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">				<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid api key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// bearerToken extracts the content from the header, striping the Bearer prefix
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">bearerToken</span><span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">header</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">rawToken</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">header</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">pieces</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">SplitN</span><span class="p">(</span><span class="nx">rawToken</span><span class="p">,</span> <span class="s">&#34; &#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">pieces</span><span class="p">)</span> <span class="p">&lt;</span> <span class="mi">2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;token with incorrect bearer format&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">token</span> <span class="o">:=</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">TrimSpace</span><span class="p">(</span><span class="nx">pieces</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">token</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A middleware is a function that takes an <code>http.Handler</code> and returns an <code>http.Handler</code>. In this code, the function <code>ApiKeyMiddleware</code> is a factory that creates an instance of the middleware with the provided configuration and logger. The <code>config.Config</code> is a struct populated from environment variables and <code>logging.Logger</code> is an interface that can be implemented using any logging library or the standard library. You could pass only the header and map of keys, but for clarity we choose to denote the dependency from this middleware to the configuration.</p>
<p>After extracting the fields that it relies on, the function creates a reverse index of the API Keys, which is originally a map from a key id/name to the key value. Using this reverse index it&rsquo;s trivial to verify if the user key is valid by doing a map lookup on line 18.</p>
<p>However, this approach expects the API Keys as plaintext values and is susceptible to timing attacks, because its validation algorithm is not constant time.</p>
<h3 id="using-key-hashes-for-validation">Using key hashes for validation</h3>
<p>To improve the key provisioning workflow, we can use a simple yet effective solution: expect the available keys to be hashes. Using this approach we can now commit our key hashes to our repository because even in the event of a data leak they could not be reversed to their original value.</p>
<p>Let&rsquo;s use the SHA256 hashing algorithm to encode our keys. For example, if one of them is <code>123456789</code> (please, do not use a key like this :D) then its hash will be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225
</span></span></code></pre></div><p>Now you can add this hash to your deployment script, Kubernetes Secret, etc., and commit it with peace of mind.</p>
<p>Next, we need to handle this new format on our middleware. This is what the code will look like now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ApiKeyMiddleware</span><span class="p">(</span><span class="nx">cfg</span> <span class="nx">conf</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">logger</span> <span class="nx">logging</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">handler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apiKeyHeader</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeyHeader</span> <span class="c1">// string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">apiKeys</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeys</span> <span class="c1">// map[string]string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="nx">reverseKeyIndex</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">key</span> <span class="o">:=</span> <span class="nx">apiKeys</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">reverseKeyIndex</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="p">=</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">next</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">apiKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">bearerToken</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="nx">apiKeyHeader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;request failed API key authentication&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid API key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nf">apiKeyIsValid</span><span class="p">(</span><span class="nx">apiKey</span><span class="p">,</span> <span class="nx">reverseKeyIndex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">hostIP</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">					<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;failed to parse remote address&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">					<span class="nx">hostIP</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span>
</span></span><span class="line"><span class="cl">				<span class="p">}</span>
</span></span><span class="line"><span class="cl">				<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;no matching API key found&#34;</span><span class="p">,</span> <span class="s">&#34;remoteIP&#34;</span><span class="p">,</span> <span class="nx">hostIP</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">				<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid api key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// apiKeyIsValid checks if the given API key is valid and returns the principal if it is.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">apiKeyIsValid</span><span class="p">(</span><span class="nx">rawKey</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">availableKeys</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">hash</span> <span class="o">:=</span> <span class="nx">sha256</span><span class="p">.</span><span class="nf">Sum256</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">rawKey</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="nx">key</span> <span class="o">:=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">hash</span><span class="p">[:])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">name</span><span class="p">,</span> <span class="nx">found</span> <span class="o">:=</span> <span class="nx">reverseKeyIndex</span><span class="p">[</span><span class="nx">apiKey</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">found</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// bearerToken function omitted..
</span></span></span></code></pre></div><p>Here we extracted the logic to validate the key into a function that, before checking the equality of the user key against the available ones, encodes the user key using the same SHA256 algorithm.</p>
<p>This simple step improved a lot our security posture without adding much complexity. Now we can have the benefits of version control, like change history and easy detection when someone changes a key hash.</p>
<p>This approach works well when there are few keys to be managed, and you want to follow a GitOps approach. However, if you need to scale the key management, allow for self-service key requests and automatic rotation, you may want to look for a solution like <a href="https://www.vaultproject.io">Hashicorp Vault</a>. Even using an external secret store I still believe this strategy, to rely on key hashes to be valid, because your external secret store can persist both the original key and the hash, and the access policy for the application can have fewer privileges in such a way that it can only read the hashes.</p>
<h3 id="constant-time-key-verification">Constant time key verification</h3>
<p>Once we have a better strategy to provision our keys, we need to defend ourselves against them being exfiltrated by timing attacks. The solution for this kind of vulnerability is to use an algorithm that takes the same time to produce a result whether the keys are equal or not. This is called a constant time comparison, and the Go Standard Library offers us an implementation in the <code>crypto/subtle</code> package that is perfect to solve most of our problems. Hence, we can update our code to use this package:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ApiKeyMiddleware</span><span class="p">(</span><span class="nx">cfg</span> <span class="nx">conf</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">logger</span> <span class="nx">logging</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">handler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apiKeyHeader</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeyHeader</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apiKeys</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeys</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apiKeyMaxLen</span> <span class="o">:=</span> <span class="nx">cfg</span><span class="p">.</span><span class="nx">APIKeyMaxLen</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">decodedAPIKeys</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">][]</span><span class="kt">byte</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">value</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">apiKeys</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">decodedKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hex</span><span class="p">.</span><span class="nf">DecodeString</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nx">decodedAPIKeys</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="p">=</span> <span class="nx">decodedKey</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">next</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">ctx</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">Context</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">apiKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">bearerToken</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="nx">apiKeyHeader</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;request failed API key authentication&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid API key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nf">apiKeyIsValid</span><span class="p">(</span><span class="nx">apiKey</span><span class="p">,</span> <span class="nx">decodedAPIKeys</span><span class="p">);</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				 <span class="nx">hostIP</span><span class="p">,</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">net</span><span class="p">.</span><span class="nf">SplitHostPort</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">					<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">						<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;failed to parse remote address&#34;</span><span class="p">,</span> <span class="s">&#34;error&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">						<span class="nx">hostIP</span> <span class="p">=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">RemoteAddr</span>
</span></span><span class="line"><span class="cl">					<span class="p">}</span>
</span></span><span class="line"><span class="cl">					<span class="nx">logger</span><span class="p">.</span><span class="nf">Errorw</span><span class="p">(</span><span class="s">&#34;no matching API key found&#34;</span><span class="p">,</span> <span class="s">&#34;remoteIP&#34;</span><span class="p">,</span> <span class="nx">hostIP</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">					<span class="nf">RespondError</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span> <span class="s">&#34;invalid api key&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">					<span class="k">return</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">.</span><span class="nf">WithContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">		<span class="p">})</span>
</span></span><span class="line"><span class="cl">	<span class="p">},</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// apiKeyIsValid checks if the given API key is valid and returns the principal if it is.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">apiKeyIsValid</span><span class="p">(</span><span class="nx">rawKey</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">availableKeys</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">hash</span> <span class="o">:=</span> <span class="nx">sha256</span><span class="p">.</span><span class="nf">Sum256</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">rawKey</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="nx">key</span> <span class="o">:=</span> <span class="nx">hash</span><span class="p">[:]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">value</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">availableKeys</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">contentEqual</span> <span class="o">:=</span> <span class="nx">subtle</span><span class="p">.</span><span class="nf">ConstantTimeCompare</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">key</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">contentEqual</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="nx">name</span><span class="p">,</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// bearerToken function omitted...
</span></span></span></code></pre></div><p>Now, the function <code>apiKeyIsValid</code> uses <code>subtle.ConstantTimeCompare</code> to verify the user key against each available key. Since <code>subtle.ConstantTimeCompare</code> operates upon byte slices we don&rsquo;t cast our hash to string anymore and also our reversed index has gone in place of a decoded map.</p>
<p>The decoding is necessary because the string representation of our key hashes are actually a hexadecimal encoding of the binary value. Hence, we cannot just cast the string to byte slice because Go assumes all strings to be UTF-8 encoded.</p>
<blockquote>
<p>Note: for an example on how using a cast instead of the correct decoding function, the result of <code>[]byte(&quot;09&quot;)</code> is <code>110000111001</code> while <code>hex.DecodeString(&quot;09&quot;)</code> produces <code>1001</code>. Check out the live example <a href="https://go.dev/play/p/CPy16o7hvDO">here</a>.</p>
</blockquote>
<p>The major disadvantage of this solution is that now we need to iterate over all available keys before finding out if the key is incorrect. This doesn&rsquo;t scale well if there are too many keys, however one simple workaround would be to require the client to send an extra header with the key ID/name, e.g. <code>X-App-Key-ID</code>, with which you can find the key in <code>O(1)</code> and then apply the constant time comparison.</p>
<p>However, there is one subtle (<em>pun intended</em>) behavior from <code>subtle.ConstantTimeCompare</code> that we must be aware before deploying our solution to production. When the byte slices have different lengths, the functions returns earlier without performing the bitwise operations. This is natural because it does an XOR between each pair of bits from each slice, and with slices of different sizes, there would be bits from one slice without a matching pair to be combined with. <strong>Because of it, an adversary could measure that keys with the wrong length have a smaller response time than keys with the correct length, hence leaking the key length</strong>. It would only be a vulnerability if you use a short key that is easily brute-forced, but with a simple 30 character key using the UTF-8 printable characters you would have <code>30^95 = 2.12089515 × 10^140</code> possible keys.</p>
<p>Finally, we&rsquo;ve built a simple, secure and efficient API Key solution that should handle a lot of uses cases without additional infrastructure or complexity. Using a basic understanding of threats and the Golang standard library, we could do a security-oriented design instead of leaving security as an after-though in an iterative way.</p>
<hr>
<p>Photo by <a href="https://unsplash.com/@silas_crioco?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Silas Köhler</a> on <a href="https://unsplash.com/s/photos/key?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Kubernetes dynamic client</title>
      <link>https://caioferreira.dev/posts/the-kubernetes-dynamic-client/</link>
      <pubDate>Fri, 28 May 2021 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/the-kubernetes-dynamic-client/</guid>
      <description>A dive into a hidden tool to build Controllers and Operators</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="./cover.png" alt=""  />
</p>
<h2 id="introduction">Introduction</h2>
<p>Kubernetes won the battle for the cloud-native platform and the characteristic that makes me enjoy the most working with it is its extensibility. By providing an open model through the <code>kube-apiserver</code>, without splitting an internal and external interface, we can interact with the cluster and any other system to integrate both from the same application (Controller) and even use custom resources to describe our unique operations, know as the <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/">Operator Pattern</a>.</p>
<p>Although one could use any HTTP Client to interact with the API Server, this is no simple task. There are many resources with different response structures and possible operations if we only consider the core resources on Kubernetes. Hence, Kubernetes itself provides a set of clients for easier integration through the <a href="https://github.com/kubernetes/client-go"><code>k8s.io/client-go</code> project</a>.</p>
<p>The most used client provided by this project is the <code>k8s.io/client-go/kubernets.ClientSet</code>, which is a typed client. What that means is that this interface provides exclusive methods for each resource on Kubernetes (think of Pods, Deployments, Services, everything!) and operation (Create, Get, List, Watch, Update, Patch and Delete). It is obvious why you should, whenever possible, prefer to use this client.</p>
<p>However, there are situations where this can be limiting. It is when <code>k8s.io/client-go/dynamic.Interface</code>, the dynamic client, will enter the game. This client has a twofold purpose:</p>
<p>First, it allows working with custom resources while avoiding strong dependencies. If you want to build some automation or workflow using another Operator as the building block, like ExternalDNS, CertManager, or Prometheus (Operator) usually you would need to add these projects as dependencies to use their Go types and register them on your client instance. This obviously introduces a lot of burdens as you now need to manage their ever-evolving versions and try to keep the version you have installed on the cluster matching the version on your <code>go.mod</code>.</p>
<p>Secondly, you can work with multiple or unknown resources. When your operator implements a generic logic that can interact with any common Kubernetes resource (from RBAC to Pods) and even custom resources, the dynamic client may be your only solution. A few examples are the garbage collection controller relies heavily on it and if you would want to add support for an arbitrary custom resource on a project like <a href="https://github.com/bitnami-labs/kubewatch">KubeWatch</a>.</p>
<p>Therefore, let&rsquo;s dive into this resourceful (<em>pun intended</em>) component of the <a href="http://k8s.io/client-go"><code>k8s.io/client-go</code></a> project and see how we can leverage it.</p>
<h2 id="basic-operations-with-the-dynamic-client">Basic operations with the dynamic client</h2>
<blockquote>
<p>The code below assumes to be running inside a Kubernetes cluster.</p>
</blockquote>
<p>Many operations with the dynamic client is similar to the typed client, like creating a new instance can be done by providing the config to its constructor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">newClient</span><span class="p">()</span> <span class="p">(</span><span class="nx">dynamic</span><span class="p">.</span><span class="nx">Interface</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">config</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">rest</span><span class="p">.</span><span class="nf">InClusterConfig</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">dynClient</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">dynamic</span><span class="p">.</span><span class="nf">NewForConfig</span><span class="p">(</span><span class="nx">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">dynClient</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Since the dynamic client has no knowledge about the resource you want to consume, it does not provide helper methods like <code>CoreV1().Pod</code> . Instead, you need to first provide a <code>schema.GroupVersionResource</code>, which is a Golang type that provides the necessary information to construct an HTTP request to the cluster API Server.</p>
<p>For example, if you want a function to list all MongoDB resources from the MongoDB Community Operator:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">monboDBResource</span> <span class="p">=</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">GroupVersionResource</span><span class="p">{</span><span class="nx">Group</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity.mongodb.com&#34;</span><span class="p">,</span> <span class="nx">Version</span><span class="p">:</span> <span class="s">&#34;v1&#34;</span><span class="p">,</span> <span class="nx">Resource</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ListMongoDB</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">client</span> <span class="nx">dynamic</span><span class="p">.</span><span class="nx">Interface</span><span class="p">,</span> <span class="nx">namespace</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="nx">unstructured</span><span class="p">.</span><span class="nx">Unstructured</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>  <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// GET /apis/mongodbcommunity.mongodb.com/v1/namespaces/{namespace}/mongodbcommunity/
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">list</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Resource</span><span class="p">(</span><span class="nx">monboDBResource</span><span class="p">).</span><span class="nf">Namespace</span><span class="p">(</span><span class="nx">namespace</span><span class="p">).</span><span class="nf">List</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">metav1</span><span class="p">.</span><span class="nx">ListOptions</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">Items</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Note that if you are dealing with a namespaced resource then <code>.Namespace(namespace)</code> is obligatory, even if you will use an empty string to list on all namespaces.</p>
<p>In this snippet, we can see the main companion of the dynamic client: <code>unstructured.Unstructured</code>. This is a special type that encapsulates an arbitrary JSON while also complying with standard Kubernetes interfaces like <code>runtime.Object</code> , but most importantly it provides a set of helpers on the <code>unstructured</code> package to manipulate this data.</p>
<p>Expanding our example, if we would scale a MongoDB by an proportion we could do so like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// ScaleMongoDB changes the number of members by the given proportion,
</span></span></span><span class="line"><span class="cl"><span class="c1">// which should be 0 =&lt; proportion &lt; 1.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">ScaleMongoDB</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">client</span> <span class="nx">dynamic</span><span class="p">.</span><span class="nx">Interface</span><span class="p">,</span> <span class="nx">name</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">namespace</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">proportion</span> <span class="kt">uint</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">proportion</span> <span class="p">&gt;</span> <span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;proportion should be between 0 =&lt; proportion &lt; 1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">mongoDBClient</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Resource</span><span class="p">(</span><span class="nx">monboDBResource</span><span class="p">).</span><span class="nf">Namespace</span><span class="p">(</span><span class="nx">namespace</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">mdb</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">mongoDBClient</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">metav1</span><span class="p">.</span><span class="nx">GetOptions</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">members</span><span class="p">,</span> <span class="nx">found</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">unstructured</span><span class="p">.</span><span class="nf">NestedInt64</span><span class="p">(</span><span class="nx">mdb</span><span class="p">.</span><span class="nf">UnstructuredContent</span><span class="p">(),</span> <span class="s">&#34;spec&#34;</span><span class="p">,</span> <span class="s">&#34;members&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">found</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;members field not found on MongoDB spec&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">scaled</span> <span class="o">:=</span> <span class="nb">int</span><span class="p">(</span><span class="nx">members</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="nx">proportion</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">patch</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">		<span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;op&#34;</span><span class="p">:</span>    <span class="s">&#34;replace&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;path&#34;</span><span class="p">:</span>  <span class="s">&#34;/spec/members&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;value&#34;</span><span class="p">:</span> <span class="nx">scaled</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="p">},</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">payload</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">patch</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">mongoDBClient</span><span class="p">.</span><span class="nf">Patch</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">types</span><span class="p">.</span><span class="nx">JSONPatchType</span><span class="p">,</span> <span class="nx">payload</span><span class="p">,</span> <span class="nx">metav1</span><span class="p">.</span><span class="nx">PatchOptions</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here we leverage <code>unstructured.NestedInt64</code> to access only the field that we are interested in, keeping our coupling to the MongoDB CRD to a minimum while also being able to manipulate the resource data with type safety.</p>
<p>The <code>unstructured</code> package has lots of helpers like this, not only for reading but also for writing to any field on the resource.</p>
<p>Performing all the usual operations on Kubernetes (get, list, watch, create, patch, and delete) follow the same approach: provide the <code>scheme.GroupVersionResource</code> and handle the <code>unstructured.Unstructured</code> result.</p>
<h2 id="controller-with-a-dynamic-client">Controller with a dynamic client</h2>
<p>More advanced but frequent use of a Kubernetes client is to build a controller that reacts to changes on the actual cluster state to bring it to the desired state.</p>
<p>Usually, we leverage an Informer, a component provided by <code>k8s.io/client-go</code>, that runs a handler when changes are detected, created from a typed client. Luckily the <code>dynamic</code> package also provides an Informer component that we can use.</p>
<p>For example, if we want to capture when a MongoDB is deleted to clean the associated <code>PersistentVolumeClaims</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">utilruntime</span> <span class="s">&#34;k8s.io/apimachinery/pkg/util/runtime&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/apimachinery/pkg/util/wait&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/client-go/dynamic&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/client-go/dynamic/dynamicinformer&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/client-go/tools/cache&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/client-go/util/workqueue&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;time&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">maxRetries</span> <span class="p">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">monboDBResource</span> <span class="p">=</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">GroupVersionResource</span><span class="p">{</span><span class="nx">Group</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity.mongodb.com&#34;</span><span class="p">,</span> <span class="nx">Version</span><span class="p">:</span> <span class="s">&#34;v1&#34;</span><span class="p">,</span> <span class="nx">Resource</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">MongoDBController</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">informer</span> <span class="nx">cache</span><span class="p">.</span><span class="nx">SharedIndexInformer</span>
</span></span><span class="line"><span class="cl">	<span class="nx">stopper</span>  <span class="kd">chan</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">queue</span>    <span class="nx">workqueue</span><span class="p">.</span><span class="nx">RateLimitingInterface</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewMongoDBController</span><span class="p">(</span><span class="nx">client</span> <span class="nx">dynamic</span><span class="p">.</span><span class="nx">Interface</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">MongoDBController</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">dynInformer</span> <span class="o">:=</span> <span class="nx">dynamicinformer</span><span class="p">.</span><span class="nf">NewDynamicSharedInformerFactory</span><span class="p">(</span><span class="nx">client</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">informer</span> <span class="o">:=</span> <span class="nx">dynInformer</span><span class="p">.</span><span class="nf">ForResource</span><span class="p">(</span><span class="nx">monboDBResource</span><span class="p">).</span><span class="nf">Informer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">stopper</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">queue</span> <span class="o">:=</span> <span class="nx">workqueue</span><span class="p">.</span><span class="nf">NewRateLimitingQueue</span><span class="p">(</span><span class="nx">workqueue</span><span class="p">.</span><span class="nf">DefaultControllerRateLimiter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="nx">informer</span><span class="p">.</span><span class="nf">AddEventHandler</span><span class="p">(</span><span class="nx">cache</span><span class="p">.</span><span class="nx">ResourceEventHandlerFuncs</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">DeleteFunc</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">obj</span> <span class="kd">interface</span><span class="p">{})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">key</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cache</span><span class="p">.</span><span class="nf">DeletionHandlingMetaNamespaceKeyFunc</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">queue</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="p">},</span>
</span></span><span class="line"><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">MongoDBController</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">informer</span><span class="p">:</span> <span class="nx">informer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">queue</span><span class="p">:</span> <span class="nx">queue</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">stopper</span><span class="p">:</span> <span class="nx">stopper</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="p">},</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">MongoDBController</span><span class="p">)</span> <span class="nf">Stop</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nb">close</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">stopper</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">MongoDBController</span><span class="p">)</span> <span class="nf">Run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">defer</span> <span class="nx">utilruntime</span><span class="p">.</span><span class="nf">HandleCrash</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">defer</span> <span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">ShutDown</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">go</span> <span class="nx">m</span><span class="p">.</span><span class="nx">informer</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">stopper</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// wait for the caches to synchronize before starting the worker
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">if</span> <span class="p">!</span><span class="nx">cache</span><span class="p">.</span><span class="nf">WaitForCacheSync</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">stopper</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">informer</span><span class="p">.</span><span class="nx">HasSynced</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">utilruntime</span><span class="p">.</span><span class="nf">HandleError</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;timed out waiting for caches to sync&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// runWorker will loop until some problem happens. The wait.Until will then restart the worker after one second
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">wait</span><span class="p">.</span><span class="nf">Until</span><span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">runWorker</span><span class="p">,</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="nx">m</span><span class="p">.</span><span class="nx">stopper</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">MongoDBController</span><span class="p">)</span> <span class="nf">runWorker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">key</span><span class="p">,</span> <span class="nx">quit</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">Get</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">quit</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nx">err</span> <span class="o">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nf">processItem</span><span class="p">(</span><span class="nx">key</span><span class="p">.(</span><span class="kt">string</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">Forget</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">NumRequeues</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">&lt;</span> <span class="nx">maxRetries</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">AddRateLimited</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">Forget</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">utilruntime</span><span class="p">.</span><span class="nf">HandleError</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nx">m</span><span class="p">.</span><span class="nx">queue</span><span class="p">.</span><span class="nf">Done</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">MongoDBController</span><span class="p">)</span> <span class="nf">processItem</span><span class="p">(</span><span class="nx">mongodb</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// Clean up PVCs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Most of this code is standard, like the work queue, informer event handlers, and item processing, of a controller using the typed client.</p>
<p>Hence, leveraging the decoupling provided by the dynamic client really comes with a low overhead in terms of complexity.</p>
<h2 id="testing-with-the-dynamic-client">Testing with the dynamic client</h2>
<p>If we were to scale the use of the dynamic client it is paramount that it is as easy to test as the typed client.</p>
<p>As in the controller case, the <code>dynamic</code> package provides an equivalent fake client that allows for stubbing objects and asserting actions performed using it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;context&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;k8s.io/apimachinery/pkg/runtime&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">dynamicfake</span> <span class="s">&#34;k8s.io/client-go/dynamic/fake&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestDynamicClient</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// Setup an Object as mock on the client
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// Write it like its YAML manifest
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">mdb</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">unstructured</span><span class="p">.</span><span class="nx">Unstructured</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">mdb</span><span class="p">.</span><span class="nf">SetUnstructuredContent</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;apiVersion&#34;</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity.mongodb.com/v1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;kind&#34;</span><span class="p">:</span> <span class="s">&#34;MongoDBCommunity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;metadata&#34;</span><span class="p">:</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;name&#34;</span><span class="p">:</span>      <span class="s">&#34;mongodb-test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;namespace&#34;</span><span class="p">:</span> <span class="s">&#34;default&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="p">},</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;spec&#34;</span><span class="p">:</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;members&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="p">},</span>
</span></span><span class="line"><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">dynamicClient</span> <span class="o">:=</span> <span class="nx">dynamicfake</span><span class="p">.</span><span class="nf">NewSimpleDynamicClient</span><span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nf">NewScheme</span><span class="p">(),</span> <span class="nx">mdb</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Run any logic that depend on the dynamic client
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nf">NotifyMongoDBs</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">dynamicClient</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">AssertActions</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span>  <span class="nx">dynamicClient</span><span class="p">.</span><span class="nf">Actions</span><span class="p">(),</span> <span class="p">[]</span><span class="nx">ExpectedAction</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">Verb</span><span class="p">:</span> <span class="s">&#34;list&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="nx">Namespace</span><span class="p">:</span> <span class="s">&#34;default&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="nx">Resource</span><span class="p">:</span> <span class="s">&#34;mongodbcommunity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="p">},</span>
</span></span><span class="line"><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Using the <code>unestructured.Unestructured</code> type we can create stub Kubernetes objects using the same syntax as in YAML, but with maps.</p>
<p>After performing the tested logic we can use <code>dynamicClient.Actions()</code> to see all operations that were performed by our code. However, manually asserting these actions on every test often lead to unreadable code and brittle assertions.</p>
<p>Hence, I often use a special assertion function <code>AssertActions</code> that verify if every expected action can be found in the performed actions. An important note is that this function does not perform an exact list match, i.e. if a delete operation was performed using the client the test would not break, the only condition for the <code>AssertActions</code> to fail is if the list operation provided on the expected list isn&rsquo;t found. One could change the asserting function or make a sibling function that validates only if the expected actions were performed.</p>
<p>Although the current implementation is verbose, this function has the benefit of working both with the dynamic and the typed client.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ExpectedAction</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Verb</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Name</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Namespace</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Resource</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Patch action
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">PatchType</span> <span class="nx">types</span><span class="p">.</span><span class="nx">PatchType</span>
</span></span><span class="line"><span class="cl">	<span class="nx">PatchPayload</span> <span class="p">[]</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AssertActions</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">,</span> <span class="nx">got</span> <span class="p">[]</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">Action</span><span class="p">,</span> <span class="nx">expected</span> <span class="p">[]</span><span class="nx">ExpectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">expected</span><span class="p">)</span> <span class="p">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">got</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">t</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;executed actions too short, expected %d, got %d&#34;</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">expected</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="nx">got</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">expectedAction</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">expected</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">!</span><span class="nf">AssertExpectedAction</span><span class="p">(</span><span class="nx">got</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">t</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;action %d does not match any of the got actions&#34;</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">AssertExpectedAction</span><span class="p">(</span><span class="nx">got</span> <span class="p">[]</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">Action</span><span class="p">,</span> <span class="nx">expectedAction</span> <span class="nx">ExpectedAction</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">gotAction</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">got</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">switch</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Verb</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;get&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">getAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">GetAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">getAction</span><span class="p">.</span><span class="nf">GetName</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Name</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">getAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;list&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">listAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">ListAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">listAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;watch&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">watchAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">WatchAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">watchAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;create&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">createAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">CreateAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">createAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;update&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">updateAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">UpdateAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">updateAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;delete&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">deleteAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">DeleteAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">deleteAction</span><span class="p">.</span><span class="nf">GetName</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Name</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">deleteAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="s">&#34;patch&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="nx">patchAction</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">gotAction</span><span class="p">.(</span><span class="nx">kubetesting</span><span class="p">.</span><span class="nx">PatchAction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">patchAction</span><span class="p">.</span><span class="nf">GetName</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Name</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">patchAction</span><span class="p">,</span> <span class="nx">expectedAction</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">patchAction</span><span class="p">.</span><span class="nf">GetPatchType</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">PatchType</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="nx">patchBytes</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">expectedAction</span><span class="p">.</span><span class="nx">PatchPayload</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="p">!</span><span class="nx">bytes</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">patchAction</span><span class="p">.</span><span class="nf">GetPatch</span><span class="p">(),</span> <span class="nx">patchBytes</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">continue</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">validateNamespaceAndResource</span><span class="p">(</span><span class="nx">action</span> <span class="nx">kubetesting</span><span class="p">.</span><span class="nx">Action</span><span class="p">,</span> <span class="nx">expectedAction</span> <span class="nx">ExpectedAction</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">action</span><span class="p">.</span><span class="nf">GetNamespace</span><span class="p">()</span> <span class="o">==</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Namespace</span> <span class="o">&amp;&amp;</span> <span class="nx">action</span><span class="p">.</span><span class="nf">GetResource</span><span class="p">().</span><span class="nx">Resource</span> <span class="o">==</span> <span class="nx">expectedAction</span><span class="p">.</span><span class="nx">Resource</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This asserting function allows for more conditions to be added, like verifying list/watch restrictions and create/update bodies.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The Kubernetes ecosystem is rich and every now and then we stumble upon this kind of treasure. I strongly recommend reading through the documentation not only of the <a href="http://k8s.io/client-go"><code>k8s.io/client-go</code></a> project but also from the <a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime"><code>sigs.k8s.io/controller-runtime</code></a> project and the <a href="https://kubernetes.io/docs/reference/kubernetes-api/">Kubernetes Reference API</a> documentation.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing Cache in your System</title>
      <link>https://caioferreira.dev/posts/introducing-cache-in-your-system/</link>
      <pubDate>Mon, 04 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/introducing-cache-in-your-system/</guid>
      <description>Before jumping into tutorials, let&amp;#39;s try to understagind how and if a cache fit in your architecture</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="./cover.jpg" alt=""  />

Photo by <a href="https://unsplash.com/@joshstyle?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Joshua Coleman</a> on <a href="https://unsplash.com/s/photos/storage?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p>
<p>Caching is one of the most popular tools used to scale systems and anyone looking to maintain high throughput, resilient and cost-effective products should understand how to use it because it is financially impractical to apply only compute resources in order to meet the access demands.</p>
<p>Knowing the basics about it and what parameters you should be looking when choosing your solution is rarely addressed and hence is the purpose of this article.</p>
<h2 id="dont-rush-tour-decision">Don&rsquo;t rush tour decision</h2>
<p>If one would search for caching it will find a plethora of tutorials teaching how to set up your solution. They make it look so easy to use a cache in your application that one may do it without thinking twice. Be careful, every choice comes with costs and tradeoffs, caching is no different.</p>
<p>Usually, we can find bad cache designs when we stumble on the most important metrics for it: hit ratio and miss ratio. As a brief overview, we can define these metrics as follow:</p>
<p><strong>Hit ratio</strong>: when the cache <strong>has</strong> a key and can provide the value for the system to use, we call it a <em>hit</em>. The metric is simply the <em>number of hits / number of lookups</em>.</p>
<p><strong>Miss ratio</strong>: when the cache <strong>hasn&rsquo;t</strong> the key and the value must be computed for the system to use, we call it a <em>miss</em>. The metric is simply the <em>number of misses / number of lookups</em>.</p>
<p>The <em>number of lookups</em> is simply the total quantity of cache accesses, which is <em>lookups = hits + misses</em>.</p>
<p>You can know that the cache is not being efficient if it has a low hit ratio and, therefore, a high miss ratio. What low or high means will depend on your problem. Discovering your baseline metrics can only be achieved through our first guideline.</p>
<h2 id="guidelines">Guidelines</h2>
<h3 id="monitor-your-cache">Monitor your cache</h3>
<p>You should set up a way to capture the hit and miss ratio of your cache solution. How you will do it is highly dependable on the chosen implementation, but most of it should have an easy way of extracting these statistics and if not, consider looking for others.</p>
<p>The most important about having monitoring is exactly that you will be able to experiment with different algorithms and tradeoffs presented in the below guidelines and strive to improve your ratios. Hence, formulate a hypothesis and let your data drive your solution.</p>
<h3 id="tradeoffs-performance-and-resilience-vs-consistency">Tradeoffs: Performance and Resilience vs Consistency</h3>
<p>Caching is one of the simplest and more powerful ideas in computation. Understanding and applying its basic cases is easy but can become extremely hard sooner than you imagine. Therefore the classical phrase, “there is only two difficult things in computer science: cache invalidation and naming things”. But, why cache invalidation is so hard? Because it usually is critical and has many moving parts.</p>
<p>The moving parts come from the fact that caches improve application performance because they bring the data closer, which also means we now have gone off the rails with the most important principle for Consistency: have one source of truth. This has another effect which is resilience, since now if our main source of information goes off, our application can survive a little longer with its cached values.</p>
<p>The critical segment arises usually when you need to update the data on your cache. In order to give your user meaningful information, you need to understand the access patterns to choose the right eviction algorithm and parameters which will balance performance and correctness, choosing the wrong one will probably damage your product. Besides that, there is the case when you need to force clean your cache and the distributed nature of it can cause a lot of pain in the process of invalidating each cache node.</p>
<p>Therefore, adding a cache to your solution is a trade-off between Performance+Resilience versus Consistency and so the first question you should ask yourself is “Can my system live with potentially old and invalid data?” If your answer is Yes, then you can continue here, otherwise, caching will do you more harm than good.</p>
<h3 id="keyspace">Keyspace</h3>
<p>So you decided that you really want a cache. The first decision you will have to make is about your keyspace, i.e. what you will use as a cache key to index your costly computed values?</p>
<p>It is important because you need to analyze your key cardinality, which means how many distinct values your key can have. For example, a boolean key has a cardinality of 2 (true or false) whereas a customer id can have thousands of possible values. This is really important to understand because a low cardinality key would limit the amount of data your cache could store but your hit ratio would be really high. On the other side, if a key has an extremely high cardinality (tending to uniqueness, never repeating itself) your cache could grow exponentially and you may end up with a low hit ratio, demanding much computation and providing little performance improvements.</p>
<p>Hence you want to choose a high cardinality key, avoiding never-repeating ones, but not to small, avoiding limited ones. One way many uses to achieve this balance is to use complex keys (like a map or list), mixing a medium cardinality key like customer id with a low cardinality one like state names.</p>
<p>One scenario that you should be careful is with memoization. For those coming from OO lands, it is a technique to cache the values computed by a function. It uses the function&rsquo;s arguments as keys and caches the result. But, since a function may change over time and you may use a solution that wraps the function in a place far from the local here it is implemented, there is a great risk that some feature or refactoring adds unique arguments (like a timestamp) or reduce the arguments to low cardinality ones (a small enum and a boolean). You should implement memoization near to the implementation and/or use cache solutions that allow you to choose the keys from the argument list, which is the best solution since one could not accidentally chance that.</p>
<h3 id="cache-algorithms-and-strategies">Cache algorithms and strategies</h3>
<p>Next, you need to understand your access pattern in order to choose your cache algorithm and possible strategies. Each one will have trade-offs, you can combine some of them and you should experiment because in this area data will be better to guide you.</p>
<ul>
<li><strong>FIFO (First in First out)</strong>: this is the simplest algorithm where the cache works like a queue and evict the first block to enter, independent of how many times it was used. Through time, you will have the most used keys remaining in the cache, since even if a block was evicted, since it is highly used, duplicated blocks of this key will be presented at the queue. This strategy is really simple to implement and has low overhead but also has an inefficient usage of memory in comparison to other algorithms.</li>
<li><strong>LRU (Least Recently Used)</strong>: probably the most used cache algorithm, it tracks when some block was used and evict the one with fewer accesses. Hence it keeps the most used keys in the cache but with better memory usage. As a tradeoff, it has a more complex and costly implementation since it has to add and track age bits in the cache blocks.</li>
<li><strong>LFU (Least Frequently Used)</strong>: imagine that you are using an LRU cache and you have 100 accesses in the last second. There were 80 hits in the key A, 19 hits in key B and 1 hit in key C. If your cache is full, in the next miss that needs to load a new key, your cache would not evict C, because it was the most recent one accessed. This can be really bad since we are probably removing a more usage key (A or B) in favor of C. That is the problem that the LFU algorithm addresses since it keep the most frequent usage keys, it would evict the key C from our example because it has a small access frequency. This is a really interesting model and probably is better suited to most use cases, but as a pattern, it also has more overhead than LRU because how it has to keep track of how many times a block was accessed in relation to how many accesses happened to the cache.</li>
<li><strong>TTL (Time to Live)</strong>: one really common strategy is time to live, an algorithm that evicts blocks that are older than a certain pre-defined timespan. It is used for more volatile data and usually with two cases: for low cardinality keys that can’t grow the memory footprint to the point where one block would be evicted or in combination with other cache strategies (like the ones mentioned above) to provide more refresh opportunities.</li>
<li><strong>Stale data</strong>: we say that a block is stale when it passes its expiration time and should be evicted or refreshed. The stale data strategy is an augmentation of TTL that instead of eliminating the block from the cache once the time to live expires, it runs a refresh function (in case of memoization it reruns the function) that will compute a new value for the key. During this time, the stale (old) data is served for those that access the cache. It can also be the case where if the refresh function fails, it simply maintains the block in the cache, that may be evicted by some algorithm, but is not eliminated by its expiration. This can be a really powerful strategy for increased resilience if your system support living with a possibly long-living stale data.</li>
</ul>
<p>Besides these there is also more modern algorithms like Windowed TinyLFU (used by <a href="https://github.com/ben-manes/caffeine">Caffeine</a>), <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Low_inter-reference_recency_set_(LIRS)">LIRS</a> and <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Adaptive_replacement_cache_(ARC)">ARC</a>. Note that various discussions about the cache algorithm will reference the theoretical <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#B%C3%A9l%C3%A1dy's_algorithm">Bélady’s algorithm</a>, so it is good to have a look at it.</p>
<h3 id="local-vs-distributed">Local vs Distributed</h3>
<p>One last question you might need to answer is if you are going to use a local or distributed cache. This will be the most important question in financial terms, so look close to your needs.</p>
<p><strong>Local</strong>: it means that you will maintain the data in the memory of an application instance. This is the most simple setup, but it can impose a burden in the allocated RAM, maybe forcing you to upgrade to a bigger instance, which can be pretty expensive. It also can have suboptimal performance-wise, since the worst-case scenario for a cache with a size limit of 1000 keys is to have just it stored across all instances, that is, the same data is replicated in all local caches. This intersection diminishes the total performance gain across your system.</p>
<p><strong>Distributed</strong>: in this case one would use a solution like Redis or Memcache, running separately from the application, and being shared by all instances. This solution optimizes the resource usage (the RAM is allocated exclusively for the cache), allow for bigger cache entries and improves data density (there is, no more replicated entries), but even then it can be less performant than the local solution since it demands a network roundtrip to access the database. Besides that, the cost to maintain new instances and the skill needed to operate these should not be overlooked.</p>
<p>All these points should be taken into consideration when choosing the design for your cache in your system.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Therefore, before looking into tutorials and getting started articles about how to set up your cache, think about and discuss your problem and how you could configure the cache to deliver the most value to your system. You can use these and other guidelines as starting points for the debates and focus on guiding your decisions based on the data measured from your implementation.</p>
<p>I hope you found it useful and you can find more of my thoughts at <a href="http://caioferreira.dev/">caioferreira.dev</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Lambda Path</title>
      <link>https://caioferreira.dev/posts/the-lambda-path/</link>
      <pubDate>Tue, 08 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/the-lambda-path/</guid>
      <description>A collection of resources to help you out enter the world of Functional Programming</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="./cover.jpg" alt=""  />

Photo by <a href="https://unsplash.com/@roman_lazygeek">Roman Mager</a> on <a href="https://unsplash.com/photos/5mZ_M06Fc9g">Unsplash</a></p>
<h1 id="introduction">Introduction</h1>
<p>Being passionate by functional programming I am often asked about how and where one can learn more about this style. Hence, I decided to compile some resources on the subject to suggest a path to this beautiful world.</p>
<p>I split the content into three levels: Beginner, Intermediate and Advanced. Since this is thought of as a starter to master path, I gave preference to contents in Javascript, which is a well-known language that allows for a great extent of functional patterns.</p>
<p>Some purists probably will hate me for this choice.</p>
<p>In this path I tried to add resources on fundamental functional concepts and tools, then an introduction to some functional patterns like Combinators, Category Theory, Railroad Programming and finally a material to deepening into complex subjects.</p>
<p>After all, I also added some tips on dedicated functional languages that I found easy to play with or that I simply love to work with.</p>
<h1 id="the-yellow-bricks-road">The Yellow Bricks Road</h1>
<h2 id="beginner">Beginner</h2>
<p><a href="https://www.freecodecamp.org/news/functional-programming-principles-in-javascript-1b8fc6c3563f/">Functional Programming Principles</a> - Article about basic principles of the functional paradigm, like pure functions and immutability</p>
<p><a href="https://www.youtube.com/playlist?list=PL0zVEGEvSaeEd9hlmCXrk5yUyqUag-n84">Functional programming in Javascript</a> - <strong>Highly recommended.</strong> Video series from the renowned Youtube channel FunFunFunction about functional tools and concepts in Javascript. The explanation is deep and clear about the subjects.</p>
<p><a href="http://eloquentjavascript.net/1st_edition/chapter6.html">Eloquent Javascript: Functional Programming</a> - The Eloquent Javascript&rsquo;s chapter about functional programming. It is a hands on to building fundamental tools by hand, understanding its applications and usages.</p>
<p><a href="http://shop.oreilly.com/product/0636920030331.do">Becoming Functional</a> - An excellent book about the functional paradigm as a whole, presenting topics from functional principles like pure functions to advanced techniques like pattern matching. Unfortunately, the example is in Java.</p>
<h2 id="intermediate">Intermediate</h2>
<p><a href="https://www.youtube.com/watch?v=E8I19uA-wGY&ab_channel=IvanPlyusnin">Functional Design Patterns</a> - An amazing talk about how to combine different pieces of pure functions into more complex applications.</p>
<p><a href="https://vimeo.com/153097877">Javascript Combinators</a> - a talk from one of the greatest names in Functional Javascript, Reginald “Raganwald” Braithwaite, where he explores the concept of composition and introduces the Combinators pattern, a powerful functional tool inspired by Lambda Calculus.</p>
<p><a href="https://mostly-adequate.gitbooks.io/mostly-adequate-guide/">Professor Frisby Mostly Adequate Guide to Functional Programming</a> - An excellent resource on more complex functional patterns using Category Theory. This approach is less common on Front End and probably we can get most of the benefits of the functional paradigm without the complexity of an entire branch of mathematics.</p>
<h2 id="advanced">Advanced</h2>
<p><a href="https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/">Category Theory for Programmers</a> - The most recognized and advanced resource on Category Theory for development ends.</p>
<p><a href="https://raganwald.com/2018/08/30/to-grok-a-mockingbird.html">To Grok a Mockingbird</a> &amp; <a href="http://raganwald.com/2018/09/10/why-y.html">Why Y? Deriving the Y Combinator in JavaScript</a> - two incredible articles from Reginald Braithwaite about the application of the Combinators pattern.</p>
<h1 id="going-full-functional">Going full functional</h1>
<p>If you enjoyed your travel and want to keep going into this path, I recommend that you play with some fully functional programming language. I suggest Elm, ReasonML, and Clojure.</p>
<p>The first two (Elm and ReasonML) are functional languages that compile to JavaScript and are used to build front end applications. A special note to ReasonML which has great integration with React, being a language developed by Facebook.</p>
<p>The last one is Clojure, a JVM language, dynamically typed and from the LISP family. It is used primarily on the back-end and is well suitable for describing complex domain since it&rsquo;s syntax is very flexible and can be customized in a way to better describe your problem space.</p>
<p>You can find more about these languages here:</p>
<ul>
<li>Elm: <a href="https://elm-lang.org/">Documentation</a> and <a href="https://github.com/rtfeldman/elm-spa-example">Code Example</a></li>
<li>ReasonML: <a href="https://reasonml.github.io/">Documentation</a> and <a href="https://github.com/jihchi/reason-react-realworld-example-app">Code Example</a></li>
<li>Clojure: <a href="https://www.braveclojure.com/clojure-for-the-brave-and-true/">Best Book for new Clojurists</a> and <a href="https://github.com/furkan3ayraktar/clojure-polylith-realworld-example-app">Code Example</a></li>
</ul>
<h1 id="summary">Summary</h1>
<p>The Lambda World is a vast and beautiful one, you will find many ways of thinking and solving problems. That is the main reason why I love FP, it not only brings new tools to your workbench but also constantly shifts your mental models. In this segment I could not leave the most impactful talk on my perspective on the work and profession on software engineering: <a href="https://www.infoq.com/presentations/Simple-Made-Easy/">Simple Made Easy</a> by Rich Hickey, the creator of Clojure.</p>
<p>I hope these tips help you and fair travel!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Using Technical Debt as your next Tool</title>
      <link>https://caioferreira.dev/posts/technical-debt/technical-debt-tool/</link>
      <pubDate>Sun, 31 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/technical-debt/technical-debt-tool/</guid>
      <description>Quick Summary In this post I will show you what is the debt that we collect with during the software lifecycle, what are its causes and how to pay it back.
Introduction Often we are faced with a dilemma in software development: implement the best solution for the feature or delivery it quickly but assuming some workarounds and code smells? Whichever side you choose, will be a cost.
This cost is even greater if you work on legacy projects or high changing environments.</description>
      <content:encoded><![CDATA[<h1 id="quick-summary">Quick Summary</h1>
<p>In this post I will show you what is the debt that we collect with during the software lifecycle, what are its causes and how to pay it back.</p>
<h1 id="introduction">Introduction</h1>
<p>Often we are faced with a dilemma in software development: implement the best solution for the feature or delivery it quickly but assuming some workarounds and code smells? Whichever side you choose, will be a cost.</p>
<p>This cost is even greater if you work on legacy projects or high changing environments. I am a software engineer @ B2W, the biggest e-commerce on Latin America, and because of its long history, we deal with +6 years legacy applications, in which dozens of developers worked through this time. Sum up the e-commerce environment with its high competitiveness that drives constant changes and new features then you will have a millionaire decision at your hands.</p>
<p>Hence, understating how to manage this cost can be a key asset to achieve a product that has both high quality and a short time to market.</p>
<h1 id="the-decision-cost">The decision cost</h1>
<p>To understand how to manage the decision cost, we must first learn what cost is. Every decision we make in a software project, not just about best solution vs quick &amp; dirty, incurs in a time cost. Sometimes we cannot take this time and need to target production rather earlier than later. In this situation, we exchange bad code for time. Apply this behavior through time and we get a large debt.</p>
<p>Therefore, <strong>Technical Debt is a metaphor in software development, based on the financial debt, about the accumulation of low-quality code in a project over time</strong>.</p>
<p>Extending the metaphor, we can see financial debt as a resource uptake from some institution, therefore, in the software scope, the resource we borrow is time instead of money and the institution that provides us with this is our code base rather than a bank. Thus, in technical debt calculation, the main amount is the time cost to refactor the codebase to a clean design and the interest represents the extra time that will be taken in the future if the team has to work on the messy code.</p>
<h1 id="even-more-debt">Even more debt</h1>
<p>Although the best solution vs quick &amp; dirty decision is an excellent example to introduce technical debt, it isn&rsquo;t its only source. As Fowler (2009) <a href="https://martinfowler.com/bliki/TechnicalDebtQuadrant.html">points out</a>, this case is just a Prudent and Deliberate debt, where the team is aware of the cost it is incurring and know how to pay it back. But we can have four types of debt in total.</p>
<p><img loading="lazy" src="./tech-debt-quadrant.png" alt=""  />
</p>
<p>Each debt type has a source associated with it:</p>
<ul>
<li>Prudent &amp; Deliberate - the team know how to build good software and has the opportunity to deliver more product value if they speed up. They analyze the tradeoff and judge that cost/earn ratio is enough to justify the bad code, otherwise delaying the solution. Then, they plan how to refactor the debt and implement it as early as possible.</li>
<li>Reckless &amp; Deliberate - the team has the skill to build a well-designed solution but do not has the tools or support to do so. This happens when a team is purposely taking debt but without a plan to repay it or to do so in a code that probably will never change. This scenario is more common on unmotivated teams, legacy projects and due to management pressure for fast delivery instead of a constant and quality delivery.</li>
<li>Reckless &amp; Inadvertent - the team doesn&rsquo;t know good design practices and incur on costs due to lack of training, experience, and leadership.</li>
<li>Prudent &amp; Inadvertent - it is a common case that after we delivery a feature or project we realize that the best design approach would be other than what we did. This situation is unpredictable and can happen to any team due to lack of domain knowledge, obscure requirements, and technology limitations.</li>
</ul>
<p>Of course, this exploration does not exhaust the possible causes for technical debt but includes the most frequent ones.</p>
<h1 id="living-on-high-debt">Living on high debt</h1>
<p>Since the debt causes can be so many, projects can experience an ever-increasing technical debt, to the point where adding new functionality, fix current bugs and operate the application becomes impossible.</p>
<p><img loading="lazy" src="./failure-rate.png" alt=""  />
</p>
<p>This type of scenario can be catastrophic, because while the team lives with this application they will lose agility, will observe an increasing bug count, have loss of motivation, increased stress, long production problems (long living bugs), customer complains and possible single points of failure in case only a few people know how to develop and operate the project.</p>
<p>When an application reaches this point we say that it went into technical bankruptcy.</p>
<p>However, using the right strategies we can make the highlighted curve steeper, leading the actual curve nearest to the idealized curve.</p>
<h1 id="paying-back">Paying back</h1>
<p>In order to avoid that applications achieve high debts or to handle projects already with high debt, we need to build a repayment plan. This can have any sort of effort in order to refactor and pay the time due, but the industry set some strategies that have proven effective.</p>
<ol>
<li><strong>Technical Backlog</strong>: each task should have a brief description, the reason that the change is important for the project and which part of the code base must change. Like any other task, we should estimate the effort needed to build a good and clean solution. When estimating the cost, the team should add an interest cost that is directly proportional to the probability of this code to change in the future and hence prioritizing the most costly task. A precise estimation is really difficult but a rudimentary approximation is enough to guide the decisions.
<ul>
<li>With this approach the technical debt becomes visible to all stakeholders and the decision of doing such a task can be made upon effort and future impact.</li>
<li>The task cost is easily traceable.</li>
<li>Don&rsquo;t mix technical and feature tasks.</li>
</ul>
</li>
<li><strong>Refactoring costs included on the feature estimation:</strong> explain the costs is necessary since we follow the principle that no feature should be implemented above bad code and therefore this code should be refactored first (<em>Boy Scout Rule</em>). This is a fundamental principle once bad code already incur in interests and adding a new feature using it will increase this interest exponentially, to the point where the cost to work on this code will be so high that will be impossible to make further changes.</li>
</ol>
<p><strong><em>Besides that, there are two other critic strategies that can be used depending on how high debt the project is in.</em></strong></p>
<ol>
<li><strong>Buffer Tasks:</strong> the task has a portion of the team&rsquo;s sprint. It can be used to unplanned refactorings, unforeseen problems, discoveries, etc. To avoid the task to be wasted on non-relevant work members of the team should always propose how they pretend to use the time and discuss with the rest of the team.</li>
<li><strong>Clean-up Releases:</strong> periodically or in critic scenarios, a team can make releases only with technical refactorings. This strategy is only useful if there is already a refactoring list to be made. Furthermore, the business team should give support as this will probably delay new features. The teams should consider using this when a greater effort is necessary, like in big architectural changes, infrastructural changes, build &amp; deploy, etc.</li>
</ol>
<h1 id="conclusion">Conclusion</h1>
<p>Therefore, Technical Debt can be key tool from the product perspective. It allows business people to improve time to market and customer satisfaction with the cost of bad code.</p>
<p>However high debt can bring to huge problems to the project, hence we must strive to be Prudent and Deliberated about our debt, always taking it with a repayment plan together, using all kinds of strategies to keep the debt under control.</p>
<p>To achieve this all stakeholders must be aware of the debt&rsquo;s nature and existence and bring the development team together in the decision.</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://www.infoq.com/presentations/debt-aware-culture">https://www.infoq.com/presentations/debt-aware-culture</a></li>
<li><a href="https://martinfowler.com/bliki/TechnicalDebtQuadrant.html">https://martinfowler.com/bliki/TechnicalDebtQuadrant.html</a></li>
<li><a href="https://www.infoq.com/minibooks/emag-technical-debt?utm_source=minibooks_about_TechnicalDebt&utm_medium=link&utm_campaign=TechnicalDebt">https://www.infoq.com/minibooks/emag-technical-debt?utm_source=minibooks_about_TechnicalDebt&amp;utm_medium=link&amp;utm_campaign=TechnicalDebt</a></li>
<li><a href="https://books.google.com.br/books?id=pKVFDwAAQBAJ&lpg=PA18&ots=Gc9vC8vf80&dq=Software%20Quality%20Assurance%20Pressman%202014&hl=pt-BR&pg=PA18#v=onepage&q=Software%20Quality%20Assurance%20Pressman%202014&f=false">https://books.google.com.br/books?id=pKVFDwAAQBAJ&amp;lpg=PA18&amp;ots=Gc9vC8vf80&amp;dq=Software Quality Assurance Pressman 2014&amp;hl=pt-BR&amp;pg=PA18#v=onepage&amp;q=Software Quality Assurance Pressman 2014&amp;f=false</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Difference between state and State</title>
      <link>https://caioferreira.dev/posts/difference-between-state-and-state/</link>
      <pubDate>Tue, 18 Dec 2018 12:00:00 +0000</pubDate>
      
      <guid>https://caioferreira.dev/posts/difference-between-state-and-state/</guid>
      <description>Photo by Annie Spratt on Unsplash
Today we will try to solve the ambiguity in the concept of state, highlighting the differences in the two main notions about it. The post describes the nature of each one, some use cases and how they fit in the object and functional paradigms.
Introduction On the last couple of months, I dove into the topic of State Machines and how we can design UI’s with this concept in order to provide better semantic and predictability to our application.</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="./cover.jpg" alt=""  />
</p>
<p>Photo by <a href="https://unsplash.com/photos/hzdgFPz1V24?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Annie Spratt</a> on <a href="https://unsplash.com/search/photos/machine?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p>
<p>Today we will try to solve the ambiguity in the concept of state, highlighting the differences in the two main notions about it. The post describes the nature of each one, some use cases and how they fit in the object and functional paradigms.</p>
<h1 id="introduction">Introduction</h1>
<p>On the last couple of months, I dove into the topic of State Machines and how we can design UI’s with this concept in order to provide better semantic and predictability to our application. When reading and talking about it often I have to stop and clarify which of the two ideas about state I am referring to:</p>
<ul>
<li>the idea of a collection of data at a point in time</li>
<li>the idea of the representation of an entity modeled as a state machine.</li>
</ul>
<p>For the sake of comprehension, we will use <strong>state</strong> to refer to the first and <strong>State</strong> to the last.</p>
<h1 id="requirements">Requirements</h1>
<p>We will use Typescript for our yummy examples so some familiarity with it would be good.</p>
<h1 id="the-state-as-a-travel-bag">The state as a travel bag</h1>
<p>The first notion we became comfortable with when learning about state in software development is the entity “travel bag”. Basically, we see a state as a collection of data in a specific point in time. Throughout the application lifecycle, this data is manipulated and altered in order to reflect the business process. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Pizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span> <span class="c1">// it&#39;s an enum that could be traditional or thin
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// entity controls
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">isBeingnPrepared</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBaking</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">baked</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">isBeingnPrepared</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">isBaking</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">baked</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">oven</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OvenService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingnPrepared</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBaking</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">oven</span><span class="p">.</span><span class="nx">bake</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">baked</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>From this point on, the pizza state, and hence the application state, has changed, because its data was updated. However, two booleans can be arranged in four different ways, and some of them are invalid states. In the object-oriented paradigm we would avoid this by encapsulating such data in an object and modeling its operations only through methods that guarantee atomic and consistent changes.</p>
<p>Until this use case, our model seems to be fine. But, the time comes to implement the next step in the pizzeria flow, the delivery.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Pizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// entity controls
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">isBeingnPrepared</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBaking</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">baked</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBeingDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">hasBeenDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">isBeingnPrepared</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">isBaking</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">baked</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">isBeingDelivered</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">hasBeenDelivered</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// bake behavior
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza() {</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">baked</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaNotBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">deliveryService</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DevelieryService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingnPrepared</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingDelivered</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">deliveryService</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">hasBeenDelivered</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>What raises a flag in this code is the use of a guard condition at the start of the delivery function that checks if the pizza is baked. If not, it throws an exception. This seems really simple, and if this were the only condition, it would be fine. But, a pizza could already be left for delivery, as such, we don’t want to try to send it again. So, we add another guard condition to our function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Pizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// entity controls
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">isBeingPrepared</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBaking</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">baked</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBeingDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">hasBeenDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// constructor
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// bake behavior
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza() {</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">baked</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaNotBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">isBeingDelivered</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyLeftException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">deliveryService</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DevelieryService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingPrepared</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingDelivered</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">deliveryService</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">notifyDelivery</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// notify delivery behavior
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>If we elaborate all the scenarios which a pizza can be, this kind of implementation with lots of branches and conditions expressed by <code>if/else</code> statements grows exponentially. It increases our code cyclomatic complexity and diminishes maintainability as such code is more fragile, harder to read and understand.</p>
<p>It gets worse when this kind of conditional start to spread across the code, as in the bake function, which needs to be updated in order to not try to bake it again.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Pizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// entity controls
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">isBeingPrepared</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBaking</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">baked</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">isBeingDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">hasBeenDelivered</span>: <span class="kt">boolean</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// constructor
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="k">void</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">baked</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">oven</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OvenService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBeingPrepared</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">isBaking</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">oven</span><span class="p">.</span><span class="nx">bake</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">baked</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// delivery behavior
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// notify delivery behavior
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Although this kind of design serves several proposes, in special on more simple or data-centric scenarios, in fast evolution and process-centric domains it evolves on a mess of code execution paths and unsynchronized conditionals through different functions.</p>
<p>The state as an entity travel bag has a use and it is to carry the associated information to the model. Try to control the behavior of this entity through the same concept ends up overloading it with responsibility and creating a silent trap for our design.</p>
<p>The problem faced here is that the application architecture allows for invalid behavior through invalid <strong>states</strong>, and when it does eventually some use case will expose the bugs created by this freedom. Besides that, this approach takes the system invariants, in this case, the Pizza cooking flow, and scatter then inside many implementation points instead of enforcing them in the design.</p>
<p><em>Side note: if you are versed in Algebraic Data Types you can see this as a Product Type with cardinality which tends to infinity.</em></p>
<h1 id="representational-state">Representational State</h1>
<p>Once we have the problem of control the entity information and behavior being done by the same construct, the <strong>state</strong>, our response could not be more simple: let’s break these responsibilities.</p>
<p>Therefore, we need a new pattern to handle our entity’s behavior.</p>
<p>But, the alternative pattern we propose when designing your application is not at all new. It is the State Pattern, describes in many ancient books about OO. And this books will tell you the same, that the State Pattern seeks to delegate an entity behavior to a specific implementation which is the current State and at the end of the method calculate the entity’s next State, which will now represent the entity, replacing its behaviors implementation on the fly. After all, this pattern is a translation of a state machine to the <em>idiom of the nouns.</em> An alternative implementation for our Pizza example can be as below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">interface</span> <span class="nx">IPizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">bakePizza</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">deliveryPizza</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span>: <span class="kt">boolean</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">type</span> <span class="nx">Pizza</span> <span class="o">=</span> <span class="nx">PreparingPizza</span> <span class="o">|</span> <span class="nx">BakedPizza</span> <span class="o">|</span> <span class="nx">DeliveringPizza</span> <span class="o">|</span> <span class="nx">DeliveredPizza</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">PreparingPizza</span> <span class="kr">implements</span> <span class="nx">IPizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span> <span class="c1">// it is an enum that could be traditional or thin
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">constructor</span><span class="p">(</span><span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">,</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">dough</span> <span class="o">=</span> <span class="nx">dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">ingredients</span> <span class="o">=</span> <span class="nx">ingredients</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="nx">getDough() {</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">getIngredients() {</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">ingredients</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">BakedPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">oven</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OvenService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">oven</span><span class="p">.</span><span class="nx">bake</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="nx">BakedPizza</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza() {</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaNotReadyForDelivery</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaNotReadyForDelivery</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">BakedPizza</span> <span class="kr">implements</span> <span class="nx">IPizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// constructor
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">constructor</span><span class="p">(</span><span class="nx">pizza</span>: <span class="kt">PreparingPizza</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">dough</span> <span class="o">=</span> <span class="nx">pizza</span><span class="p">.</span><span class="nx">getDough</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">ingredients</span> <span class="o">=</span> <span class="nx">pizza</span><span class="p">.</span><span class="nx">getIngredients</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">BakedPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">DeliveringPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">deliveryService</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DevelieryService</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">await</span> <span class="nx">deliveryService</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="nx">DeliveringPizza</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaNotLeftForDeliveryYey</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">DeliveringPizza</span> <span class="kr">implements</span> <span class="nx">IPizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// constructor
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">BakedPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">DeliveringPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyLeftForDeliveryException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="nx">DeliveredPizza</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">DeliveredPizza</span> <span class="kr">implements</span> <span class="nx">IPizza</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">dough</span>: <span class="kt">Dough</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">private</span> <span class="nx">ingredients</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">Ingredient</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// constructor
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// getters and setters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">bakePizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">BakedPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyBakedException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="kr">async</span> <span class="nx">deliveryPizza</span><span class="p">()</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">DeliveringPizza</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyLeftForDeliveryException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">public</span> <span class="nx">notifyDelivery</span><span class="p">(</span><span class="nx">wasSuccessful</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">PizzaAlreadyDeliveredException</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With this implementation, we enforce the domain invariants with our type system through the interface and the Pizza union type. With it we gain less cyclomatic complexity since we don’t have so many branches in our code and, by design, we don’t allow for invalid States to happen. Besides that, each State carries an internal data, its <em>travel bag</em>. As such, these patterns are not excluding, but rather composable.</p>
<p>In the trend on the front-end what we are usually seeing is more a functional paradigm approach to the state machines. The entity, represented as a state machine, is now just a different data structure for each State that can be interpreted by the pure functions that implement the domain behaviors. These functions than can internally delegate its call to others functions specialized in each State. This separation of the state machine implementation of the behavior is natural as it follows the idiom for functional architectures.</p>
<p>What remains in both cases are the nature of the State as an <strong>entity’s representation</strong>. It works on its behalf and delimits the possible behaviors it can expose.</p>
<p>For example, a Pizza could never be in Baked and Delivered States at the same time. Now, it isn’t an implementation that guarantees that it is the design itself. Such abstractions, that models the domain, the heart of our product, couldn’t depend on implementation details to be valid, they must depend on the abstractions itself.</p>
<p><em>Side note: if you are versed in Algebraic Data Types you can see this as a Union Type with finite cardinality in the order of less then a dozen.</em></p>
<h1 id="evolving-the-abstraction">Evolving the abstraction</h1>
<p>One could implement a State oriented design by using a simple enum, a proper state machine implementation or a more advanced concept, a statechart.</p>
<p>It is true that many domains can be modeled using the two first approaches to code a State, but sometimes we are faced with a high complexity scenario where this abstraction implementation would not scale with the development of the application.</p>
<p>For that reason that in 1987 David Harel proposed a new technique that expanded the grounds of the state machine definition, introducing tools like state hierarchy, parallelism, clustering, history, etc. He called it statecharts and it is a formalism that helps us scale the development of a State design, be implementing it thoroughly or just taking some tools.</p>
<p>I highly recommend reading more about statecharts as it can shift your mindset about how to approach problems.</p>
<h1 id="summary">Summary</h1>
<p>Now we can differentiate <strong>state</strong> from <strong>State</strong> and avoid accidental complexity by using the right construct to model our domain. Its worth nothing if I don’t say that there is no silver bullet and these are tools to deliver a job. We have been experimenting with this design style on my team and it has been helpful since our scenario is really complex and fast pacing.</p>
<p>If you have any questions or want to discuss these and other topics more in deep please comment or you can reach me at Twitter, @caiorcferreira.</p>
<p>Thanks for reading!</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://en.wikipedia.org/wiki/State_pattern">State Design Pattern</a></li>
<li><a href="https://dev.to/nimmo/state-driven-development-for-user-interfaces-part-1-an-introduction-27f1">State Driven Development for User Interfaces</a></li>
<li><a href="https://xstate.js.org/docs/">A Statechart implementation on JS</a></li>
<li><a href="http://www.inf.ed.ac.uk/teaching/courses/seoc/2005_2006/resources/statecharts.pdf">Statecharts: A Visual Formalism For Complex Systems</a> - the original article</li>
<li><a href="https://medium.com/@asolove/pure-ui-control-ac8d1be97a8d">Pure UI Control</a></li>
<li><a href="https://www.amazon.com/Constructing-User-Interface-Statecharts-Horrocks/dp/0201342782%5D(https://www.amazon.com/Constructing-User-Interface-Statecharts-Horrocks/dp/0201342782)">Constructing User Interfaces with Statecharts</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
