{"@attributes":{"version":"2.0"},"channel":{"title":"Guillaume","description":"Guillaume's personal website and blog","link":"https:\/\/guillaume.wuips.com\/","pubDate":"Sun, 03 Mar 2024 11:22:22 +0000","lastBuildDate":"Sun, 03 Mar 2024 11:22:22 +0000","generator":"Jekyll v3.9.5","item":[{"title":"On \"A Philosophy of Software Design\" by John Ousterhout","description":"<p>John Ousterhout is an awarded American professor of Computer Science. Born in 1954, his career follows the one of those that have been part of the beginning of Computer Science in the US: prestigious universities, fundamental Computer Science work, big companies and a bit of entrepreneurship.<\/p>\n\n<p><strong>In 2018 Ousterhout publishes \u201cA Philosophy of Software Design\u201d<\/strong>. I\u2019ve read the book last summer and found it very interesting, specially in how it can help us do Software at BlaBlaCar.<\/p>\n\n<p><img alt=\"book cover\" src=\"\/assets\/img\/on-philosophy-of-software-design-0.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<h1 id=\"software-design\">Software Design<\/h1>\n\n<p>Without any surprise, \u201cA Philosophy of Software Design\u201d is about Software Design, ie. <strong>the macro view one should take to architecture or design programs<\/strong>: looking at the problem and decomposing it into independent smaller pieces. For us at BlaBlaCar: what are the user features we have to support, and how should they be architectured.<\/p>\n\n<p>So the book is not about Software Development (ie. programing languages, for loops, objects and classes, inheritance, etc.), nor Software methodologies (ie. Agile, versioning, design patterns, etc.), although they are of course all linked.<\/p>\n\n<p>This focus on Software Design is fundamentally important. <strong>As a discipline, it\u2019s not present that much in the day to day work of Software Engineers<\/strong>. \nThat\u2019s very paradoxal given how much leverage investing in good Software Designs can have - we\u2019re talking in order-of-magnitude improvements here. That\u2019s where we should invest more of our time.<\/p>\n\n<p>We won\u2019t try to sum up the book in its entirety here. You can read the book\u2019s table of content for that: it gives a pretty strong overview of the book already, and enough food for thought to start designing software better. Let\u2019s focus on some key points that I\u2019ve found relevant for us instead.<\/p>\n\n<h1 id=\"complexity\">Complexity<\/h1>\n\n<p><strong>Software Design is all about managing Complexity<\/strong>, and this should be the start of all software initiatives. Ousterhout defines complexity in a pragmatic way, \u201canything related to the structure of a software system that makes it hard to understand and modify\u201d, and proposes that we reason with a cost\/benefit ratio.<\/p>\n\n<p>When a codebase is complex, it costs a lot to do even a small change with low benefit. When an architecture is simple, it costs little to implement a significant change that has a high benefit.<\/p>\n\n<p>We have <strong>two kinds of Complexities<\/strong> to deal with. The first is inherent to the problem solved. 2FA login for example is an intrinsically complex feature. This should be challenged and managed. The second kind of Complexity does not relates to the problem solved. Add the Redux library in a legacy web codebase and the code is immediately harder to grasp. This Complexity should be removed.<\/p>\n\n<p><img alt=\"2 kind of complexity schema\" src=\"\/assets\/img\/on-philosophy-of-software-design-1.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>The issue with Complexity is that it\u2019s incremental. It compounds over space (the codebase) and time. A bit of Complexity somewhere is not a critical issue but multiple complex parts there and there can make the global structure extremely challenging overnight.<\/p>\n\n<p><strong>Consider Complexity as a factor to be multiplied<\/strong> and not merely added.<\/p>\n\n<p><img alt=\"complexity compounds\" src=\"\/assets\/img\/on-philosophy-of-software-design-2.png\" style=\"max-height: 80px; margin: 2rem auto\" \/><\/p>\n\n<h1 id=\"managing-complexity\">Managing Complexity<\/h1>\n\n<p>Our job as Software Engineers, or Software Designers we should say, is to <strong>understand a given problem and manage its inherent Complexity<\/strong> the best we can, and in doing so, remove any unnecessary Complexity. How to do that?<\/p>\n\n<p>First, <strong>we should not introduce unnecessary Complexity<\/strong>. The book details a lot of good postures to follow that I will rephrase and illustrate like so: be obvious with the code (eg. via the names you choose for variables, methods, etc.), be consistent in the codebase (eg. use an uniform code style, document conventions and patterns, choose a few <a href=\"https:\/\/mcfunley.com\/choose-boring-technology\">Boring Technologies<\/a> and use them extensively), use comments (they should be written close to the code and provide high-level clues) and <a href=\"https:\/\/www.youtube.com\/watch?v=IcgmSRJHu_8\">make impossible states impossible<\/a>.<\/p>\n\n<p>Ousterhout also makes a great deal of a fundamental concept: <strong>Modules<\/strong>. Think about anything that can be used to group code and design a modular codebase: \u201cpackages\u201d, \u201ccomponents\u201d, \u201cclasses\u201d, \u201clibraries\u201d, \u201cservices\u201d, etc. Modules help manage Complexity that comes from dependencies between different part of code.<\/p>\n\n<p>We distinguish <strong>shallow and deep modules<\/strong>. Deep modules are \u201cthose whose interfaces are much simpler than their implementations\u201d and should be preferred: they hide a big chunk of information and the related Complexity behind a simple interface. The module\u2019s users can then reason from an higher design layer, using <strong>high-level Abstractions<\/strong> from the module\u2019s interface without having to know its internals.<\/p>\n\n<p><img alt=\"abstract module illustration\" src=\"\/assets\/img\/on-philosophy-of-software-design-3.png\" style=\"max-height: 380px; margin: 2rem auto\" \/><\/p>\n\n<p>A good deep module is for example an AutoComplete UI component that has an interface very similar to a standard input, but it deals with suggestions fetching and options sorting and filtering by itself. It removes the \u201cautocomplete burden\u201d from the developper using it, appart from providing some high-level options via its interface if needed.<\/p>\n\n<p>Deep modules follow the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Unix_philosophy\">Unix philosophy<\/a>: \u201cdo one (hard) thing and to it well\u201d. They therefore help to <strong>organize the problem space by layers of Abstractions<\/strong>, and pull the Complexity downwards inside their implementations.<\/p>\n\n<p>That\u2019s a quite challenging task to do, specially when the same people are working across layers. As developers, we need to consistently change posture between being the provider of a module, or its user. This can lead to unfortunate information leakage: \u201cthe same knowledge is used in multiple places\u201d, and a trivial change then requires to update multiple different modules.<\/p>\n\n<p>As <a href=\"https:\/\/www.youtube.com\/watch?v=LKtk3HCgTa8\">Rich Hickey would say<\/a>: \u201cDesign is separating things into things that can be composed\u201d. We separate things by hiding them inside modules.<\/p>\n\n<p><img alt=\"abstract module illustration with internal modules\" src=\"\/assets\/img\/on-philosophy-of-software-design-4.png\" style=\"max-height: 380px; margin: 2rem auto\" \/><\/p>\n\n<h1 id=\"but-in-real-life\">But in real life?<\/h1>\n\n<p>In reading this book from the perspective of a very <strong>Business-driven company<\/strong>, we can quickly think about arguments that will be made against this vision.<\/p>\n\n<p>With Deep Modules, don\u2019t we risk to over-engineer our code? Not more than with shallow code, the book argues. It\u2019s not because an interface is designed with an high-level, so quite general purpose, Abstraction that its implementation is supporting any potential use case.<\/p>\n\n<p>Is it really \u201cAgile\u201d to take time for the design of the software? Good question. The book argues that with Agile we are tented to ship features with a \u201ctactical mindset\u201d: as soon as possible without organizing them in a robust design. Should we start by designing everything before coding then? Of course not: \u201cit isn\u2019t possible to visualize a complex system well enough at the outset of a project to determine the best design\u201d.<\/p>\n\n<p>Instead, we should <strong>move incrementally on the problem<\/strong>, like we classically move incrementally on the implementation of a feature. We need to welcome architectural change in the middle of a project (and even maybe throw our first code), add abstractions one after another, and refactor on the go with your design becoming better.<\/p>\n\n<p>For Ousterhout, an iteration\u2019s output should be Abstractions, not features.<\/p>\n\n<p><img alt=\"iteration illustration\" src=\"\/assets\/img\/on-philosophy-of-software-design-5.png\" style=\"max-height: 80px; margin: 2rem auto\" \/><\/p>\n\n<h1 id=\"the-challenges-of-software-design\">The challenges of Software Design<\/h1>\n\n<p>To conclude, we can only praise the book for being able to highlight very well the challenges of Software Design. There is no absolute method. Any tactic used to the extreme can do more harm than good.<\/p>\n\n<p>That\u2019s where Software Design is a collective, always ongoing, effort. <strong>The Design of a program should be documented by the team responsible for it, discussed and adapted regularly<\/strong>, for example with <a href=\"https:\/\/buttondown.email\/hillelwayne\/archive\/more-software-projects-need-defenses-of-design\/\">Defenses of Design documents<\/a>.<\/p>\n\n<p><strong>\u201cWorking Code Isn\u2019t Enough\u201d<\/strong>, Ousterhout argues. We need to invest into decomposing the problem at hand. This requires time to understand and challenge it properly. The code produced should be strategic, not tactical, and that\u2019s specially true for parts of our software that serve long-lasting features.<\/p>\n\n<p>But what is \u201clong-lasting\u201d, we may ask? Actually lot of things are long-lasting-enough to benefit for a proper Software Design. It would be a fault to not take the few person-hours that are needed to produce a robust enough design, even for a part of the codebase that is not part of the very few highly critical pieces of code.<\/p>\n\n<p>That\u2019s the mindset we followed on some projects that were described as temporary set of features by the Business. We nevertheless took the time to understand the problem space, design frontend-oriented endpoints that provided enough high-level Abstractions to iterate with minimal effort on the feature. This has been very valuable in shipping the feature on time, and working on it again after that.<\/p>\n\n<p><strong>The corollary of Software Design is that some projects will lead to surprising big and costly software changes<\/strong>. That\u2019s expected. By adding new features requirements or new constraints, the problem space of a program can fundamentally change. We have to adapt the program structure deeply for the new features it has to support.<\/p>\n\n<p>That\u2019s true when following a Software Design mindset, that\u2019s also true without. It\u2019s better to take the time needed for such a refactoring than to hack around and force the code into doing something it\u2019s not designed for. That\u2019s would be the best way to shoot ourselves in the foot by introducing significant technical debt that will soon impact the team negatively.<\/p>\n\n<p>Let\u2019s also use the book ideas to reason at a bigger scale. Because <a href=\"https:\/\/en.wikipedia.org\/wiki\/Conway%27s_law\">\u201cSoftware Architecture and Organization are two side of the same coin\u201d<\/a>, <strong>lot of the insights on managing Software Complexity can be applied to the way we organize ourselves<\/strong>. There is a bridge to be made between \u201cA Philosophy of Software Design\u201d, <a href=\"https:\/\/teamtopologies.com\/book\">\u201cTeam Topologies\u201d<\/a> and others ressources like <a href=\"https:\/\/www.effectiveengineer.com\/\">\u201cThe Effective Engineer\u201d<\/a> that use the Complexity prism also (eg. code complexity, system complexity, product complexity, organization complexity).<\/p>\n\n<p><img alt=\"other complexities illustration\" src=\"\/assets\/img\/on-philosophy-of-software-design-6.png\" style=\"max-height: 200px; margin: 2rem auto\" \/><\/p>\n\n<blockquote>\n  <p>An effective team communicates much like optimized code: with clarity, modularity, and a focus on simplicity.<\/p>\n\n  <p>Addy Osmani<\/p>\n<\/blockquote>\n\n<p>A Philosophy of Software Design from John Ousterhout is ~190 pages long but a quick read, specially when you go over the examples given quickly and focus on the most relevant insights for your current context. It will be quite an unusual read in the Computer Science section of your library and hopefully give you a new perspective on Software Engineering.<\/p>\n\n<p>\u2013<\/p>\n\n<p><em>This post has originaly been shared internaly at BlaBlaCar. This is a slightly edited version.<\/em><\/p>\n\n","pubDate":"Fri, 01 Mar 2024 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2024-03-01-on-philosophy-of-software-design","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2024-03-01-on-philosophy-of-software-design"},{"title":"Operating Node.js in Kubernetes at scale at BlaBlaCar","description":"<p><img alt=\"Abstract illustration of a person on a computer alongside infrastructure servers\" src=\"\/assets\/img\/bbc-infrastructure.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>At BlaBlaCar, there is a dedicated team of frontend engineers in charge of the\nuser-facing web application. This frontend team\u2026 is also a backend team.<\/p>\n\n<p>Every BlaBlaCar web user consumes a web application. It\u2019s a Single Page\nApplication (SPA) based on Typescript and React. Alongside this SPA, the\nfrontend team also owns the Node.js service used for Server-Side Rendering\n(SSR).<\/p>\n\n<p>Why do we need SSR? When a user asks for a web page, we want to return this page\nfully rendered for both performance and SEO reasons. We don\u2019t want the user, or\na crawler, to receive an empty SPA skeleton without any content included yet.<\/p>\n\n<p>But operating this Node.js server at scale in a Kubernetes cluster is not as\ntrivial as it might look, especially for a team without previous infrastructure\nbackground. We had to understand deeply how Node.js works under the hood,\nmeasure how our server behaves with traffic and\u2026 learn Kubernetes. Such a big\ntopic when the day to day job is about writing new UI components!<\/p>\n\n<p>Let\u2019s look at the challenges we have overcome in the past few months. We will\nshow how we use CPU and memory metrics and other Kubernetes options to configure\nhow we scale Node.js. This article assumes the reader has basic knowledge about\nKubernetes or similar container orchestration tools.<\/p>\n\n<h1 id=\"scaling-whenneeded\">Scaling when\u00a0needed<\/h1>\n\n<p>All web traffic targeting our SPA goes through our Node.js server. We need to\nadapt the resources that support the service to match how much traffic we have\nto serve.<\/p>\n\n<p>We\u2019re scaling horizontally to handle more or less traffic. Like the majority of\nmicroservices, we add more Pods when traffic goes up. We remove some Pods when\ntraffic goes down. Each Pod contains its own Container of our Node.js server. It\nserves part of the web traffic directed to the application in the cluster.<\/p>\n\n<h2 id=\"scaling-oncpu\">Scaling on\u00a0CPU<\/h2>\n\n<p>The point in this setup is to know when we need to scale up and down. It\u2019s not a\nsurprise that we have to monitor the CPU for that as it indicates how much we\nuse the Pod.<\/p>\n\n<p>When the average CPU on all Pods exceeds a certain threshold, we add more Pods.\nWhen the CPU goes down, we remove Pods. This allows us to only use resources\nwhen needed. We don\u2019t want to pay for 20 Pods when only 5 would be enough.<\/p>\n\n<p>The Horizontal Pod Autoscaler (HPA) controls this scaling. Let\u2019s say our HPA\nconfig is this one:<\/p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"s\">\/\/ hpa.yml<\/span>\n<span class=\"na\">apiVersion<\/span><span class=\"pi\">:<\/span> <span class=\"s\">autoscaling\/v2beta2<\/span>\n<span class=\"na\">kind<\/span><span class=\"pi\">:<\/span> <span class=\"s\">HorizontalPodAutoscaler<\/span>\n<span class=\"na\">metadata<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n  <span class=\"na\">labels<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">app<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n<span class=\"na\">spec<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">scaleTargetRef<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">apiVersion<\/span><span class=\"pi\">:<\/span> <span class=\"s\">apps\/v1<\/span>\n    <span class=\"na\">kind<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Deployment<\/span>\n    <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n  <span class=\"na\">minReplicas<\/span><span class=\"pi\">:<\/span> <span class=\"m\">4<\/span>\n  <span class=\"na\">maxReplicas<\/span><span class=\"pi\">:<\/span> <span class=\"m\">30<\/span>\n  <span class=\"na\">metrics<\/span><span class=\"pi\">:<\/span>\n    <span class=\"pi\">-<\/span> <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Resource<\/span>\n      <span class=\"na\">resource<\/span><span class=\"pi\">:<\/span>\n        <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">cpu<\/span>\n        <span class=\"na\">target<\/span><span class=\"pi\">:<\/span>\n          <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Utilization<\/span>\n          <span class=\"na\">averageUtilization<\/span><span class=\"pi\">:<\/span> <span class=\"m\">70<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>And our Deployment config is the following:<\/p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"s\">\/\/ deployment.yml<\/span>\n<span class=\"na\">apiVersion<\/span><span class=\"pi\">:<\/span> <span class=\"s\">apps\/v1<\/span>\n<span class=\"na\">kind<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Deployment<\/span>\n<span class=\"na\">metadata<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n  <span class=\"na\">labels<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">app<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n<span class=\"na\">selector<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">matchLabels<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">app<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n<span class=\"na\">template<\/span><span class=\"pi\">:<\/span>\n  <span class=\"na\">metadata<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">labels<\/span><span class=\"pi\">:<\/span>\n      <span class=\"na\">app<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa-service<\/span>\n  <span class=\"na\">spec<\/span><span class=\"pi\">:<\/span>\n    <span class=\"na\">containers<\/span><span class=\"pi\">:<\/span>\n    <span class=\"pi\">-<\/span> <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa<\/span>\n      <span class=\"na\">image<\/span><span class=\"pi\">:<\/span> <span class=\"s\">spa<\/span>\n      <span class=\"na\">resources<\/span><span class=\"pi\">:<\/span>\n        <span class=\"na\">limits<\/span><span class=\"pi\">:<\/span>\n          <span class=\"na\">cpu<\/span><span class=\"pi\">:<\/span> <span class=\"s\">700m<\/span>\n          <span class=\"na\">memory<\/span><span class=\"pi\">:<\/span> <span class=\"s\">450Mi<\/span>\n        <span class=\"na\">requests<\/span><span class=\"pi\">:<\/span>\n          <span class=\"na\">cpu<\/span><span class=\"pi\">:<\/span> <span class=\"s\">600m<\/span>\n          <span class=\"na\">memory<\/span><span class=\"pi\">:<\/span> <span class=\"s\">320Mi<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Kubernetes instructs the workload resource to scale up when we reach\n<code class=\"language-plaintext highlighter-rouge\">averageUtilization<\/code>. In our situation it means the Deployment will scale up when\nthe average CPU reaches the target of 70% of the requested CPU value. The\nDeployment scales down when the metric is below the target.<\/p>\n\n<p>We chose resources limits and requests values after multiple iterations. The\nsmaller the values, the smaller the Pods are. With small Pods, we need more\nreplicas to handle the load than with bigger ones. We observed a clear overhead\non the memory side because of Node.js loading the service\u2019s code in memory. This\nlimits how small a Pod can be.<\/p>\n\n<p>In our example, we never go below 4 or above 30 Pods. Why 4 and not 1 or 2? This\nallows us to be sure we always have some servers running, on different Nodes.\nThe <code class=\"language-plaintext highlighter-rouge\">maxReplicas<\/code> helps us staying in control of the scaling. We don\u2019t want to\ndiscover that the service scaled to hundreds of Pods for an unknown reason\nduring the night without being paged first.<\/p>\n\n<p>This basic setup was a good first step to operate our service on the cluster\nwithout too much issue. There were parameters we had to carefully choose here,\nby classic trial and error. The main one is of course the CPU target percentage.\nWe don\u2019t want it to be too low to not provision unused resources, but not either\nso high that scaling happens too late or that we end up in a noisy neighbor\nsituation. 70% works well for us.<\/p>\n\n<h1 id=\"when-cpu-is-notenough\">When CPU is not\u00a0enough<\/h1>\n\n<p>Our CPU-based HPA worked great until a calm and sunny Saturday afternoon a few\nmonths ago. A random credential-stuffing attack targeting BlaBlaCar started. It\nsoon generated a lot of traffic and overloaded our Pods.<\/p>\n\n<p>Such an attack is usually not a big deal for us. We have the security tools and\nthe scaling in place to mitigate the potential impact for our users. It should\nnot even require any human action. But this time the traffic received by our\nNode.js servers doubled in less than 30 seconds. Pods were overloaded and unable\nto handle the traffic, and our HPA started scaling down our Deployment! Why?<\/p>\n\n<h2 id=\"back-toschool\">Back to\u00a0school<\/h2>\n\n<p>CPU is not the only metric to have in mind when operating code. Memory is here\ntoo. We may have learned in engineering school how the two are actually like the\ntwo sides of the same coin.<\/p>\n\n<p>During the incident the CPU usage dropped significantly. This justifies the HPA\nasking the workload to scale down.<\/p>\n\n<p><img alt=\"Graph showing the CPU usage drastically going down\" src=\"\/assets\/img\/operating-drop.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>In the meantime, memory usage for Pods was significantly more important than\nusual.<\/p>\n\n<p><img alt=\"Graph showing the memory usage drastically going up\" src=\"\/assets\/img\/operating-memory-important.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>What happened? Pods had to handle twice as much traffic. The Server-Side\nRendering endpoint was consuming much more memory than usual. We were making\nmore calls to our backends to fetch data and we were rendering more pages.\nBackend services were struggling too. This led to more IOs and more data waiting\nin memory. Pods were overloaded, yet the limited metric was not CPU, but memory.<\/p>\n\n<p>A snowball effect took place: some pods crashed with Out Of Memory errors (OOM).\nBecause memory usage reached the config limit level, Kubernetes killed the Pods\nas a sanction. New pods were started but crashed too. The remaining ones were\nnearly in a frozen state: memory bloated but CPU not at all. This led to the HPA\nasking to scale down Pods: the CPU metric was indeed below the target.<\/p>\n\n<p><img alt=\"Graph showing the number of pods decreasing drastically\" src=\"\/assets\/img\/operating-pod-drop.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>We were DDOSed and had to manually scale up the Deployment.<\/p>\n\n<h1 id=\"scaling-onmemory\">Scaling on\u00a0Memory<\/h1>\n\n<p><em>Important notice: this has been written before Node.js 12 and the addition of\ncontainer-aware memory. Using Node.js 12 or higher, the service would not have\ncrashed because of OOM. Instead, it would have reached the container memory\nlimit and then seen reduced performance because of some memory swapping.<\/em><\/p>\n\n<p>We learned the hard way that the CPU metric is not enough for our HPA. We have\nto read memory usage too.<\/p>\n\n<p>Of course, memory does not reflect the Node.js server activity in a linear way.\nSo it\u2019s not about making the metric the principal indicator we\u2019re using to scale\nup or down. But we do have to make the HPA aware of a certain limit.<\/p>\n\n<p>Turns out it\u2019s very easy to do with Kubernetes. We have to add another metric in\nthe HPA config, like so:<\/p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"s\">\/\/ hpa.yml<\/span>\n<span class=\"na\">apiVersion<\/span><span class=\"pi\">:<\/span> <span class=\"s\">autoscaling\/v2beta2<\/span>\n<span class=\"na\">kind<\/span><span class=\"pi\">:<\/span> <span class=\"s\">HorizontalPodAutoscaler<\/span>\n<span class=\"nn\">...<\/span>\n<span class=\"na\">spec<\/span><span class=\"pi\">:<\/span>\n    <span class=\"s\">...<\/span>\n    <span class=\"s\">metrics<\/span><span class=\"err\">:<\/span>\n    <span class=\"pi\">-<\/span> <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Resource<\/span>\n      <span class=\"na\">resource<\/span><span class=\"pi\">:<\/span>\n        <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">cpu<\/span>\n        <span class=\"na\">target<\/span><span class=\"pi\">:<\/span> \n         <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Utilization<\/span>\n          <span class=\"s\">averageUtilization<\/span><span class=\"err\">:<\/span> <span class=\"m\">70<\/span>\n    <span class=\"pi\">-<\/span> <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Resource<\/span>\n      <span class=\"na\">resource<\/span><span class=\"pi\">:<\/span>\n        <span class=\"na\">name<\/span><span class=\"pi\">:<\/span> <span class=\"s\">memory<\/span>\n        <span class=\"na\">target<\/span><span class=\"pi\">:<\/span> \n         <span class=\"na\">type<\/span><span class=\"pi\">:<\/span> <span class=\"s\">Utilization<\/span>\n          <span class=\"s\">averageUtilization<\/span><span class=\"err\">:<\/span> <span class=\"m\">85<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>There are two ways to understand this addition. We can say we\u2019re asking the HPA\nto scale up if memory usage reaches 85%. Or we can say we\u2019re asking the HPA to\nnot scale down if memory is over 85%. Both are valid. In our situation the\nsecond one suits the rationale quite well.<\/p>\n\n<p>If we had this kind of memory limit in place at the time of the incident, the\nmemory usage would never have reached the problematic level. And Pods would not\nhave scaled down.<\/p>\n\n<p><img alt=\"Graph showing the memory behaviour with a target\" src=\"\/assets\/img\/operating-pod-fixed.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<h1 id=\"better-operating-nodejs-in-kubernetes\">Better operating Node.js in Kubernetes<\/h1>\n\n<p>Writing the post mortem made the team invest more time in the infrastructure\ndimension of the service. We fixed our HPA, then started some background\ninvestigation on how to operate Node.js in Kubernetes better.<\/p>\n\n<p>We first looked at how others were doing. Some are scaling on a custom Node.js\nmetric: the <a href=\"https:\/\/nodesource.com\/blog\/event-loop-utilization-nodejs\">Event Loop Utilization\n(ELU)<\/a>. Can we do the\nsame? We tried but finally rolled back to the CPU\/memory duo. ELU seems\ninteresting when you have important computation off the main thread, or garbage\ncollection impacting the CPU measurement too much. This was not our case.<\/p>\n\n<p>Instead, we invested time in having a more robust and elastic setup. We\ndistributed Pods on nodes with\n<a href=\"https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/assign-pods-nodes-using-node-affinity\/\">affinity<\/a>\nand <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/scheduling-eviction\/topology-spread-constraints\/\">topology\nspread<\/a>\nconstraints to reduce the risk of seeing the Deployment impacted too much when\nlosing a Pod.<\/p>\n\n<p>We\u2019re also scaling up more rapidly and with more granularity by configuring our Pods to be smaller, cleaning the assets built into our Docker image and using a <a href=\"https:\/\/github.com\/GoogleContainerTools\/distroless\">distroless<\/a> base Node.js image. With more Pods, but smaller ones, we are consuming nearly the same amount of resources with only a small overhead. We\u2019ve gained in granularity as the HPA is scaling up and down.<\/p>\n\n<p>Because it is useless to have a lot of small Pods for granularity if the\ncontainer takes a lot of time to start, we have improved our procedure, going\nfrom a start-up time of 2 minutes in the worst scenario to something under 30\nseconds.<\/p>\n\n<p>We also leveraged HPA\u2019s new behavior config to scale up as fast as possible but\nscale down slowly. Paired with Kubernetes\u2019 scaling window, this prevents\n\u201cflapping\u201d, i.e. seeing the Deployment being scaled up and down continuously.<\/p>\n\n<p>To sum up the key points of our infrastructure setup:<\/p>\n\n<ul>\n  <li>We use both CPU and Memory metrics for our HPA, to scale safely<\/li>\n  <li>We target small &amp; quick-to-start Pods, for a reactive scaling<\/li>\n  <li>We spread out Pods on the maximum amount of nodes possible, for robustness<\/li>\n<\/ul>\n\n<p>Our service now consumes less resources when not needed, for example at night,\nbut is also able to handle short and big traffic increases better. This reduced\nthe amount of false positive alerts for the on-call person to nearly 0. More\nimportantly, we now handle traffic changes better, improving the user experience\noverall.<\/p>\n\n<hr \/>\n\n<p>This is backup post, primarily published on <a href=\"https:\/\/medium.com\/blablacar\/operating-node-js-in-kubernetes-at-scale-at-blablacar-3afb6d5d4299\">BlaBlaCar\u2019s blog<\/a><\/p>\n\n","pubDate":"Fri, 01 Jul 2022 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2022-07-01-operating-nodejs-in-kubernetes","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2022-07-01-operating-nodejs-in-kubernetes"},{"title":"Where is my store stored?","description":"<p><img alt=\"Abstract illustration of a person holding two puzzle pieces\" src=\"\/assets\/img\/where-is-store-stored-illustration.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>Some of us have concepts in mind that they keep returning to over an over, as if\nobsessed with them. For me as a frontend software engineer, it\u2019s all about the\nconcepts of <em>State<\/em> and <em>Store<\/em>. I keep looking at technical issues through this\nlens - current and previous colleagues know this all too well (sorry\ncolleagues!). I\u2019ve actually already <a href=\"\/tech\/posts\/2020-11-22-state-store-in-frontend-codebases\">written about\nit<\/a> in the past.<\/p>\n\n<p>I have changed companies a few month ago to join BlaBlaCar and the very cool\nfrontend team building and maintaining its web app. After this move I find\nmyself in situations that make me change how I think about the \u201cfrontend Store\ndilemma\u201d I kept referring to. There is now a new question I ask myself again and\nagain looking at our frontend codebases:<\/p>\n\n<p>Where is the store stored?<\/p>\n\n<h1 id=\"state-vs-store---one-more-time\">State vs Store - one more time<\/h1>\n\n<p>Let\u2019s go back to the difference between the concepts of <em>State<\/em> and <em>Store<\/em>\neven though we sometimes use the two terms indistinctively in frontend\ncodebases. In every frontend app, data (the State) is stored somewhere (the\nStore).<\/p>\n\n<p>When we\u2019re thinking about states, we\u2019re in the world of ideas. We ask\nquestions about what those states represent, which transition between states are\nvalid, or how should we shape our states.<\/p>\n\n<p>When we deal with stores, we\u2019re not in the world of ideas anymore. We\u2019re back in\nthe reality of the code that runs somewhere, mainly in a Javascript runtime.  We\nask where do we store out data, how do we read it, how do we write it, how can\nwe subscribe to changes?<\/p>\n\n<p>In this post, we\u2019re focusing on the \u201cstore\u201d kind of questions.<\/p>\n\n<h1 id=\"the-multiple-places-we-can-store-our-stores\">The multiple places we can store our stores<\/h1>\n\n<p>The reality of the store that lives \u201csomewhere\u201d in the code is the very\nproblem. The store in itself, whether a simple variable or a full Redux store,\nhas be stored somewhere in our browser, NodeJS or other fancy javascript\nengine runtime.<\/p>\n\n<p>Deciding <em>where our stores are stored<\/em> is not trivial and comes with big\nimpacts. Let me draw here the three main potential locations we can use for our\nstores.<\/p>\n\n<h2 id=\"modules\">Modules<\/h2>\n\n<p>We can use modules to host our stores. It\u2019s quite convenient and it makes full\nuse of the Javascript ES6 language.<\/p>\n\n<p>Let\u2019s say we have a module storing Cars (our state here).<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ cars\/store.js<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">_cars<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">add<\/span><span class=\"p\">(<\/span><span class=\"nx\">car<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">_cars<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">car<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">list<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">_cars<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span> \n<\/code><\/pre><\/div><\/div>\n\n<p>This <code class=\"language-plaintext highlighter-rouge\">cars\/store.js<\/code> module acts as a very naive store with both <em>read<\/em>\n(<code class=\"language-plaintext highlighter-rouge\">list<\/code>) and <em>write<\/em> (<code class=\"language-plaintext highlighter-rouge\">add<\/code>) functionalities.<\/p>\n\n<p>Using this store in the rest of the codebase is very easy: we just have to use\nclassic ES6 imports.<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ another\/part\/of\/the\/codebase.js<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">Cars<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/path\/to\/cars\/store<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">doingSomething<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">current cars: <\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">Cars<\/span><span class=\"p\">.<\/span><span class=\"nx\">list<\/span><span class=\"p\">());<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">newCar<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">My old car<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n  <span class=\"nx\">cars<\/span><span class=\"p\">.<\/span><span class=\"nx\">add<\/span><span class=\"p\">(<\/span><span class=\"nx\">newCar<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>And <em>voil\u00e0<\/em>! Because ES6 module are stateful, the <code class=\"language-plaintext highlighter-rouge\">_cars<\/code> \u201cprivate\u201d variable\nwill stay the same across all imports, making <code class=\"language-plaintext highlighter-rouge\">cars\/store.js<\/code> a store by itself.<\/p>\n\n<p>Should we need a reactive store (ie. being able to <em>subscribe<\/em> to state\nchanges), we would write our own subscribe function by hand or use classic store\nlibraries (some opinionated like Redux, Mobx, or some more transversal like\nthe <a href=\"https:\/\/github.com\/fp51\/store-library\">@fp-51\/store<\/a> I authored one or\ntwo years ago).<\/p>\n\n<h2 id=\"libraries--framework-location\">Libraries \/ Framework location<\/h2>\n\n<p>Another very common way of storing our store is using the locations provided by\nthe main view library or framework we use (React, Vue, etc.).<\/p>\n\n<p>Let\u2019s say that we\u2019re using React here. The <em>go to<\/em> store location is then a\nsimple <code class=\"language-plaintext highlighter-rouge\">useState<\/code> hook.<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ app.js<\/span>\n<span class=\"k\">import<\/span> <span class=\"nx\">React<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">react<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">UserContext<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/cars.js<\/span><span class=\"dl\">'<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">({<\/span> <span class=\"nx\">children<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">cars<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setCars<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useState<\/span><span class=\"p\">([]);<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">addCar<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">car<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">setCars<\/span><span class=\"p\">(<\/span><span class=\"nx\">cars<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">[...<\/span><span class=\"nx\">cars<\/span><span class=\"p\">,<\/span> <span class=\"nx\">car<\/span><span class=\"p\">]);<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"o\">&lt;<\/span><span class=\"nx\">MyComponent<\/span> <span class=\"nx\">cars<\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span><span class=\"nx\">cars<\/span><span class=\"p\">}<\/span> <span class=\"nx\">addCar<\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span><span class=\"nx\">addCar<\/span><span class=\"p\">}<\/span> <span class=\"sr\">\/<\/span><span class=\"err\">&gt;\n<\/span>  <span class=\"p\">)<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We\u2019re not using modules to host our store anymore. React does it for us via\n<code class=\"language-plaintext highlighter-rouge\">useState<\/code>.  Note that we\u2019re reactive by default here: calling <code class=\"language-plaintext highlighter-rouge\">addCar<\/code> in\n<code class=\"language-plaintext highlighter-rouge\">MyComponent<\/code> will rerender all <code class=\"language-plaintext highlighter-rouge\">&lt;App \/&gt;<\/code>.<\/p>\n\n<p>Using <code class=\"language-plaintext highlighter-rouge\">useState<\/code> alone usually doesn\u2019t scale well as we\u2019re forced to pass <code class=\"language-plaintext highlighter-rouge\">cars<\/code>\nand <code class=\"language-plaintext highlighter-rouge\">setCar<\/code> everywhere through props. The issue is called <a href=\"https:\/\/sebhastian.com\/react-prop-drilling\/\">props\ndrilling<\/a>. To overcome this, we\ncan add a <code class=\"language-plaintext highlighter-rouge\">Context<\/code> to make our state and its API available to every child\ncomponents via a simple <code class=\"language-plaintext highlighter-rouge\">useContext<\/code> - even if <a href=\"https:\/\/twitter.com\/sebmarkbage\/status\/1219836431972978689\">it\u2019s not made for\nthat<\/a>. The idea is\nthe same: the store is stored inside React \u201cruntime\u201d via <code class=\"language-plaintext highlighter-rouge\">useState<\/code>.<\/p>\n\n<h2 id=\"programmatic-store\">Programmatic store<\/h2>\n\n<p>And finally, there\u2019s the good old way: dependency injection by hand. Let\u2019s say\nwe\u2019re in a full custom app (no React shenanigans) and we don\u2019t want to use\nmodules. We can then craft a store instance when the app starts and pass it\nalong everywhere via function parameters.<\/p>\n\n<p>Let\u2019s write a store factory very similar to the ES6 module option.<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ store.js<\/span>\n<span class=\"kd\">function<\/span> <span class=\"nx\">buildStore<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">_cars<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[];<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">add<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">car<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">_cars<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">car<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">},<\/span>\n\n    <span class=\"na\">list<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"k\">return<\/span> <span class=\"nx\">_cars<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">},<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now that our store\u2019s factory is ready, we just have to craft a store instance\nsomewhere in our app\u2019s entrypoint and pass it along everywhere.<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ index.js<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">startApp<\/span><span class=\"p\">(<\/span><span class=\"nx\">store<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">add<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">new car<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"nb\">document<\/span><span class=\"p\">.<\/span><span class=\"nx\">body<\/span><span class=\"p\">.<\/span><span class=\"nx\">innerText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">list<\/span><span class=\"p\">().<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">, <\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">initApp<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">store<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">buildStore<\/span><span class=\"p\">();<\/span>\n\n  <span class=\"nx\">startApp<\/span><span class=\"p\">(<\/span><span class=\"nx\">store<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"nx\">initApp<\/span><span class=\"p\">();<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Of course, any function that needs to access the store will have to receive it\nvia its parameters\u2026 We\u2019re right back where we left our props drilling issue.<\/p>\n\n<h1 id=\"pros-and-cons-of-each-store-location\">Pros and Cons of each store location<\/h1>\n\n<p>We\u2019ve outlined three potential store locations. You can see me coming with\nthe list of pros and cons for each of them.<\/p>\n\n<p>Let\u2019s start with modules. Pros: it\u2019s raw ES6 (<code class=\"language-plaintext highlighter-rouge\">import<\/code> \/ <code class=\"language-plaintext highlighter-rouge\">export<\/code>) and it is\nquite simple to grasp. It\u2019s also very flexible. We can do more or less whatever\nwe want in a module and every part of our codebase can use it.<\/p>\n\n<p>But there is a big constraint. The same codebase can be used in different\nruntimes.  Actually a lot of single page applications run in both browser and\nNodeJS runtimes, where the contexts are very different. In the browser, we can\nmake the assumption that there is only one user at a given time using the\nruntime. So our module state will be \u201cour user state\u201d. On NodeJS, we will\nprobably use the code to render the page server-side for multiple HTTP requests.\nIn this situation, our module state will be shared between multiple users. Boom!\nEnjoy the data leak issue.<\/p>\n\n<p>For library or framework locations such as our React <code class=\"language-plaintext highlighter-rouge\">useState<\/code>, the big\nadvantage is probably the strong integration with the corresponding library.\nEven bigger store solutions like Redux come with their bindings like\n<code class=\"language-plaintext highlighter-rouge\">react-redux<\/code> that do the job for us. No need to handle the Context \/ Provider\nstuff I mentioned. No issue with server-side rendering: the store being \u201cin\nReact runtime\u201d, it will be scoped to the <code class=\"language-plaintext highlighter-rouge\">ReactDOM.render<\/code> or\n<code class=\"language-plaintext highlighter-rouge\">ReactDOMServer.renderToString<\/code> which will be called once for every http\nrequests.<\/p>\n\n<p>I\u2019m beginning to think this deep integration with view libraries in a way that\nmakes server and browser contexts similar is a big factor in the success of\nRedux and others (Mobx, etc.).<\/p>\n\n<p>The main issue is however that we\u2019re locked to the corresponding library \/\nframework. Let\u2019s say you have an SPA where you want to limit React to the\nviews, and that you need to read \/ update \/ subscribe to store change in other\ncontexts. You just can\u2019t, it\u2019s React or bust. Back at square one.<\/p>\n\n<p>Let\u2019s give another chance to our programmatic store sharing solution, ie.\n\u201ccrafting a store as soon as the app starts and then pass it along to every\nsubsequent bit of code that needs it through a sort of a handmade dependency\ninjection\u201d.<\/p>\n\n<p>It\u2019s clearly not very convenient to have to pass our store programmaticaly to\nevery part of the codebase as it creates a lot of boilerplate, but at least\nwe face no issue with SSR if we craft one store per request server-side, and one\nstore when the app starts browser-side. It is very flexible as well: we\ncan do whatever we want with our store and use it everywhere in the codebase.<\/p>\n\n<p>I didn\u2019t mention testing as all three locations look kind of similar to me in\nterms of how easy it is to test code that rely on them. We can use full module\nmocking (eg. with <code class=\"language-plaintext highlighter-rouge\">jest.mock<\/code>). This is especially usful for option 1. We can\nalso use \u201cprogrammatic mocks\u201d, more or less handcrafted (option 2 and option 3)\nby crafting fake stores and passing them to the code that use them (through\ninjection or by wrapping a fake React <code class=\"language-plaintext highlighter-rouge\">Provider<\/code>).<\/p>\n\n<h1 id=\"no-one-location-to-rule-them-all\">No one location to rule them all<\/h1>\n\n<p>There is no perfect solution to host stores with Javascript. It\u2019s all about\ncompromises. The module location is very convenient as long as you don\u2019t have to\nrender your app server-side. We don\u2019t have threads or the luxury of a\nsingle runtime per user there. You may find solutions exploring low level <a href=\"https:\/\/nodejs.org\/api\/worker_threads.html\">NodeJS\nprimitives<\/a> or <a href=\"https:\/\/github.com\/laverdet\/isolated-vm\">V8\nfunctionalities<\/a> deep features to\nisolate runtimes, but it\u2019s certainly not out of the box.<\/p>\n\n<p>The library \/ framework location is very convenient but leaves no\nroom for code outside of the library \/ framework pattern which is a big issue in my\nopinion for big and\/or old codebases that have to support multiple technologies,\nhistoricities and teams.<\/p>\n\n<p>The programmatic store, while not being very convenient by forcing us to inject\nour store everywhere by hand, could be a good low-level start on top of which\nwe could build abstractions for the different parts of the codebase (providing\nReact context for the views, automatic dependency injection for other parts of\nthe code, for example).<\/p>\n\n<p>So here I am now. Every time I deal with a store I\u2019m looking at <em>where the store\nis stored<\/em> to understand the constraints that come with its location and how it\ncan be used and I suggest you do the same. Another fascinating part of the State\nand Store puzzle!<\/p>\n\n<hr \/>\n\n<p>Special thanks to <a href=\"https:\/\/www.benrajalu.net\/\">Benoit Rajalu<\/a>, <a href=\"https:\/\/twitter.com\/asauray\">Antoine\nSauray<\/a> and Sylvain Hamelain for having reviewed\npreliminary drafts of this.<\/p>\n\n<p>Illustration made by <a href=\"https:\/\/www.artify.co\/uncommon-illustrations\">Hugo for Artify<\/a>.<\/p>\n\n","pubDate":"Sun, 26 Sep 2021 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-09-26-where-the-store-is-stored","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-09-26-where-the-store-is-stored"},{"title":"Distinction Drawer","description":"<p><img alt=\"Abstract illustration of a flower where leaves are replaced by light bulb\" src=\"\/assets\/img\/distinction-drawer-flowers.png\" style=\"max-height: 480px; margin: 2rem auto\" \/><\/p>\n\n<p>Earlier this week <a href=\"https:\/\/martinfowler.com\/aboutMe.html\">Martin Fowler<\/a>\nrevealed who he was all along. Turns out, he <a href=\"https:\/\/twitter.com\/martinfowler\/status\/1363847064317263876?ref_src=twsrc%5Etfw\">sees himself as a \u201cdistinction\ndrawer\u201d<\/a>\nin software. The confession immediately clicked for me. I realised that being in\nthis distinction-drawer mode is something I like a lot in my job as a software\nengineer.<\/p>\n\n<p>What does it mean to be in distinction-drawer mode?<\/p>\n\n<p>We\u2019re in this distinction-drawer mode when we take a step back from the day to\nday activity of writing code and start reasoning about what we\u2019re doing. While\ndoing so, we see our current bit of code as a tiny cog distinct from other parts\nof the larger machine. We\u2019re thinking \u201cmeta\u201d.<\/p>\n\n<p>When we\u2019re writing specs for a new feature, using our knowledge to articulate\nhow this feature could be built upon multiple logical parts of the codebase\ntalking to each other, we\u2019re also in this distinction-drawer mode.<\/p>\n\n<p>What logical distinctions can we draw? How do we combine things with one\nanother? How do we avoid making false equivalencies? What are the fundamental\nresponsibilities at play? What concepts should we bring together to reach our\ngoal? Making strong, identified distinctions will help us build a strong new\nfeature, but it will also enable us to preserve the \u201cmachine\u201d as well.<\/p>\n\n<p>Imagine a gardener having to plant a new flower, stepping back from the\nflowerbed to decide what\u2019s the next step. Where will the flower go? And why\nthere? To answer that without relying only on a possibly flawed intuition, the\ngardener uses years of previous experiences as well as the general rules of\ngardening.<\/p>\n\n<p>This moment of thought, this step back, has a purpose. It enables the gardener to\nstop focusing on the single flower, seeing the whole garden instead. With this\npoint of view, finding how the new flower can be combined with the existing\nplants becomes easier. The gardener can now see where, and why. It triggers the\ndistinction-drawer mode.<\/p>\n\n<p>Can the flower go here? No it can\u2019t because it\u2019s not the right color. Can it go\nthere? The surrounding flowers are the right color, but much taller; it would\nruin the new one. Drawing distinctions is acknowledging a flower is not a\ngarden.<\/p>\n\n<p>It should not be confused with the attitude we have when we\u2019re just taking a\npause from coding. It\u2019s nothing like having a coffee with a colleague and\nsharing how our tasks are doing.<\/p>\n\n<p>It\u2019s about using the ideas we collected from technical books we\u2019ve read,\ntechnical discussion we had, common patterns we\u2019ve seen over the years working\non different codebases to understand better what is at stake in what we\u2019re\ndoing. It is actual work, maybe the most important part of it even.<\/p>\n\n<p>It\u2019s <a href=\"https:\/\/twitter.com\/ericnormand\/status\/1313854365741076480\">seeking the\ntruth<\/a>. We\u2019re\nbringing concepts and models on the table to reason from <a href=\"https:\/\/jamesclear.com\/first-principles\">first\nprinciples<\/a>.<\/p>\n\n<p>Doing so, we try to find the fundamentals principles hidden behind our code. We\nreason about responsibilities and boundaries. Where is the code <a href=\"http:\/\/localhost:4000\/tech\/posts\/2020-11-22-state-store-in-frontend-codebases\">dealing with\n<em>state<\/em><\/a>?\nHow do we <em>store<\/em> data? When are we doing <em>requests<\/em> to other services? How to\nrepresent <em>permissions<\/em>? What is the responsibility of the <em>view<\/em>?<\/p>\n\n<p>We\u2019re distinguishing logical parts from each other, forming a modularised\npicture in our head (and at one point on paper, hopefully) where each part has a\nclear and strong role.<\/p>\n\n<p>Coming back to our code with such a picture in mind helps us better articulate\nwhat we\u2019re doing. We now know <em>this thing<\/em> should be put <em>here<\/em>, but <em>this other\nthing<\/em> should be done <em>there<\/em> and we know how to explain it to our colleagues. It\nlooks like nothing but it\u2019s extremely powerful.<\/p>\n\n<p>Sometimes it\u2019s real Eureka! moments.<\/p>\n\n<p>A lot could probably be added on this curious topic. Can someone always be in\ndistinction-drawer mode, disconnected from the raw, immediate code?  Which\nresources can be used to grow such a mindset? Can we be in distinction-drawer\nmode collectively? How do we convince colleagues who don\u2019t bother with first\nprinciples reasoning?<\/p>\n\n<p>Distinction-drawer mode can start very simply, like <a href=\"https:\/\/twitter.com\/jayrosen_nyu\/status\/1363669902238900225\">Jay Rosen\nexplains<\/a>. \u201cWhen in\ndoubt, draw a distinction.\u201d This is the complete opposite of what we\u2019re usually\ndoing in software when facing an architecture issue.<\/p>\n\n<p>Instead of trying to group things together or add things over existing things,\nlet\u2019s try to find how to separate them in a way that makes the most sense,\nbringing classic computer science concepts to help.<\/p>\n\n<hr \/>\n\n<p>Thanks to <a href=\"https:\/\/www.benrajalu.net\/\">Benoit Rajalu<\/a> for having helped with\ndrafts of this.<\/p>\n\n<p>Illustration made by <a href=\"http:\/\/Bulbman.art\">Bulbman.art<\/a>.<\/p>\n","pubDate":"Tue, 23 Feb 2021 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-02-23-distinction-drawer","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-02-23-distinction-drawer"},{"title":"The best test is the test you don't have to write","description":"<p><img src=\"\/assets\/img\/best-test-trees.png\" alt=\"Abstract illustration of trees\" \/><\/p>\n\n<p>When a software engineering team wants to improve or secure its codebase\nquality, it often heavily invests in tests.<\/p>\n\n<p>But tests are not a magic remedy, and they are far from cheap.<\/p>\n\n<p>Using tests as the <em>only<\/em> tool that can help with quality can actually cause\ntroubles. Are they worth the cost of their conception and maintenance? How can\nwe maintain or improve the quality of a codebase without bloating it with\nsuperficial tests?<\/p>\n\n<h2 id=\"about-tests\">About tests<\/h2>\n\n<p>Let\u2019s say we wrote some code. Disciplined as we are, we know it\u2019s time to add\nthe corresponding test. We have the maturity to know we need different levels of\ntesting to cover a feature: unit testing, integration testing, end-to-end\ntesting, snapshot testing, etc.<\/p>\n\n<p>It\u2019s a common reflex and our intentions are good. However, continuously adding\ntests does create issues on its own. All the tests become a \u201ccodebase in the\ncodebase\u201d: with the dedicated libraries we use for them, the testing framework\nthat is so hard to change, the CI time required to run the whole test suite, the\nmaintenance those tests need, their flakiness, the colleagues complaining they\nneed the best-in-class computer to run them in an acceptable time, etc.<\/p>\n\n<p>Tests are not free, so let\u2019s challenge our good intentions. Why are we writing\nthem in the first place?<\/p>\n\n<p>We write tests to assert our code performs correctly under given constraints.\nWe know writing code is not easy, and a lot can go (and has gone) wrong.<\/p>\n\n<h2 id=\"being-its-own-enemy\">Being its own enemy<\/h2>\n\n<p>The key element is that tests are performed under \u201cgiven contraints\u201d. If the\nworld always was in one single and predictable state, we would not have to write\nand maintain any test: our code would run the way we\u2019ve tested it by hand the\nfirst time.<\/p>\n\n<p>But the world can be in an infinite number of states. To be always safe, our\ncode and therefore our tests would need to accommodate as many of them as\npossible.<\/p>\n\n<p>It\u2019s especially true when we write code that will be used by other code. The\nlarger the API we write, the more states we allow and the more tests we will\nhave to write. Hell is paved with good intentions.<\/p>\n\n<p>We don\u2019t need good intentions, we need properly-scoped ones. If we agree that\neverything our code <em>can<\/em> and <em>should not<\/em> do will require testing, then we\nshould enforce strong limitations to our code\u2019s scope.<\/p>\n\n<p>In other words we should not write code that allows <em>impossible states<\/em>.<\/p>\n\n<h2 id=\"impossible-state\">Impossible state<\/h2>\n\n<p>Impossible states are any states our code should not be in. A password that is\nempty during a login. A payment that is at the same time waiting for approval\nand rejected. A data we\u2019re fetching that is both loading and successful.<\/p>\n\n<p>Let\u2019s take the example of the data we\u2019re fetching. We can call such data a\n<em>remote<\/em> data. Imagine we\u2019re writing a naive React hook that does an\nunspecified request and returns some data. One can use it like so:<\/p>\n\n<div class=\"language-typescript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">isLoading<\/span><span class=\"p\">,<\/span> <span class=\"c1\">\/\/ true | false <\/span>\n  <span class=\"nx\">error<\/span><span class=\"p\">,<\/span>     <span class=\"c1\">\/\/ Error | undefined<\/span>\n  <span class=\"nx\">data<\/span><span class=\"p\">,<\/span>      <span class=\"c1\">\/\/ TheData | undefined<\/span>\n<span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useMyRequest<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>How many states can be represented by the API here? 2*2*2 = 8 states. Not\nconviced? Here is the list of all of them:<\/p>\n\n<figure>\n  <div>\n\n    <table>\n      <thead>\n        <tr>\n          <th>isLoading<\/th>\n          <th>error<\/th>\n          <th>data<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody>\n        <tr>\n          <td>true<\/td>\n          <td>undefined<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>true<\/td>\n          <td>undefined<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>true<\/td>\n          <td>Error<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>true<\/td>\n          <td>Error<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>undefined<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>undefined<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>Error<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>Error<\/td>\n          <td>Data<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n\n  <\/div>\n  <figcaption>\nEach line is a possible state created by this hook\n  <\/figcaption>\n<\/figure>\n\n<p>That\u2019s a lot of possibilities and some of them are clearly not needed. After\nall, how many actual states can or should a naive remote data fetcher be in?\nProbably only 4: request not yet asked, request loading, request failure,\nrequest success.<\/p>\n\n<p>The API we chose with the hook above therefore leaves us with 4 valid and 4\nimpossible states:<\/p>\n\n<figure>\n  <div>\n\n    <table>\n      <thead>\n        <tr>\n          <th>isLoading<\/th>\n          <th>error<\/th>\n          <th>data<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody>\n        <tr>\n          <td>true<\/td>\n          <td>undefined<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>undefined<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>undefined<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>Error<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n\n  <\/div>\n  <figcaption>\n  The valid states\n  <\/figcaption>\n<\/figure>\n\n<p><code class=\"language-plaintext highlighter-rouge\">false | Error | undefined<\/code> for example means \u201crequest completed, but resulted\nin failure\u201d, which is one of the valid states.<\/p>\n\n<figure>\n  <div>\n\n    <table>\n      <thead>\n        <tr>\n          <th>isLoading<\/th>\n          <th>error<\/th>\n          <th>data<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody>\n        <tr>\n          <td>true<\/td>\n          <td>undefined<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>true<\/td>\n          <td>Error<\/td>\n          <td>undefined<\/td>\n        <\/tr>\n        <tr>\n          <td>true<\/td>\n          <td>Error<\/td>\n          <td>Data<\/td>\n        <\/tr>\n        <tr>\n          <td>false<\/td>\n          <td>Error<\/td>\n          <td>Data<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n\n  <\/div>\n  <figcaption>\n  The impossible states\n  <\/figcaption>\n<\/figure>\n\n<p><code class=\"language-plaintext highlighter-rouge\">true | Error | Data<\/code> means nothing for our naive request (both error and data\nwhile still loading). It\u2019s an impossible state unrepresentative of the actual,\n  actionable code functionality.<\/p>\n\n<p>To ensure those 4 impossible states never happen despite the API allowing them,\nwe would need to write tests around the API, like an alarm system. \u201cHey! The\nthings you knew could happen but did not want to happen\u2026 they did happen\u201d.<\/p>\n\n<p>Is it irremediable? It should not be. Testing the four valid states is enough\nwork already, why deal with the impossible ones?<\/p>\n\n<p>So, how to <a href=\"https:\/\/www.youtube.com\/watch?v=IcgmSRJHu_8\">make impossible states\nimpossible<\/a>?<\/p>\n\n<h2 id=\"representing-knowledge-in-code\">Representing knowledge in code<\/h2>\n\n<p><a href=\"http:\/\/guillaume.wuips.com\/tech\/posts\/2020-12-01-defining-success-engineering-team\">There is more to software engineering than continuously producing code (and\ntests)<\/a>.\nWe sometimes have to pause and reflect on what models we\u2019re using, what business\nfeatures we\u2019re implementing. We can then use code to write this knowledge down.<\/p>\n\n<p>Now that we know our remote data can only be in 4 possibles states, how do we\nenforce it with code rather than tests?<\/p>\n\n<p>Such a mindset is something we\u2019re more used to see in codebases written with\nML-languages like Haskell:<\/p>\n\n<div class=\"language-haskell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">data<\/span> <span class=\"kt\">RemoteData<\/span> <span class=\"o\">=<\/span> <span class=\"kt\">NotAsked<\/span>\n                <span class=\"o\">|<\/span> <span class=\"kt\">Loading<\/span>\n                <span class=\"o\">|<\/span> <span class=\"kt\">Failure<\/span> <span class=\"kt\">Error<\/span>\n                <span class=\"o\">|<\/span> <span class=\"kt\">Success<\/span> <span class=\"kt\">TheData<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>But we can also write something similar using more familiar\nlanguages, here in Typescript:<\/p>\n\n<div class=\"language-typescript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">type<\/span> <span class=\"nx\">RemoteData<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">E<\/span><span class=\"p\">,<\/span> <span class=\"nx\">P<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> \n  <span class=\"o\">|<\/span> <span class=\"p\">{<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">NotAsked<\/span><span class=\"dl\">'<\/span> <span class=\"p\">}<\/span>\n  <span class=\"o\">|<\/span> <span class=\"p\">{<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Loading<\/span><span class=\"dl\">'<\/span> <span class=\"p\">}<\/span>\n  <span class=\"o\">|<\/span> <span class=\"p\">{<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Failure<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"nx\">E<\/span> <span class=\"p\">}<\/span> \n  <span class=\"o\">|<\/span> <span class=\"p\">{<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Success<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"na\">payload<\/span><span class=\"p\">:<\/span> <span class=\"nx\">P<\/span> <span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This typing explicits our targeted benefits. The request can\u2019t be at the same\ntime loading and with an error.<\/p>\n\n<p>Working with such a model is both easy and secure: we\u2019re forced to handle each\nstate in order to eventually access the one with data, and we\u2019re limited to the\n4 states defined earlier:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">Component<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">request<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useMyRequest<\/span><span class=\"p\">()<\/span>\n\n  <span class=\"k\">switch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">NotAsked<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">NotAskedScreen<\/span> <span class=\"p\">\/&gt;<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Loading<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">LoadingScreen<\/span> <span class=\"p\">\/&gt;<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Failure<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">ErrorScreen<\/span> <span class=\"na\">error<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">error<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Success<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">SuccessScreen<\/span> <span class=\"na\">data<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">payload<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We have covered all valid states and no impossible ones. The developper writing\nthe <code class=\"language-plaintext highlighter-rouge\">useMyRequest<\/code> hook has 4 less tests to write: the typing of the hook itself\nenforces the output can ever only be these four cases. Testing it further would\nbe testing the language itself. We have scoped our code properly. Developers\nusing the hook also have 4 less tests to write as a result. Everyone is happy\nand can go home early. Good work!<\/p>\n\n<h2 id=\"using-this-mindset-at-scale\">Using this mindset at scale<\/h2>\n\n<p>There are tools available to help us write code focused on producing no impossible states.<\/p>\n\n<h3 id=\"typings\">Typings<\/h3>\n\n<p>Using a typed language of course helps a lot. Though they are not mandatory,\nthey certainly help developers avoid blind spots in their code. It\u2019s the\nlow-hanging fruit of code quality.<\/p>\n\n<p>When we write this\u2026<\/p>\n\n<div class=\"language-typescript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">someNumber<\/span><span class=\"p\">:<\/span> <span class=\"kr\">number<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">2<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>\u2026we\u2019re using the language to help remind ourself that valid states for\n<code class=\"language-plaintext highlighter-rouge\">someNumber<\/code> are all the numbers. Don\u2019t put a <code class=\"language-plaintext highlighter-rouge\">string<\/code> in there.<\/p>\n\n<p>The challenge is to push this simple idea as far as we can, not just using what\nthe language offers (low-level types) but typing higher level code using the\nsame basic principle.<\/p>\n\n<p>As we\u2019ve seen, exploiting typed languages save on testing time. If the language\nitself makes it impossible for <code class=\"language-plaintext highlighter-rouge\">2+2<\/code> to ever equals anything but <code class=\"language-plaintext highlighter-rouge\">4<\/code>, why would\nwe need to double check? What our code can and cannot do is no longer only our\nresponsibility as developers, it is now also woven within the very fabric of it.<\/p>\n\n<h3 id=\"theory\">Theory<\/h3>\n\n<p>Mathematics, type theory and functional programming also help. We should\nespecially be interested by <a href=\"https:\/\/en.wikipedia.org\/wiki\/Algebraic_data_type\">Algebraic Data\nType<\/a> (ADT) and more\nprecisely <em>sum types<\/em>.<\/p>\n\n<p>Sum types are types where the value must be one of a <em>fixed<\/em> set of options.<sup id=\"fnref:1\" role=\"doc-noteref\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\">1<\/a><\/sup>\nThe <code class=\"language-plaintext highlighter-rouge\">RemoteData<\/code> type we wrote is a sum type.<\/p>\n\n<p>Libraries that leverage those concepts and functional programming are strong\nallies. The Typescript ecosystem has seen the addition of a lot of libraries in\nthat space recently.<\/p>\n\n<p>Don\u2019t bother writing the above <code class=\"language-plaintext highlighter-rouge\">RemoteData<\/code> stuff by hand.\n<a href=\"https:\/\/github.com\/devexperts\/remote-data-ts\">A library<\/a> does just that.\n<a href=\"https:\/\/github.com\/gcanti\/fp-ts\"><code class=\"language-plaintext highlighter-rouge\">fp-ts<\/code> also offer a lot of ADTs<\/a> to model\nwhat\u2019s going on in our codebase like having <em>either<\/em> something or something else\n(Either) or maybe having something or not having it (Option).<\/p>\n\n<p>The thing is, our applications are not that different. If you are faced with a\nfeature and ponders on its modelling as code, chances are a tool has been made\nto help you do just that.<\/p>\n\n<h3 id=\"design\">Design<\/h3>\n\n<p>Last but not least, our brains! As software engineers, it\u2019s sometimes tempting\nto rush into writing code without having analysed what we should represent and\nwhich valid states we should support.<\/p>\n\n<p>It\u2019s not just about using technical concepts such as <code class=\"language-plaintext highlighter-rouge\">RemoteData<\/code>, <code class=\"language-plaintext highlighter-rouge\">Option<\/code> or\n<code class=\"language-plaintext highlighter-rouge\">Either<\/code>. It is first and foremost about using the right mindset to represent\nthe high level states the codebase should be in.<\/p>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n\n<p>Does it mean we should stop writing tests? Certainly not. Tests are absolutly\nnecessary to prevent our codebases to fail.<\/p>\n\n<p>But remember, we need tests because our code can fail. Reducing the number of\nway it can fail should be our priority. We can then keep writing tests for\nthe few remaining chances of failure.<\/p>\n\n<p>The best test is the test you don\u2019t have to write.<\/p>\n\n<hr \/>\n\n<p>Thanks to <a href=\"https:\/\/www.benrajalu.net\/\">Benoit Rajalu<\/a> and <a href=\"https:\/\/twitter.com\/nicomaligne\">Nicolas\nMaligne<\/a> for reading drafts of this.<\/p>\n\n<p>Illustration made by <a href=\"http:\/\/Bulbman.art\">Bulbman.art<\/a>.<\/p>\n\n<hr \/>\n\n<div class=\"footnotes\" role=\"doc-endnotes\">\n  <ol>\n    <li id=\"fn:1\" role=\"doc-endnote\">\n\n      <p>This post, <a href=\"https:\/\/jrsinclair.com\/articles\/2019\/algebraic-data-types-what-i-wish-someone-had-explained-about-functional-programming\/\">Algebraic Data Types: Things I wish someone had explained about\nfunctional\nprogramming<\/a>,\nexplains what ADTs are and the difference between product and sum types.<\/p>\n\n      <p>The first <code class=\"language-plaintext highlighter-rouge\">useMyRequest<\/code> returned value was a product type.\u00a0<a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#8617;<\/a><\/p>\n    <\/li>\n  <\/ol>\n<\/div>\n","pubDate":"Mon, 22 Feb 2021 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-02-22-the-best-test-is-the-test-you-dont-have-to-write","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-02-22-the-best-test-is-the-test-you-dont-have-to-write"},{"title":"Design System and Crazyness","description":"<p>Last week my ex-colleague and friend <a href=\"https:\/\/www.benrajalu.net\/\">Benoit Rajalu<\/a>\nshared a post called \u201cIs it really a design system?\u201d in which he lists the\ndifferent reasons why the shiny project you call a \u201cDesign System\u201d maybe isn\u2019t\nreally one, but just a collection of bad components.<\/p>\n\n<p>You\u2019re <em>not<\/em> building a Design System when designers and developers are not\nusing the same language and components, when you have no documentation except a\nStorybook or few comments on a Figma file, when designers reinvent the wheel in\neach project and developers code single-use ultra-specific components. You\u2019re\nmost certainly not building a Design System when there is all that and nothing\nis attempted to fix it.<\/p>\n\n<p>And I strongly agree with this.<\/p>\n\n<p>I formulated a strong opinion on the situation thanks to the powerful schema his\narticle ends with.<\/p>\n\n<p><img src=\"\/assets\/img\/design-system-schizophrenia-diagram.png\" alt=\"A diagram showing on one side an opposition between code and design, and on\nthe other a cycle between features and a Design\nSystem\" \/><\/p>\n\n<p>It shows that setting-up a Design System for your organization means switching\nfrom the traditional \u201cdesigners vs developers\u201d mindset (left) to a Product\nmindset (right).<\/p>\n\n<p>The target is to have a <em>team<\/em> that owns a <em>product<\/em> (the DS and its artifacts).\nA product that has <em>customers<\/em> (your other teams).<\/p>\n\n<p>I have to admit that I am not seeing a lot of Design System <em>teams<\/em> in companies\nso far. I see Design System <em>projects<\/em>, for sure. It\u2019s very trendy these days.\nThey are mainly driven by the base of the organization, ie. some designers\nand\/or developers. They work on it on weekends. They come early in the morning\nor they leave late at night to work for the project without slowing down\nproduction for their \u201creal\u201d job, shipping features.<\/p>\n\n<p>The organization\u2019s leadership may eventually enter the game, especially if the\nproject begins to see some traction (as it will because, turns out, it\u2019s a\npowerful concept) and if the initial volunteers haven\u2019t burned out yet.\nLeadership declares: \u201cWe have a Design System! It\u2019s so good! Thanks so and so\u201d.\nA quick mention of it is made in a blog post or a corporate presentation,\nshowing the world how their Design System revolutionized the company.\nMeanwhile, nothing has actually changed inside.<\/p>\n\n<p>Nothing has changed because the leadership has not embraced that a Design System\nis a product, and nurturing a product requires a dedicated team.<\/p>\n\n<p>Is such a thing a pipe dream? It should not be, we are already used to this\nparadigm in our engineering departments. We have infrastructure or platform\nteams that cater to the other units, providing a solid platform to run their\nservices. We have engineering productivity teams that help others with day to\nday tools (CI, testing environment, etc.). We have analysts and UX researchers\nto support the decision-making of other teams with bespoke data. They are all\n\u201cin-house\u201d services teams.<\/p>\n\n<p>Organizations willingly invest in all of those teams but can\u2019t form one for the\nDesign System? What\u2019s the problem?<\/p>\n\n<p>Is it the idea of mixing designers and developers together as one unit? How is\nit different from what we do for feature teams, squads, swarms or whatever name\nyou give to multidisciplinary teams, those working on features?<\/p>\n\n<p>Is it an org chart issue? Where it\u2019s unclear whether such a team reports to the\nVP of Product or to the VP of Engineering? Why would that matter? Aren\u2019t they\nboth after the same ultimate company goal?<\/p>\n\n<p>Is it because designers or developers are already understaffed? Well shoot,\nthat\u2019s precisely when a dedicated team would help: with a Design System they\nwill do more with less.<\/p>\n\n<p>Is it because nobody is here to lead this team? What about those who have\nmanaged to build the current Design System and do their day to day job <em>at the\nsame time<\/em>? Aren\u2019t they leadership enough?<\/p>\n\n<p>Is it because you don\u2019t believe in investing in such low levels teams for the\nbenefit of the rest of the organization? Then why keep talking about a Design\nSystem, and why keep investing in infrastructure teams, UX researchers and so on\nif you\u2019re against such an idea?<\/p>\n\n<p>All this questioning leads only to one conclusion: wanting to produce a Design\nSystem without updating your organization to support it will make your designers\nand developers go crazy.<\/p>\n\n<p>They have to work for their team\u2019s projects on the roadmap <em>while<\/em> fighting with\nmanagement over time budgets for Design System tasks. These are never the\npriority and the global roadmap has never enough room for them. But both\ndesigners and developers see their benefits, so they keep trying, and trying,\nand trying.<\/p>\n\n<p>They end up giving 100% for the Design System (because, you know, such a project\nis not an easy task) <em>while<\/em> they\u2019re asked to keep giving 100% on their\nregularly-scheduled features, knowing such a pace always ends poorly (yes,\nburnout. Or wry cynicism in the best of cases, go ask Benoit).<\/p>\n\n<p>They are often put in acrimonious situations with other colleagues <em>while<\/em>\ncontinuing to work with them on their features. This is unavoidable. The\nsituation doesn\u2019t actually make sense to anyone. The people invested in the\nDesign System will of course fight for it. Those who aren\u2019t as invested are\nfaced with an unsanctioned, \u201cbottom-up\u201d project, and some will therefore have no\nissue undermining that project. If it were so important, a team would have been\nformed around it, right? Right.<\/p>\n\n<p>They are left with two very opposite missions: they must keep focusing on the\nspecific needs of their individual teams <em>while simultaneously<\/em> creating a\nproduct, one that preaches the need to cater collaboratively to the necessities\nof every team. In the same work day, they may be tasked with creating design or\ncode in complete opposition with the goal of the System they defend.<\/p>\n\n<p>They also lack the proper communication structures. Nobody listens to them.\nNobody considers their requests. Nobody respects the rules the Design System\ncomes with because they have no authority. Their product has not been\nsanctioned, it is not materialized by a single, identifiable core team. It\u2019s\njust a bunch of people being mad at the void.<\/p>\n\n<p>They are mad because they have to work in constant questioning, constant\ntradeoff. They have to manage all those contradictions and conflicts by\nthemselves, not being afforded the same support and leadership other \u201cin-house\nservices\u201d teams have been given.<\/p>\n\n<p>They\u2019re going completely crazy.<\/p>\n\n<p>By wanting the company to produce a Design System without organizing around and\nfor it, we chart a clear path to craziness. After all, it is exactly to prevent\nthat that we usually create new teams with clear responsibilities. It\u2019s for\npeople to work efficiently towards one team goal. For conflicts and\ncontradictions to be resolved by teams speaking to each others, not by poisoning\neveryone\u2019s mind.<\/p>\n\n<p>I\u2019m not saying it\u2019s impossible to build a real Design System relying only on a\nfew charitable souls spread across teams. There are certainly successful\nexamples.<\/p>\n\n<p>However, it certainly is the hardest path. If the benefits of the Design System\ncome without change on your organization\u2019s part, without a dedicated team,\nsupportive leadership, ressources, time and headcount, then you can trust the\ncost is being paid by the employees. At the cost of their sanity, or their\ntrust, or their motivation.<\/p>\n\n<hr \/>\n\n<p>Thanks to Benoit Rajalu for helping me with drafts of this!<\/p>\n","pubDate":"Fri, 29 Jan 2021 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-01-29-design-system-crazy","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2021-01-29-design-system-crazy"},{"title":"Defining success for an engineering team","description":"<p><em>Looking back at the two years that made us challenge our engineering habits.<\/em><\/p>\n\n<p>In the coming weeks my team at iAdvize will be making public the first step\ntowards a complete overhaul of one of the company\u2019s key products. Like most\nstrategic parts of a 10 year old software suite, this app has had its fair share\nof stories to tell.<\/p>\n\n<p>Those stories could be of the scary kind. They were legacy. As the engineering\nteam working on its codebase, we were not confident that we had what it takes to\ndeliver evolutions (or basic maintenance). It is a classic software engineering\nissue.<\/p>\n\n<p>We reached this conclusion two years ago. We have since managed nevertheless to\ndeliver new features, improve old ones and map a new path that let us overhaul\nthe app significantly. Did we do this following a carefully crafted plan? Not\nreally, not at first. But since we might have to do it again, let\u2019s go ahead and\ndocument what made it work.<\/p>\n\n<p>Let us show you how we learnt to fight against uncertainty with tools and\nmethods, how we cultivated certainty by writing things down, by reasoning in\n\u201cfirst principles\u201d, by building architectural, technical and human leverage\nfocused on the long-term. Let us show you how we became a Product team and why\nit worked. Let\u2019s reverse-engineer this success.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration of glasses \" src=\"\/assets\/img\/defining-success-6.png\" \/>\n<\/figure>\n\n<h1 id=\"two-years-ago-in-a-galaxy-far-far-away\">Two years ago, in a galaxy far far away<\/h1>\n\n<p>The story starts around an old and complex iAdvize frontend application. It was\nnot really abandoned, but it was not really under control either.<\/p>\n\n<p>It was the Big Legacy App\u2122.<\/p>\n\n<p>Teams were scared of it. Some of its features were built when we were still\nmaturing as engineers, unsure of how we should build our stack. Some had to be\nshipped fast and single-handedly by developers now long gone. Few of the current\ndevs in our team had first-hand experience with the code.<\/p>\n\n<p>Its UX was poor partly because its developer experience was bad. There were bugs\nand performance or accessibility issues. Maintaining the codebase and solving\nthese issues was difficult: it was a mix of spaghetti code here and there, a\nstrange soup of paradigms and libraries, of bad naming and no real separation of\nconcern.<\/p>\n\n<p>It left everyone frustrated. Developers had to apply patches for obscure bugs or\nadd new features precariously relying on dodgy code while the product team\ndidn\u2019t really understand why everything took so much time with this app.<\/p>\n\n<p>Something had to be done. Nearly two years ago, management decided to form a new\nteam dedicated to the Big Legacy App. At first, two front-end developers rallied\naround one product manager. Two more developers have joined in since then.<\/p>\n\n<p>The mission was obvious, we needed to deliver a product that serves its users\nwell and that can meet their evolving needs without friction, a product that\ncould grow with new business requirements alongside the rest of the iAdvize\nsolution. An obvious mission does not make it an easy one. Soon we realised we\nhad to change our approach. We needed an engineering strategy.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration of a man \" src=\"\/assets\/img\/defining-success-1.png\" \/>\n<\/figure>\n\n<h1 id=\"not-everything-is-uncertain-in-the-world\">Not everything is uncertain in the world<\/h1>\n\n<p>Let\u2019s face it: as software engineers it is easy to work on autopilot on such a\ncodebase. Patching bugs after bugs, adding features on top of legacy features.\n\u201cShip ship ship!\u201d and <em>voil\u00e0<\/em>, job done.<\/p>\n\n<p><strong>Like many, we were used to this autopilot mode but we started seeing it as a\ndangerous mindset.<\/strong> <strong>Unlike some, we decided it is not the nominal condition\nof software engineering.<\/strong> We refused to see ourselves as mercenaries.<\/p>\n\n<p>Autopilot is an approach rooted in the present. It assumes the future is\nuncertain and the past not worth digging up. But is it true? It kept putting us\nin a \u201cnot worse, not better\u201d situation. Maybe we could succeed at not making the\ncodebase worse, but we could not make it better along the way. We were\npreserving an uncomfortable status quo.<\/p>\n\n<p>Is it so unrealistic to plan for the big picture? To specify how things should\ncombine and work together? We wanted to change our mindset.<\/p>\n\n<p>The turning point for us has been realizing that changing things does not mean\nthat everything has to be thrown away, even in this scary Big Legacy App. We\nfound that some things do not change often: our core business, the features we\nprovide, the APIs we use, the data we manipulate, the UI we have to implement,\netc. It is the product\u2019s vision and its value. Once isolated from the uneasy fog\nof the legacy, it becomes solid ground. It is a rich soil to cultivate on.<\/p>\n\n<p>In other words, we saw two kinds of things in this codebase: the uncertain\nthings that must be fought and the certain things that must be cared for.<\/p>\n\n<h2 id=\"the-uncertain-things-that-must-be-fought\">The uncertain things that must be fought<\/h2>\n\n<p>The bugs, the spaghetti code, the unpredicatble module left by a long gone\nengineer, the undocumented black box, the 4-year old fork of a famous library to\nfix a bug in a hurry, etc.<\/p>\n\n<p>We have to fight them intelligently. That is the part we must identify and\neventually remove.<\/p>\n\n<h2 id=\"the-certain-things-that-must-be-cared-for\">The certain things that must be cared for<\/h2>\n\n<p>The business rules and their edge cases, the workflows, the API calls, the data\nwe use. The well documented and reliable external library. The software\narchitecture books that inspire us.<\/p>\n\n<p><strong>The knowledge of this capital can be lost, spread out in the codebase or\nforgotten because some developers have left.<\/strong> Its usefulness however is always\nobvious and it is therefore often rediscovered on the go.<\/p>\n\n<p>Such certainty has to be handled with care: it is what is precious about the\nproduct and the team that builds it. It should be curated, organised and\ndocumented so it can endure.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration of hands \" src=\"\/assets\/img\/defining-success-2.png\" \/>\n<\/figure>\n\n<h1 id=\"fighting-the-uncertainty\">Fighting the uncertainty<\/h1>\n\n<p>There are two levels of uncertainty. The first one is the codebase itself and\nits behavior. Code that is hard to read, buggy features, undocumented or\nhard-to-understand librairies or APIs. That is the easy part\u2026 of the hard part.<\/p>\n\n<p>The second one is more tricky. <strong>It is the uncertainty in ourselves<\/strong>. It is\nwhat happens when we are tired and forget about some edge cases. It is when we\nhave to accomodate with colleagues with different mindsets or motivations or\nwhen we are simply wrong in our assumptions or lacking some key concepts.<\/p>\n\n<p>Uncertainty can be created on purpose or accidentally when a team is mismanaged.\nOne can undermine a team\u2019s attempt at focusing on the product rather than the\nproject, citing a need for immediacy or arguing that there could be <a href=\"https:\/\/martinfowler.com\/articles\/is-quality-worth-cost.html\">such a\nthing as \u201ctoo much\nquality\u201d<\/a>.<\/p>\n\n<p>To fight these uncertainties we progressively added specific tools and methods.<\/p>\n\n<p>We committed to not write Javascript anymore by switching to Typescript,\nstrongly tied with a functional programming approach (with\n<a href=\"https:\/\/gcanti.github.io\/fp-ts\">fp-ts<\/a>). This helps manage the intrinsic\nuncertainty of the language itself and the data we manipulate. We now try to\nleverage our new language \/ paradigm combo to write the most robust code we can.\nWe wrote an article, <a href=\"https:\/\/medium.com\/iadvize-engineering\/how-to-model-your-entities-with-opaque-and-sum-types-in-typescript-round-2-a3ca7a474773\">how we model our entities with opaque and sum types in\nTypescript<\/a>,\nthat gives an example of just that.<\/p>\n\n<p>We also invested in a good darklaunch tool and decided to always use it to\ndeploy new big features. This helps manage our partial understanding of the\nbusiness rules and client usage of our product and balance this uncertainty with\nthe need of shipping new features. The progressive rollouts and the impact they\nhad on the codebase (isolation of the old code, atomic deploys) allowed us to\ngain confidence and velocity. We wrote <a href=\"https:\/\/medium.com\/iadvize-engineering\/feature-flags-strategy-iadvize-fd2f993d177b\">a full article on\nthis<\/a>\nas well.<\/p>\n\n<p>Sometimes, something new has to be implemented: a new <a href=\"https:\/\/github.com\/iadvize\/store-library\">low-level\ntool<\/a>, a complete revision of a\ncomplex component or a brand new UI for example. It is impossible to produce a\ngreat solution instantly so we have regularly benchmarked external libraries,\nuser-tested things or built Proof Of Concepts (POCs).<\/p>\n\n<p>UI POCs happened to be extremely useful for us when working in pair with our\ndesigner on a complete interface revamp. Static mock-ups were not enough: we had\nto find a way to validate the relevance of the new UI in the browser, assessing\nits technical feasibility before jumping on the hard and long work of\nimplementing it in our still-legacy codebase. Think of this as lifting the fog\nover a perilous valley before marching on.<\/p>\n\n<p>Our approach to testing also completely changed. Two years ago our mantra was\n\u201cunit-test all the things\u201d, from the Redux action type to the most complex\nfunction. This was a doomed endeavour from the start, given our situation.<\/p>\n\n<p><strong>As a team with scarce resources in both time and devs, we have to choose\ncarefully what we do.<\/strong> Having switched languages and paradigm helped us\ndrastically reduce the number of unit-tests needed without compromising on the\nquality of our work. We switched to visual snapshots when appropriate and\nend-to-end testing that are in certain situations way <a href=\"https:\/\/increment.com\/testing\/designing-automated-tests-for-react\/\">more\nrelevant<\/a>.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration a race \" src=\"\/assets\/img\/defining-success-3.png\" \/>\n<\/figure>\n\n<h1 id=\"cultivating-the-certainty\">Cultivating the certainty<\/h1>\n\n<p>The problem with certainty is how easily it leads to overconfidence. You\u2019re\nreally confident about this new project the PM has presented and you think\nyou\u2019ve understood all the constraints of a library by reading its README. Then\nthe moment you have to write code for this project in your app, you discover a\nnew level of complexity. This happened a lot with our legacy app.<\/p>\n\n<p>Then there is the time factor. As time goes, certainty about things can decrease\nquickly. It is what happens when people come and go with their knowledge of the\nbusiness rules. Not only people, but code obviously changes over time. Combine\nthe two, and you get that feeling we feel coming back from a long vacation and\nwe can\u2019t fully understand our code anymore. Certainty should be cultivated with\ncare.<\/p>\n\n<p>To do just this we introduced a new method: we started to <a href=\"https:\/\/increment.com\/teams\/code-less-engineer-more\/\">plan seriously before\nwe do<\/a>. We don\u2019t want to\ndiscover something fundamental at the 90% build milestone on a project anymore.\nWe used to only give some clues on how to implement things in Jira tickets. Now\nwe dedicate 30% to 50% of the project time writing <em>thorough<\/em> <em>specs<\/em> (see also\n<a href=\"https:\/\/www.industrialempathy.com\/posts\/design-docs-at-google\/\">Google Design\nDocs<\/a> as\ninspiration). This helps a lot in creating strong certainty about what we are\ndoing. No more last-minute surprise that challenges a whole project. And when\nyou come back from your hard-earned vacation, you have ample documentation to\nremind you what\u2019s what.<\/p>\n\n<p>This pattern soon became central to our team. We now write things down. We write\nspecs, as we\u2019ve seen, but we don\u2019t stop at them. We use\n<a href=\"https:\/\/github.com\/joelparkerhenderson\/architecture_decision_record\">ADRs<\/a> or\nRFCs to keep track of decisions. We try to write business knowledge directly <em>as\ncode<\/em> (we call this part of the codebase the <em>domain<\/em>). We write APIs contracts\nwith parsers like <a href=\"https:\/\/github.com\/gcanti\/io-ts\">io-ts<\/a>.<\/p>\n\n<p><strong>The goal is not to set things in stone but to extract the knowledge from\npeople\u2019s brains.<\/strong> We want to refer to it and challenge it constructively in the\nlong term. With this we can preserve and share the certainty we have on a\nsubject for a long time.<\/p>\n\n<p>Certainty is not just about our confidence in our codebase. It is also about our\nunderstanding of classic computer science subjects and our knowledge of the\nfrontend ecosystem. It is a little bit about not trusting our weak human brains\nas well. Unconsciously, we regularly \u201cborrowed\u201d certainty from others by reading\narticles, books, watching conferences, trying new languages. It is not so much\nabout introducing new tools in our codebase as it is about learning new concepts\nthat will inspire us and help us reason from <a href=\"https:\/\/fs.blog\/2018\/04\/first-principles\">first\nprinciples<\/a>.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration books seen as flowers \" src=\"\/assets\/img\/defining-success-4.png\" \/>\n<\/figure>\n\n<h1 id=\"building-leverage\">Building leverage<\/h1>\n\n<p>We recently understood that our process can be summarized as trying to work in\n<a href=\"https:\/\/martinfowler.com\/articles\/products-over-projects.html\">Product-mode as opposed to\nProject-mode<\/a>.\nOur goal has been to consolidate a durable team around our specific product. We\nthink it\u2019s a powerful mindset to take back control of a legacy codebase such as\nours. It creates actionable knowledge and the right dynamic to motivate people.<\/p>\n\n<p>Once we got the hang of having a long term mindset we understood that\n<strong>everything is about\n<a href=\"https:\/\/www.value.app\/feed\/the-age-of-infinite-leverage\">leverage<\/a><\/strong>: investing\nstrategically in things that will \u201cmaximize the output of our work\u201d in the long\nterm.<\/p>\n\n<p>There is architecture leverage. The team needs a mental framework to put things\nin the right place in the codebase, handle uncertainty properly and dissect new\nprojects correctly.<\/p>\n\n<p>We now use <a href=\"https:\/\/www.oreilly.com\/library\/view\/domain-driven-design-tackling\/0321125215\/\">Domain Driven\nDesign<\/a>\nfor that. It is our structural framework, helping us handle the legacy code and\nthe external actors around the app. All our specs are now written through the\nprism of the infrastructure \/ application \/ domain \/ view layers.<\/p>\n\n<p>There is also <a href=\"https:\/\/lethain.com\/building-technical-leverage\/\">technical\nleverage<\/a>. We invested time in\nautomation (great CI and continuous deployment with end-to-end tests embedded),\nin the Typescript \/ functional programming approach that kind of automates part\nof the work we would manually do writing standard Javascript. We also set-up\nbots that rebase and merge Pull Requests automatically or alert us of production\nerrors or deployments.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration of a wall \" src=\"\/assets\/img\/defining-success-5.png\" \/>\n<\/figure>\n\n<p>Last but not least, there is human leverage. Since we are a small team we don\u2019t\nfeel the need for formal processes but there are nevertheless key takeaways.<\/p>\n\n<p>We are lucky to have a diversity of frontend developers in the team, in both\nskills and experiences. Some are UI and design systems experts, some are more\ninto codebase architecture. Some are really invested in leveraging Typescript\nwhile others are more into agile processes and team building. Some of us are\njuniors fresh from school, others are seniors with multiple past experiences.\nThanks to this diversity everyone has something to learn and we challenge each\nother\u2019s work constructively.<\/p>\n\n<p>We also put attention in cultivating empathy for one another in our day to day\ninteractions, like during <a href=\"https:\/\/mtlynch.io\/human-code-reviews-1\/\">code\nreviews<\/a>. We are not robots.<\/p>\n\n<p>And finally, we try not to make mountains out of molehills but to focus and\ncelebrate things that matter instead. We\u2019ve been confined and separated from\neach other because of COVID-19 for a few months now. Is it the end of the world?\nProbably not! The relationships we have formed inside our team are not about the\nsuperficiality of sharing the same open-space for 8 hours a day or drinking a\nbeer every week with the rest of the company. It is about our common mission,\nabout freely sharing our feelings on a project. It is promoting transparency,\ngiving praise and celebrating team milestones together even remotely.<\/p>\n\n<p>Now that our <em>Product mode<\/em> team is formed and sticks to an engineering\nstrategy, <strong>projects are seen as opportunities<\/strong>, small steps towards the\ntechnical vision, even if we have to sometimes \u201chack\u201d the roadmap the management\nwants us to follow. Projects are not isolated periods of time decoralated from\nprevious and future work, they are part of the journey.<\/p>\n\n<p>This <a href=\"https:\/\/www.oreilly.com\/library\/view\/the-pragmatic-programmer\/9780135956977\/\">pragmatic\nmindset<\/a>\ndoesn\u2019t prevent the team from doing the easy things early, but we plan for the\nhard things even earlier. Thanks to that, everything is seen through a solid\ntechnical vision and we can actually move faster. Old parts are isolated\ncorrectly and are not obstacles for new code anymore.<\/p>\n\n<p>When the time comes for us to rework a legacy part of the codebase, it is not so\nmuch about refactoring than it is about shipping a new version (with darklaunch,\nof course!) that will enable new usages or consolidate current ones. What would\nyou do if you had a lousy, heavy bike with a rusted frame, no wheels and no\nbrakes but were asked to run the Tour de France? You would assess your budget.\nIf you find it more costly or inefficient to repair the broken bike than it\nwould be to build a light and speedy new one, you would build a new one. We\u2019re\nsure that PMs, managers and businesses understand this mindset.<\/p>\n\n<p>And because everything is about the long term, <strong>patience is key<\/strong>. It is a\nmarathon, not a sprint, and from time to time everyone must zoom out and\nremember the road ahead.<\/p>\n\n<h1 id=\"where-are-we-now\">Where are we now?<\/h1>\n\n<p>This is the team\u2019s vision:<\/p>\n\n<ol>\n  <li><strong>Fighting the uncertainty<\/strong> with tools and methods: languages and paradigms,\ndeployments strategies, POCs, smart tests, \u2026<\/li>\n  <li><strong>Cultivating certainty<\/strong> by shutting down the autopilot and dedicating time for\nthorough specs, writing all things down, and being curious about new concepts.<\/li>\n  <li><strong>Maturing as a Product-mode team<\/strong> that builds <em>leverage<\/em> by looking for the\nright mental framework to think about its codebase, investing in technology and\npeople.<\/li>\n<\/ol>\n\n<p>Is it to say that we\u2019ve succeeded at all of this and we don\u2019t have to be\ncautious about it anymore? No. It is a day to day fight against our tendency to\nsee everything with a short term vision. It is tempting to work in\n<em>Project-mode<\/em> but we know deep inside that without <a href=\"https:\/\/lethain.com\/good-engineering-strategy-is-boring\/\">a coherent engineering\nstrategy<\/a> it will be\nvery pricey in the long run.<\/p>\n\n<p>Of course we cannot design everything at once and once for all. Having an\nengineering strategy is not about that. It is more about collecting tools,\nconcepts and methods, having \u201cdesigned the way we design things\u201d as <a href=\"https:\/\/www.youtube.com\/watch?v=_ahvzDzKdB0&amp;app=desktop\">Guy Steele\nexplains<\/a>, and\nleveraging all of that.<\/p>\n\n<p><strong>There is more to the software engineering job than producing code\ncontinuously<\/strong>. It is also about designing solutions, planning a strategy,\nmentoring people and building relationships: all of which help tremendously with\nthe coding part.<\/p>\n\n<figure>\n  <img alt=\"Abstract illustration of people seen as flowers \" src=\"\/assets\/img\/defining-success-7.png\" \/>\n<\/figure>\n\n<p><br \/><\/p>\n\n<hr \/>\n\n<p>This article has been written with the fantastic help of Benoit Rajalu.<\/p>\n\n<p>Thanks also to the rest of the team \u2014 Victor Graffard and Nicolas Maligne \u2014 as\nto Fhenon De Urioste, Anthony Griffon, Pierre-Alexandre Gury, Nicolas Declercq,\nWandrille Verlut and Arno Baudu for reading drafts of this.<\/p>\n\n<h1 id=\"resources-for-inspiration\">Resources for inspiration<\/h1>\n\n<p>On software engineering in general:<\/p>\n\n<ul>\n  <li><a href=\"https:\/\/www.oreilly.com\/library\/view\/the-pragmatic-programmer\/9780135956977\/\">The Pragmatic Programmer: your journey to mastery, 2nd\nEdition<\/a>\nby David Thomas, Andrew Hunt<\/li>\n  <li><a href=\"https:\/\/staffeng.com\/\">Staffeng.com<\/a> and <a href=\"https:\/\/lethain.com\/\">lethain.com<\/a> by\nWill Larson<\/li>\n  <li><a href=\"https:\/\/martinfowler.com\/articles\/is-quality-worth-cost.html\">Is High Quality Software Worth the\nCost?<\/a> by Martin\nFowler and all <a href=\"http:\/\/martinfowler.com\">martinfowler.com<\/a> in general<\/li>\n  <li><a href=\"https:\/\/www.youtube.com\/watch?v=f84n5oFoZBc&amp;feature=youtu.be\">Hammock Driven\nDevelopment<\/a> by\nRich Hickey<\/li>\n  <li><a href=\"https:\/\/justinmeiners.github.io\/think-in-math\/\">Think in Math, Write in Code<\/a>\nby Justin Meiners<\/li>\n  <li>The <a href=\"https:\/\/increment.com\">Increment magazine<\/a> from Stripe<\/li>\n<\/ul>\n\n<p>On software architecture:<\/p>\n\n<ul>\n  <li><a href=\"https:\/\/www.oreilly.com\/library\/view\/domain-driven-design-tackling\/0321125215\/\">Domain-Driven Design: Tackling Complexity in the Heart of\nSoftware<\/a>\nby Eric Evans<\/li>\n  <li>Rich Hickey with <a href=\"https:\/\/www.youtube.com\/watch?v=MCZ3YgeEUPg\">Design, Composition and\nPerformance<\/a>, <a href=\"https:\/\/www.infoq.com\/presentations\/Simple-Made-Easy\/\">Simple Made\nEasy<\/a><\/li>\n  <li><a href=\"https:\/\/www.intercom.com\/blog\/run-less-software\/\">Run Less Software<\/a> by Rich\nArchbold<\/li>\n<\/ul>\n\n<p>On tools and concepts:<\/p>\n\n<ul>\n  <li><a href=\"https:\/\/twitter.com\/GiulioCanti\">Giulio Canti<\/a> for fp-ts, io-ts and others\nfunctional programming libraries<\/li>\n  <li>The <a href=\"https:\/\/github.com\/MostlyAdequate\/mostly-adequate-guide\">Mostly adequate guide to\nFP<\/a><\/li>\n  <li>Rich Hickey again with <a href=\"https:\/\/www.youtube.com\/watch?v=YR5WdGrpoug\">Maybe Not<\/a>,\n<a href=\"https:\/\/www.infoq.com\/presentations\/Are-We-There-Yet-Rich-Hickey\/\">Are We There\nYet?<\/a>,<\/li>\n  <li><a href=\"https:\/\/martinfowler.com\/articles\/feature-toggles.html\">Features Toggles<\/a> by\nPete Hodgson<\/li>\n<\/ul>\n\n<p>On management and remote:<\/p>\n\n<ul>\n  <li>The Basecamp crew for <a href=\"https:\/\/basecamp.com\/books\/rework\">Rework<\/a>, <a href=\"https:\/\/basecamp.com\/books\/calm\">It Doesn\u2019t\nHave to Be Crazy at Work<\/a> and\n<a href=\"https:\/\/basecamp.com\/shapeup\">ShapeUp<\/a><\/li>\n  <li><a href=\"https:\/\/www.oreilly.com\/library\/view\/the-extraordinary-coach\/9780071703406\/\">The Extraordinary Coach: How the Best Leaders Help Others\nGrow<\/a>\nby John Zenger and Kathleen Stinnett<\/li>\n  <li><a href=\"https:\/\/forge.medium.com\/its-not-enough-to-be-right-you-also-have-to-be-kind-b8814111fe1\">It\u2019s Not Enough to Be Right \u2014 You Also Have to Be\nKind<\/a>\nby Ryan Holiday<\/li>\n  <li><a href=\"https:\/\/pulseasync.com\/operators\/future-remote-working\/\">Context over control: the future of remote\nwork<\/a> by Leonardo\nFederico<\/li>\n<\/ul>\n\n<p>Illustrations by <a href=\"http:\/\/absurd.design\">absurd.design<\/a><\/p>\n","pubDate":"Tue, 01 Dec 2020 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-12-01-defining-success-engineering-team","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-12-01-defining-success-engineering-team"},{"title":"State and Store in frontend codebases","description":"<p><em>This post was <a href=\"https:\/\/medium.com\/iadvize-engineering\/state-and-store-in-frontend-codebases-68691d5f28bf\">originally published on Medium<\/a>.<\/em><\/p>\n\n<p><em>Taking the time to separate the State, the Store and the Workflow in frontend codebase.<\/em><\/p>\n\n<p>For the last two years my team has been continuously refactoring a large legacy app. We learnt a lot of things in the process but one of the most important tools we found was having a new outlook on two core front-end concepts: the State and the Store.<\/p>\n\n<p>It turned out that cutting problems into State, Store and \u2014 as we will see \u2014 Workflow \u201cbits\u201d really helped us deliver a better architecture, maintain the codebase in the long term and choose the right libraries to build upon.<\/p>\n\n<p>Let us show you why. We will take you through the same journey we did, first by looking at the state management libraries we sometimes blindly use. Then we\u2019ll deconstruct the core concepts and highlight how asking the right questions about them lead to demonstrable benefits.<\/p>\n\n<h1 id=\"state-management-libraries\">State management libraries<\/h1>\n\n<p>There are a lot of \u201cstate management\u201d libraries built for frontend codebases. We inherited Redux and tried to stick with it at first, but it soon became apparent it was not helping us get the job done.<\/p>\n\n<h2 id=\"a-common-redux-situation\">A common Redux situation<\/h2>\n\n<p>Imagine an app dealing with users.<\/p>\n\n<p>There are some actions creators, for example:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">fetchUsers<\/span> <span class=\"o\">=<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS<\/span><span class=\"dl\">\"<\/span>\n<span class=\"p\">});<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">fetchUsersFailed<\/span> <span class=\"o\">=<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS_FAILED<\/span><span class=\"dl\">\"<\/span>\n<span class=\"p\">});<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">fetchUsersSucceeded<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">users<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS_SUCCEEDED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">payload<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">users<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">picture<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">payload<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"nb\">Math<\/span><span class=\"p\">.<\/span><span class=\"nx\">round<\/span><span class=\"p\">(<\/span><span class=\"nb\">Math<\/span><span class=\"p\">.<\/span><span class=\"nx\">random<\/span><span class=\"p\">()<\/span> <span class=\"o\">*<\/span> <span class=\"mi\">1000000<\/span><span class=\"p\">),<\/span>\n    <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">picture<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">});<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUserFailed<\/span> <span class=\"o\">=<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER_FAILED<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">});<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUserSucceeded<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">payload<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n  <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER_SUCCEEDED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">payload<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"na\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">payload<\/span> <span class=\"p\">}<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We have to handle some actions in a reducer.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">initialState<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">loading<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span> <span class=\"na\">users<\/span><span class=\"p\">:<\/span> <span class=\"p\">[],<\/span> <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"kc\">undefined<\/span> <span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">reducer<\/span><span class=\"p\">(<\/span><span class=\"nx\">state<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">initialState<\/span><span class=\"p\">,<\/span> <span class=\"nx\">action<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">({<\/span> <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"nx\">action<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"p\">,<\/span> <span class=\"nx\">action<\/span> <span class=\"p\">});<\/span>\n  <span class=\"k\">switch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">action<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS_SUCCEEDED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n        <span class=\"p\">...<\/span><span class=\"nx\">state<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">loading<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">users<\/span><span class=\"p\">:<\/span> <span class=\"nx\">action<\/span><span class=\"p\">.<\/span><span class=\"nx\">payload<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">.<\/span><span class=\"nx\">filter<\/span><span class=\"p\">(<\/span>\n          <span class=\"p\">(<\/span><span class=\"nx\">apiUser<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">apiUser<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span> <span class=\"o\">!==<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">ADMIN<\/span><span class=\"dl\">\"<\/span>\n        <span class=\"p\">)<\/span>\n      <span class=\"p\">};<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS_FAILED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n        <span class=\"p\">...<\/span><span class=\"nx\">state<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">loading<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"k\">new<\/span> <span class=\"nb\">Error<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">oops<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n      <span class=\"p\">};<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER_SUCCEEDED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n        <span class=\"p\">...<\/span><span class=\"nx\">state<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">users<\/span><span class=\"p\">:<\/span> <span class=\"p\">[...<\/span><span class=\"nx\">state<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">,<\/span> <span class=\"nx\">action<\/span><span class=\"p\">.<\/span><span class=\"nx\">payload<\/span><span class=\"p\">.<\/span><span class=\"nx\">user<\/span><span class=\"p\">]<\/span>\n      <span class=\"p\">};<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER_FAILED<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span>\n      <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n        <span class=\"p\">...<\/span><span class=\"nx\">state<\/span><span class=\"p\">,<\/span>\n        <span class=\"na\">error<\/span><span class=\"p\">:<\/span> <span class=\"k\">new<\/span> <span class=\"nb\">Error<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">oops<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n      <span class=\"p\">};<\/span>\n\n    <span class=\"nl\">default<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n      <span class=\"k\">return<\/span> <span class=\"nx\">state<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then in a middleware or two.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">fetchUsers<\/span><span class=\"p\">,<\/span> <span class=\"nx\">createUser<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/fake-fetch<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">fetchUsersSucceeded<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">fetchUsersFailed<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">createUserFailed<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">createUserSucceeded<\/span>\n<span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/actionCreators<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">middleware<\/span> <span class=\"o\">=<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">dispatch<\/span><span class=\"p\">,<\/span> <span class=\"nx\">getState<\/span> <span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nx\">next<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nx\">action<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">next<\/span><span class=\"p\">(<\/span><span class=\"nx\">action<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"k\">switch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">action<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">FETCH_USERS<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">fetchUsers<\/span><span class=\"p\">()<\/span>\n        <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">((<\/span><span class=\"nx\">payload<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">fetchUsersSucceeded<\/span><span class=\"p\">(<\/span><span class=\"nx\">payload<\/span><span class=\"p\">));<\/span>\n        <span class=\"p\">})<\/span>\n        <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">fetchUsersFailed<\/span><span class=\"p\">());<\/span>\n        <span class=\"p\">});<\/span>\n\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"k\">case<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">CREATE_USER<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n      <span class=\"kd\">const<\/span> <span class=\"nx\">currentUsers<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">getState<\/span><span class=\"p\">().<\/span><span class=\"nx\">users<\/span><span class=\"p\">;<\/span>\n\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">currentUsers<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"mi\">10<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">createUserFailed<\/span><span class=\"p\">());<\/span>\n      <span class=\"p\">}<\/span>\n\n      <span class=\"nx\">createUser<\/span><span class=\"p\">()<\/span>\n        <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">((<\/span><span class=\"nx\">payload<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">createUserSucceeded<\/span><span class=\"p\">(<\/span><span class=\"nx\">payload<\/span><span class=\"p\">));<\/span>\n        <span class=\"p\">})<\/span>\n        <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">createUserFailed<\/span><span class=\"p\">());<\/span>\n        <span class=\"p\">});<\/span>\n\n      <span class=\"k\">break<\/span><span class=\"p\">;<\/span>\n    <span class=\"p\">}<\/span>\n\n    <span class=\"nl\">default<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We need at least a selector:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">getUsers<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n  <span class=\"nx\">state<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">user<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\n    <span class=\"p\">...<\/span><span class=\"nx\">user<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">picture<\/span><span class=\"p\">:<\/span> <span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">picture<\/span> <span class=\"o\">||<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/i.pravatar.cc\/300 <\/span><span class=\"dl\">\"<\/span>\n  <span class=\"p\">}));<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>And a view:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"nx\">React<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">useDispatch<\/span><span class=\"p\">,<\/span> <span class=\"nx\">useSelector<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react-redux<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">getUsers<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/selectors<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createUser<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/actionCreators<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"nx\">Users<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/components\/Users<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"nx\">UserForm<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/components\/UserForm<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">dispatch<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useDispatch<\/span><span class=\"p\">();<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">users<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useSelector<\/span><span class=\"p\">(<\/span><span class=\"nx\">getUsers<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">({<\/span> <span class=\"nx\">users<\/span> <span class=\"p\">});<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">handleFormSubmission<\/span> <span class=\"o\">=<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">picture<\/span> <span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">firstName<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"nx\">lastName<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">dispatch<\/span><span class=\"p\">(<\/span><span class=\"nx\">createUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">picture<\/span><span class=\"p\">));<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">};<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nc\">Users<\/span> <span class=\"na\">users<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">users<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nc\">UserForm<\/span> <span class=\"na\">onSubmit<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">handleFormSubmission<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n    <span class=\"p\">&lt;\/&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>You can find a <a href=\"https:\/\/codesandbox.io\/s\/state-and-store-in-frontend-codebases-article-redux-example-gdlp0\">full Codesandbox<\/a> for this code.<\/p>\n\n<p>This is an illustration of Redux <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cruft\">cruft<\/a>. It\u2019s <a href=\"https:\/\/redux.js.org\/recipes\/reducing-boilerplate\">not a secret<\/a> that people complain about Redux and other Flux-inspired libraries boilerplate. There are lots of files (action creators, reducers, middlewares, selectors), especially when we use <code class=\"language-plaintext highlighter-rouge\">combineReducers<\/code>. It is hard to maintain in the long run from a strictly technical point of view.<\/p>\n\n<p>It also comes with a strong paradigm based on user events called \u201cactions\u201d. What if it does not fit the way you think of your app? In our case, the vast majority of the actions were in fact tied to a single handler (reducer or middleware). Why not use a simple function instead?<br \/>\nThe danger of using Redux by default in this situation is implementing things that don\u2019t fit our needs.<\/p>\n\n<p>On top of this, the fundamental issue for us was how hard it became to see the big picture of what we were implementing. Our business rules were spread out in the codebase. They were hard to centralize because of what Redux needs in order to work.<\/p>\n\n<p>In the example above, some rules are implemented in the view (a user must have both <code class=\"language-plaintext highlighter-rouge\">firstName<\/code> and <code class=\"language-plaintext highlighter-rouge\">lastName<\/code>), some in the action creator (the new user ID is a random 10 digit number), some in middlewares (you cannot create more than 10 users) or in selectors (users lacking an avatar get a random one). The process of creating a user is spread out. There is no single file that handles the whole thing from the form submit event to the saving of the new user in the Store after receiving the backend payload. In a regular Redux app, the process is at least always separated between synchronous storage (in the reducer) and asynchronous logic (in the middleware).<\/p>\n\n<p>We used <code class=\"language-plaintext highlighter-rouge\">combinedReducers<\/code> a lot to split the codebase in smaller units. But we were unable to design the state the way we wanted. With combinedReducers, we can\u2019t say for instance that the State for our early example is <code class=\"language-plaintext highlighter-rouge\">\u2019loading\u2019 | \u2018failure\u2019 | { users: NonEmptyArray&lt;Users&gt;, ... }<\/code> in order to prevent <em><a href=\"https:\/\/www.youtube.com\/watch?v=IcgmSRJHu_8\">impossible states<\/a><\/em> (here having an empty user list, for example).<\/p>\n\n<p>Does it mean Redux is a bad library? Not at all. But it shows that we can use a library for the wrong reasons, when its model does not match the knowledge we have of the business problem. Realizing that while being up to our necks in cruft meant we had to pause to understand the situation better.<\/p>\n\n<h1 id=\"understanding-the-compromises-we-make-when-choosing-a-state-management-library\">Understanding the compromises we make when choosing a state management library<\/h1>\n\n<p>We started looking at other tools. It helped us work out typical patterns and understand what are the principles of all \u201cstate management\u201d libraries.<\/p>\n\n<p>Some of them have something to say about the shape of our State and its surface API. Redux for example makes us write actions that can be seen as a finite API on our State. It\u2019s better than allowing every part of the codebase to update the State in every way possible, without a consistent set of business rules. We like the idea, if not the execution as we\u2019ve discussed.\nIn XState, a state machine library, the State should be designed with a state diagram in mind. This also helps us secure what is possible and what is not. We like that too!<\/p>\n\n<figure>\n  <img alt=\"A state diagram\" src=\"\/assets\/img\/state-store-xstate.png\" \/>\n  <figcaption>\n    A state diagram: a finite number of app states with transitions in between.\n  <\/figcaption>\n<\/figure>\n\n<p>However the constraints libraries can sometimes impose on the State are more technical. We saw that Redux will make us split our code across multiple files. Redux\u2019s <code class=\"language-plaintext highlighter-rouge\">combineReducers<\/code> or Recoil\u2019s way of combining atoms, for example, also have the tendency to shape the state as an object. All of this prevents us from centralizing business logic and modeling the state more finely.<\/p>\n\n<p>Some libraries and tools force us to use their store in a specific context, like React\u2019s <code class=\"language-plaintext highlighter-rouge\">useState<\/code> and Recoil: they are for React views only. That\u2019s a strong constraint. What if we need the store in a vanilla view? What if we follow an architecture where the view is a simple projection of the state and the workflows of the app lies elsewhere? This was our case.<\/p>\n\n<p>What do we also love or hate about \u201cstate management\u201d libraries? Asynchronous paradigms! Communicating Sequential Processes (CSP), like in redux-saga, streams found in RxJS or redux-observable, simple promises returned in Recoil selectors, and so on and so on.<br \/>\nBut are they related to the problems of defining our state or storing it in some place? They should not be. They are workflow issues. If a library enforces strong limitations on its Store or State concepts to favor its Workflow, are we sure we will be able to design the States we really need?<\/p>\n\n<p>Some libraries do let us completely decide the shape of our State. That\u2019s the case of React\u2019s <code class=\"language-plaintext highlighter-rouge\">useState<\/code> hook and other \u201csmall\u201d store libraries that follow the Unix \u201cDo one thing and do it well\u201d philosophy. We like that! They usually also let the user choose the workflows they want. We also like that.<\/p>\n\n<p>Finally, tools like Relay or Apollo help us write apps that are \u201cprojection\u201d, when we have to connect to an API to retrieve data and present it in a UI without complex workflows. They separate concerns really well: there is the backend state described by the API payload \/ GraphQL schema on one hand, and the store that is handled completely by the library (with cache, optimizations, etc.) on the other hand. It even lets us have local states in our views when needed.<br \/>\nIt is however less relevant to use a library like this when our app is more about \u201ccomposition\u201d (of workflows, datasources, or complex functionalities) than \u201cprojection\u201d.<\/p>\n\n<p>Evidently not all \u201cstate management\u201d libraries say the same thing about our dilemma: some are really minimal, others come with strong paradigms and opinionated workflow management. They all somehow have something to say on either what the state should look like, what workflow tool or what views you should use. We therefore have to be carefull: what type of contract are we getting into?<\/p>\n\n<p>What became clear for us is that we should not have to shape the all-important State simply because the Workflow or the Store comes with baggage.<\/p>\n\n<h1 id=\"what-is-a-store-what-is-a-state\">What is a Store? What is a State?<\/h1>\n\n<p>Why should we be so defensive of our State? Most frontend applications manage some data (the State) that is stored somewhere (the Store) and provide their users with functionalities to tie them all together (the Workflow).<br \/>\nWe can be unaware of these concepts and the specific way we compose them in order to build our apps. They are too easy to overlook.<\/p>\n\n<figure>\n  <img alt=\"State, Store, Workflow schema\" src=\"\/assets\/img\/state-store-schema.png\" \/>\n  <figcaption>\n    State, Store and Workflow and how they interact with each other\n  <\/figcaption>\n<\/figure>\n\n<p>Wikipedia\u2019s general <a href=\"http:\/\/en.wiktionary.org\/wiki\/store\">definition of a \u201cstore\u201d<\/a> is \u201ca place where items may be accumulated or routinely kept\u201d. Like in \u201cthis building used to be a store for old tires\u201d. In computing, it\u2019s a direct synonym of \u201cmemory\u201d.<\/p>\n\n<blockquote>\n  <p>A program stores data in variables, which represent <strong>storage locations<\/strong> in the computer\u2019s <strong>memory<\/strong>. The contents of these memory locations, at any given point in the program\u2019s execution, is called the <strong>program\u2019s state<\/strong>.<\/p>\n<\/blockquote>\n\n<p>The store is the memory. The state is what we put in the memory.<\/p>\n\n<figure>\n  <img alt=\"State \/ Store illustration with a cat in a box\" src=\"\/assets\/img\/state-store-cat.png\" \/>\n  <figcaption>\n    <p>A cat (the state) in a box (the store).<br \/> He\u2019s not so happy because a state always prefers to live in the world of states, away from all real world problems. Like all cats.<br \/> <a href=\"https:\/\/unsplash.com\/photos\/GOiAKzoD12I\">From Unsplash, by Sahand Babali<\/a><\/p>\n  <\/figcaption>\n<\/figure>\n\n<p>The content\/container relationship between these two things is basic, but worth keeping in mind. It raises new questions.<\/p>\n\n<p>In real life we choose the container based on the content. Let\u2019s say the content is liquid. Would you store it in a paper bag? No, because the characteristics of the content implies we choose a corresponding container. I would go for a bottle.<br \/>\nHow about software? The relationship is the same and yet we don\u2019t seem to approach the problem this way with many of the \u201cstate management\u201d libraries available. The State has a shape, and it is up to us to make sure the Store adapts.<\/p>\n\n<p>What is a workflow then? It is neither directly the state nor the store in \u201cstate management\u201d libraries. It is where we find the concepts of stream, middlewares and asynchronous stuff. Workflow is about coordinating things and managing <em>effects<\/em> on the outside world.<\/p>\n\n<h1 id=\"combining-the-store-state-and-workflow-in-our-apps\">Combining the Store, State and Workflow in our apps<\/h1>\n\n<p>Armed with these three different concepts in mind, let\u2019s try to see how we can refactor our initial example. What will help us is identify the questions we ask when we think about State and Store.<\/p>\n\n<p>At the end of the process, we will have a codebase that <em>groups things that look the same, and separate things that look different<\/em>. Everything that is about the state will band together, separated from store and workflow issues.<\/p>\n\n<h2 id=\"content-and-container-as-code\">Content and container as code<\/h2>\n\n<p>When we think about the state of our app, we go down a very specific line of questioning. They all aim at turning practical issues into models of a solution:<\/p>\n\n<ul>\n  <li>What does the data represent?<\/li>\n  <li>What entities do I need to model the problem at hand?<\/li>\n  <li>What state diagram can I draw?<\/li>\n<\/ul>\n\n<p>Defining this model in code leads us to write the state definition and the surface API for it. The functions would have specific signatures. Let\u2019s say the application state is defined as <code class=\"language-plaintext highlighter-rouge\">AppState<\/code>.<\/p>\n\n<p>To \u201cread\u201d the State, we would write <code class=\"language-plaintext highlighter-rouge\">AppState -&gt; Substate<\/code> functions (given the State, we extract something from it). They are State accessors .<br \/>\nTo \u201cupdate\u201d the State we would write <code class=\"language-plaintext highlighter-rouge\">AppState -&gt; AppState<\/code> (modifying the state, emptying an array, for example) or <code class=\"language-plaintext highlighter-rouge\">A -&gt; AppState -&gt; AppState<\/code> (using something to modify the state, like adding something in the previous one to get a new one).<br \/>\nThis is the world of ideas. We\u2019re using pure functions. It\u2019s all theory. It is the liquid of the real-life example.<\/p>\n\n<p>Defining the store leads to a totally new set of questions:<\/p>\n\n<ul>\n  <li>Where do we store the data?<\/li>\n  <li>How do we read it?<\/li>\n  <li>How do we write into the store?<\/li>\n  <li>How do we subscribe to its changes?<\/li>\n<\/ul>\n\n<p>To \u201cread\u201d from the store, the signature looks like <code class=\"language-plaintext highlighter-rouge\">() -&gt; AppState<\/code>. It simply returns the state from \u201csomewhere\u201d.<br \/>\nTo \u201cupdate\u201d something in the store, we have <code class=\"language-plaintext highlighter-rouge\">AppState -&gt; () -&gt; void<\/code> or <code class=\"language-plaintext highlighter-rouge\">(AppState -&gt; AppState) -&gt; () -&gt; void<\/code> functions (give something a new state or a modifier function and apply the change).<br \/>\nWe will also want to \u201clisten\u201d for change by giving the store a function it has to call back when the content changes, for example, <code class=\"language-plaintext highlighter-rouge\">(() -&gt; void) -&gt; void<\/code>.<br \/>\nThose of you familiar with functional programming will have recognized the IO signature <code class=\"language-plaintext highlighter-rouge\">() -&gt; X<\/code> explicitly used here to point out that these functions are effects and not simple ideas anymore. Yup, this is the real world. And it\u2019s a container that can accept any content we have: it is a magic bottle.<\/p>\n\n<h2 id=\"rewriting-the-redux-situation\">Rewriting the Redux situation<\/h2>\n\n<p>So let\u2019s write a new version of the user app from the beginning by sticking to a strict State \/ Store separation. We will write the state first to encapsulate business rules, then will come the store and the workflow, and finally, the view.<br \/>\nWe define the state by writing pure functions. Thinking of a succession of states we transition to according to specific business rules really helps. Code like this is easy to read, easy to test and easy to maintain.<\/p>\n\n<p>Here we want to highlight the app could be loading, in error, or loaded with a non-empty list of users. When the app is loaded, it contains a list of Users.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ state\/index.ts<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">NonEmptyArray<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">fp-ts\/lib\/NonEmptyArray<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">User<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/user<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">State<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Loading<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">Failure<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">Loaded<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">initialState<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Loading<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Loading<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">Loading<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Loading<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"kd\">type<\/span> <span class=\"nx\">Failure<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">Error<\/span><span class=\"p\">;<\/span>\n<span class=\"kd\">type<\/span> <span class=\"nx\">Loaded<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">users<\/span><span class=\"p\">:<\/span> <span class=\"nx\">NonEmptyArray<\/span><span class=\"p\">.<\/span><span class=\"nx\">NonEmptyArray<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">User<\/span><span class=\"p\">.<\/span><span class=\"nx\">User<\/span><span class=\"o\">&gt;<\/span> <span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ state\/user.ts<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">User<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">firstName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">lastName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">picture<\/span><span class=\"p\">?:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>That is the shape of our state! We will write an API to manipulate it. We don\u2019t want to access data directly but with functions: this lets us add business rules as code, if needed, and it will make it easier to refactor internal entities in the future. Having one <a href=\"https:\/\/en.wikipedia.org\/wiki\/Indirection\">level of indirection<\/a> can always be helpful: our future selves can change the shape of a User completely without changing its API.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ state\/index.ts<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">decode<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">apiPayload<\/span><span class=\"p\">:<\/span> <span class=\"nx\">unknown<\/span><span class=\"p\">):<\/span> <span class=\"nx\">Loaded<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">Failure<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ parse payload<\/span>\n  <span class=\"c1\">\/\/ remove admins<\/span>\n  <span class=\"c1\">\/\/ if something goes wrong, return Failure<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">users<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Loaded<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">state<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">addUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span><span class=\"p\">.<\/span><span class=\"nx\">User<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n  <span class=\"nx\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Loaded<\/span>\n<span class=\"p\">):<\/span> <span class=\"nx\">Loaded<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">Failure<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"mi\">10<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"k\">new<\/span> <span class=\"nb\">Error<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">oops<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">...<\/span><span class=\"nx\">state<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">users<\/span><span class=\"p\">:<\/span> <span class=\"p\">[...<\/span><span class=\"nx\">state<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">,<\/span> <span class=\"nx\">user<\/span><span class=\"p\">]<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isLoading<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">State<\/span><span class=\"p\">):<\/span> <span class=\"nx\">state<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">Loading<\/span> <span class=\"o\">=&gt;<\/span>  <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isError<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">State<\/span><span class=\"p\">):<\/span> <span class=\"nx\">state<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">Failure<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"c1\">\/\/ ...<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">canCreateUser<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">State<\/span><span class=\"p\">):<\/span> <span class=\"nx\">boolean<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"c1\">\/\/...<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ state\/user.ts<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">DEFAULT_USER_PICTURE<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/i.pravatar.cc\/300<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">id<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">displayName<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"o\">=&gt;<\/span>\n  <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">firstName<\/span><span class=\"p\">}<\/span><span class=\"s2\"> <\/span><span class=\"p\">${<\/span><span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">lastName<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">pictureOrDefault<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"o\">=&gt;<\/span>\n  <span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">picture<\/span> <span class=\"o\">||<\/span> <span class=\"nx\">DEFAULT_USER_PICTURE<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createNew<\/span> <span class=\"o\">=<\/span> <span class=\"p\">({<\/span>\n  <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">picture<\/span>\n<span class=\"p\">}:<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nl\">firstName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">lastName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">picture<\/span><span class=\"p\">?:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}):<\/span> <span class=\"nx\">User<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">id<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nb\">Math<\/span><span class=\"p\">.<\/span><span class=\"nx\">round<\/span><span class=\"p\">(<\/span><span class=\"nb\">Math<\/span><span class=\"p\">.<\/span><span class=\"nx\">random<\/span><span class=\"p\">()<\/span> <span class=\"o\">*<\/span> <span class=\"mi\">1000000<\/span><span class=\"p\">)}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">id<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">picture<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>There it is. We now have our state and the API to manipulate it. It centralizes the few business rules we have: how should <code class=\"language-plaintext highlighter-rouge\">firstName<\/code> and <code class=\"language-plaintext highlighter-rouge\">lastName<\/code> be combined, what is the default user picture, what is a new user id, the 10 users limit, no admins in list. No more leaking of the business logic through various files.<\/p>\n\n<p>Now that the state is defined we will delegate the concern of storing it to a library.<\/p>\n\n<p>At iAdvize, we ended up creating <a href=\"https:\/\/github.com\/iadvize\/store-library\">@iadvize-oss\/store<\/a>, a minimal store library. This is our magic box: completely agnostic of the state and the context it will be used in (not only in React views). We also strived to make it as compatible with functional programming as we could. Let\u2019s use it here.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ store.ts<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">Store<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@iadvize-oss\/store<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">State<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/state<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">store<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Store<\/span><span class=\"p\">.<\/span><span class=\"nx\">create<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">State<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">initialState<\/span><span class=\"p\">)();<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We will probably also need two small services (our only \u201cworkflow\u201d part) that will coordinate asynchronous requests with storage.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ service.ts<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">User<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/state\/user<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">State<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/state<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">store<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/store<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createUserAndSave<\/span> <span class=\"o\">=<\/span> <span class=\"p\">({<\/span>\n  <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">picture<\/span>\n<span class=\"p\">}:<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nl\">firstName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">lastName<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">picture<\/span><span class=\"p\">?:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">currentState<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">read<\/span><span class=\"p\">();<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">canCreateUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">currentState<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">user<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">User<\/span><span class=\"p\">.<\/span><span class=\"nx\">createNew<\/span><span class=\"p\">({<\/span> <span class=\"nx\">firstName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">lastName<\/span><span class=\"p\">,<\/span> <span class=\"nx\">picture<\/span> <span class=\"p\">});<\/span>\n\n  <span class=\"nx\">fetch<\/span><span class=\"p\">(...)<\/span>\n    <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">apply<\/span><span class=\"p\">((<\/span><span class=\"nx\">currentState<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">isLoading<\/span><span class=\"p\">(<\/span><span class=\"nx\">currentState<\/span><span class=\"p\">)<\/span> <span class=\"o\">||<\/span> <span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">isError<\/span><span class=\"p\">(<\/span><span class=\"nx\">currentState<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n          <span class=\"k\">return<\/span> <span class=\"nx\">currentState<\/span><span class=\"p\">;<\/span>\n        <span class=\"p\">}<\/span>\n\n        <span class=\"k\">return<\/span> <span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">addUser<\/span><span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">)(<\/span><span class=\"nx\">currentState<\/span><span class=\"p\">);<\/span>\n      <span class=\"p\">})();<\/span>\n    <span class=\"p\">})<\/span>\n    <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">((<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">apply<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">error<\/span><span class=\"p\">)();<\/span>\n    <span class=\"p\">});<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">init<\/span> <span class=\"o\">=<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">fetch<\/span><span class=\"p\">(...)<\/span>\n    <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">((<\/span><span class=\"nx\">payload<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">apply<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">decode<\/span><span class=\"p\">(<\/span><span class=\"nx\">payload<\/span><span class=\"p\">))();<\/span>\n      <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">({<\/span> <span class=\"na\">state<\/span><span class=\"p\">:<\/span> <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">read<\/span><span class=\"p\">()<\/span> <span class=\"p\">});<\/span>\n    <span class=\"p\">})<\/span>\n    <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">((<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">store<\/span><span class=\"p\">.<\/span><span class=\"nx\">apply<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">error<\/span><span class=\"p\">)();<\/span>\n    <span class=\"p\">});<\/span>\n<span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>The view has now two simple responsibilities: being connected to the store with a special hook that reads the current state and subscribes to the store to listen for future updates (ie. being a reactive view) and handling new user form submissions by calling the corresponding service. Not less, not more.<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ App.tsx<\/span>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">React<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">useState<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@iadvize-oss\/store-react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">State<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/state<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">store<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/store<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createUserAndSave<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/service<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"nx\">Users<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/components\/Users<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"nx\">UserForm<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/components\/UserForm<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">handleFormSubmission<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createUserAndSave<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">state<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useState<\/span><span class=\"p\">(<\/span><span class=\"nx\">store<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">isLoading<\/span><span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;&gt;<\/span>Loading...<span class=\"p\">&lt;\/&gt;;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">isError<\/span><span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;&gt;<\/span>Something went wrong<span class=\"p\">&lt;\/&gt;;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nc\">Users<\/span> <span class=\"na\">users<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">State<\/span><span class=\"p\">.<\/span><span class=\"nx\">users<\/span><span class=\"p\">(<\/span><span class=\"nx\">state<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nc\">UserForm<\/span> <span class=\"na\">onSubmit<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">handleFormSubmission<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n    <span class=\"p\">&lt;\/&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Finally, the root of the app will be responsible for calling <code class=\"language-plaintext highlighter-rouge\">init<\/code> service after mounting the views.<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n<span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">React<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">render<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react-dom<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">import<\/span> <span class=\"nx\">App<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/App<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">init<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/service<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">rootElement<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">document<\/span><span class=\"p\">.<\/span><span class=\"nx\">getElementById<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">root<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n<span class=\"nx\">render<\/span><span class=\"p\">(&lt;<\/span><span class=\"nc\">App<\/span> <span class=\"p\">\/&gt;,<\/span> <span class=\"nx\">rootElement<\/span><span class=\"p\">);<\/span>\n\n<span class=\"nx\">init<\/span><span class=\"p\">();<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Here is the <a href=\"https:\/\/codesandbox.io\/s\/state-and-store-in-frontend-state-version-7l1zm?file=\/src\/index.tsx\">full Codesandbox example<\/a>.<\/p>\n\n<p>What have we done in this example? We have defined the state and its API isolated from the rest of the codebase (in the <code class=\"language-plaintext highlighter-rouge\">state\/<\/code> directory). We then set up a store for it and two services. With that ready, we added a view with the simple responsibility of subscribing to the store, reading it and returning the corresponding UI. When an action occurs on the interface, the view just has to call the corresponding service. This looks a lot like Flux, doesn\u2019t it?<br \/>\nBusiness rules are clearly grouped in the definition of our state. Storage issues are completely out of sight in the store created with <code class=\"language-plaintext highlighter-rouge\">@iadvize-oss\/store<\/code>. No real workflow paradigms are in place here but we could introduce powerful libraries (Rx, CSP, etc) to coordinate things like we did with our \u201cservices\u201d.<\/p>\n\n<h2 id=\"state-store-and-workflow-clearly-separated\">State, Store and Workflow, clearly separated.<\/h2>\n\n<p>Remember, the store is just a box. If the business dictates the state is liquid, the store adapts and becomes a bottle. Not the other way around. The state is where the hard business questions must be answered. This is the real knowledge of the app and one of the most important missions of software developers.<\/p>\n\n<blockquote>\n  <p>Software design is about constructing models, which is an exercise in knowledge representation. It is about seeking Truth and writing it down. That is the #1 priority.\n<br \/> <br \/>\nAnything about software design that prioritizes \"maintainability\" is putting the cart before the horse.\n<br \/> <br \/>\nEric Normand <a href=\"https:\/\/twitter.com\/ericnormand\/status\/1313854365741076480\">(@ericnormand)<\/a><\/p>\n<\/blockquote>\n\n<p>We ended-up doing just that to our legacy app.<br \/>\nFor the State we leverage Typescript and the typing libraries we already used to model states with state diagrams and opaque types in mind.<br \/>\nFor the Store we use multiple instances of <code class=\"language-plaintext highlighter-rouge\">@iadvize-oss\/store<\/code>\u2019s store for different states.<br \/>\nFor the Workflow, we postponed the discussion a bit. We still rely on redux-saga and its CSP paradigm when we need to coordinate complex workflows even if we don\u2019t put anything in the Redux store. It is strange but it works very well. We also use <a href=\"https:\/\/gcanti.github.io\/fp-ts\/modules\/Task.ts.html\">Task from fp-ts<\/a> to write new services. If someday we want to use the concept of streams for a new feature we will look at RxJS without having to rethink the whole app.<\/p>\n\n<h1 id=\"design-is-separating-things-into-things-that-can-be-composed\">Design is separating things into things that can be composed<\/h1>\n\n<p>In his \u201c<a href=\"https:\/\/www.infoq.com\/presentations\/Design-Composition-Performance\/\">Design, Composition and Performance<\/a>\u201d talk, Rich Hickey shared the controversial idea that Design is first about separating things. We might think design is only about combining stuff but, he says, we first have to deconstruct the problem. We have to cut it in smaller problems until we have \u201csolutions\u201d that are about \u201cone or a few things\u201d (Unix philosophy) that can be composed gracefully to solve the initial problem.<\/p>\n\n<p>By cutting the problem into State, Store and Workflow problems (and other things, probably) we can focus on each of them separately and combine them the right way for the specific app we have to build. It scales very well because it doesn\u2019t mix different things together or force a specific tool.<\/p>\n\n<p>We don\u2019t always need to go that far. We can sometimes agree to the specific contract of a \u201cstate management\u201d State + Store + Workflow combo and choose a library that matches our initial needs well enough. It is especially time-saving for small or \u201cquick-and-dirty\u201d apps.<\/p>\n\n<p>But be aware! We sometimes use libraries that are said \u201cscalable\u201d but are only so if they match the way you model the knowledge of the business. If you have to arrange the code in a way that muddles the clarity of your business rules because of technical constraints, it becomes hard to maintain and hard to share with others. A good codebase that scales well lets us add a new tool for part of the workflow when it\u2019s relevant, a new entity in the state when it\u2019s needed.<\/p>\n\n<p>It\u2019s therefore always helpful to remember the concepts hidden behind our choices. That\u2019s what we tried to do in my last project and what you can do too. Lots of benefits resulted from this.<\/p>\n\n<p>Isolating the states challenged the app\u2019s architecture for the better, helping the codebase describe more of the business knowledge.\nLooking at new libraries with the three concepts in mind made us more careful when adding one: does it impose something on our state? What are the impacts for the storage? How can we connect it with the existing workflow tools?<br \/>\nFinally, separating State, Store and Workflow helped us achieve a better separation of concern and better maintainability down the line. With the right concepts clear for all to see, it was also easier to communicate and work together on the same codebase.<\/p>\n\n<p>Having taken the time to think from first principles, the State, the Store and the Workflow, really pays off in the middle and long term. We can only encourage you to do the same.<\/p>\n\n<hr \/>\n\n<p>A huge thank you to my frontend colleagues for their time and help in writing this article, especially Wandrille Verlut, Nicolas Baptiste, Nicolas Maligne and of course Benoit Rajalu without whom it would not have been possible to make sense of my first drafts \u2764\ufe0f<\/p>\n\n","pubDate":"Sun, 22 Nov 2020 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-11-22-state-store-in-frontend-codebases","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-11-22-state-store-in-frontend-codebases"},{"title":"How to model your entities with opaque and sum types in Typescript - round 2.","description":"<p><em>This post was <a href=\"https:\/\/medium.com\/iadvize-engineering\/how-to-model-your-entities-with-opaque-and-sum-types-in-typescript-round-2-a3ca7a474773\">originally published on Medium<\/a>.<\/em><\/p>\n\n<p>In a <a href=\"\/tech\/posts\/2020-02-10-entities\">previous article<\/a> we shared how we use opaque and sum types to model a new domain in Typescript.<\/p>\n\n<p>We built a simple way to write a robust and maintainable domain (in the sense of Domain Driven Design), providing a clean and readable basis to build from further down the line. But, as we will see, it came with boilerplate.<\/p>\n\n<figure>\n  <img alt=\"Rails\" src=\"\/assets\/img\/entities2-illustration.jpeg\" \/>\n  <figcaption>\n    <p>Photo by <a href=\"https:\/\/unsplash.com\/@sgc26?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">SGC<\/a> on <a href=\"https:\/\/unsplash.com\/s\/photos\/railway?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Unsplash<\/a><\/p>\n  <\/figcaption>\n<\/figure>\n\n<h1 id=\"modeling-the-domain-with-our-basic-opaque-types-library\">Modeling the domain with our basic opaque-types library<\/h1>\n\n<p>This where we stopped last time:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ message.ts<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createOpaqueAPI<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">@iadvize-oss\/opaque-type<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createFoldObject<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">@iadvize-oss\/foldable-helpers<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">$Text<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">$Image<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">TextOpaqueAPI<\/span>  <span class=\"o\">=<\/span> <span class=\"nx\">createOpaqueAPI<\/span><span class=\"o\">&lt;<\/span>\n  <span class=\"dl\">'<\/span><span class=\"s1\">Text<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">$Text<\/span><span class=\"p\">,<\/span>\n<span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Text<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createOpaqueAPI<\/span><span class=\"o\">&lt;<\/span>\n  <span class=\"dl\">'<\/span><span class=\"s1\">Image<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">$Image<\/span><span class=\"p\">,<\/span>\n<span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Image<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ opaque types<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">TextOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Image<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ union of opaque types<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Text<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">Image<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ constructors<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">createText<\/span><span class=\"p\">(<\/span><span class=\"nx\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">Text<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">TextOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"p\">({<\/span> <span class=\"nx\">text<\/span> <span class=\"p\">});<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">createImage<\/span><span class=\"p\">({<\/span> <span class=\"nx\">url<\/span><span class=\"p\">,<\/span> <span class=\"nx\">description<\/span> <span class=\"p\">}):<\/span> <span class=\"nx\">Image<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"p\">({<\/span> <span class=\"nx\">url<\/span><span class=\"p\">,<\/span> <span class=\"nx\">description<\/span>  <span class=\"p\">});<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ queries<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">text<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Text<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TextOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">url<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Image<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">description<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Image<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">description<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">isText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">):<\/span> <span class=\"nx\">message<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">Text<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">TextOpaqueAPIisOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">isImage<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">):<\/span> <span class=\"nx\">message<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">Image<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">ImageOpaqueAPIisOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ transformations<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">addSignature<\/span><span class=\"p\">(<\/span><span class=\"nx\">signature<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Text<\/span><span class=\"p\">):<\/span> <span class=\"nx\">Text<\/span>  <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TextOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">newText<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">}<\/span><span class=\"s2\"> - <\/span><span class=\"p\">${<\/span><span class=\"nx\">signature<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nx\">TextOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"p\">({<\/span> <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"nx\">newText<\/span>  <span class=\"p\">});<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">withBitlyUrl<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Image<\/span><span class=\"p\">):<\/span> <span class=\"nx\">Image<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">newUrl<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">convertToBitly<\/span><span class=\"p\">(<\/span><span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">ImageOpaqueAPI<\/span><span class=\"p\">.<\/span><span class=\"nx\">toOpaque<\/span><span class=\"p\">({<\/span> <span class=\"p\">...<\/span><span class=\"nx\">$message<\/span><span class=\"p\">,<\/span> <span class=\"na\">url<\/span><span class=\"p\">:<\/span> <span class=\"nx\">newUrl<\/span>  <span class=\"p\">});<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ fold function from @iadvize-oss\/foldable-helpers<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">fold<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createFoldObject<\/span><span class=\"p\">({<\/span> \n  <span class=\"na\">Text<\/span><span class=\"p\">:<\/span> <span class=\"nx\">isText<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">Image<\/span><span class=\"p\">:<\/span> <span class=\"nx\">isImage<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This still works great. Our domain is safe: details of the types are kept private and the users of this <code class=\"language-plaintext highlighter-rouge\">message.ts<\/code> module are forced to use the exposed APIs in order to deal with messages. Opaque types hide implementation hence we cannot use them directly.<\/p>\n\n<p>Although it produces a very solid domain, there is also a lot of boilerplate to write and it doesn\u2019t accept entity addition as easily as we would like. Imagine adding a new <code class=\"language-plaintext highlighter-rouge\">$Audio<\/code> type here and having to deal with a new <code class=\"language-plaintext highlighter-rouge\">AudioOpaqueAPI<\/code> everywhere. Not fun.<\/p>\n\n<h1 id=\"modeling-the-domain-with-iadvize-ossopaque-union\">Modeling the domain with @iadvize-oss\/opaque-union<\/h1>\n\n<p>Enters <a href=\"https:\/\/github.com\/iadvize\/opaque-union-library\">@iadvize-oss\/opaque-union<\/a>. This is an experimental library to help with writing opaque sum-types without the boilerplate.<\/p>\n\n<p>With this library we can rewrite our domain like so:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">Union<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">@iadvize-oss\/opaque-union<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">$Text<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">$Image<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">MessageUnion<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">({<\/span>\n  <span class=\"na\">Text<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">$Text<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(),<\/span>\n  <span class=\"na\">Image<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">$Image<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(),<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"c1\">\/\/ opaque types<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Image<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ union of opaque types<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"nx\">Type<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ constructors<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createImage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ queries<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">text<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"kd\">get<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">url<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">url<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"kd\">get<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">description<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">description<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"kd\">get<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">is<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isImage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">is<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ transformations<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">addSignature<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">signature<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">iso<\/span><span class=\"p\">.<\/span><span class=\"nx\">modify<\/span><span class=\"p\">(<\/span>\n  <span class=\"p\">(<\/span><span class=\"nx\">$text<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span> <span class=\"p\">...<\/span><span class=\"nx\">$text<\/span><span class=\"p\">,<\/span> <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">$text<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">}<\/span><span class=\"s2\"> - <\/span><span class=\"p\">${<\/span><span class=\"nx\">signature<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span> <span class=\"p\">})<\/span>\n<span class=\"p\">);<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">withBitlyUrl<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">url<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">modify<\/span><span class=\"p\">(<\/span><span class=\"nx\">convertToBitly<\/span><span class=\"p\">);<\/span>\n\n<span class=\"c1\">\/\/ fold function from @iadvize-oss\/foldable-helpers<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">fold<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">fold<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>The exposed domain API is the same but the code is much more direct. We just had to pick what we wanted to expose out of our <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code>.<\/p>\n\n<p>Let\u2019s look in more detail at what <code class=\"language-plaintext highlighter-rouge\">@iadvize-oss\/opaque-union<\/code> enables us to do.<\/p>\n\n<p>First, it enables us to list the private types we will work with and build an \u201copaque union api\u201d.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">type<\/span> <span class=\"nx\">$Text<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"kd\">type<\/span> <span class=\"nx\">$Image<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">MessageUnion<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">({<\/span>\n  <span class=\"na\">Text<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">$Text<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(),<\/span>\n  <span class=\"na\">Image<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Union<\/span><span class=\"p\">.<\/span><span class=\"kd\">type<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">$Image<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(),<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We will build our entire APi from this <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code>. Because it has great power, it should not be shared outside the module: it would be counterproductive to expose opaque types but also share the key to \u201cunopaque\u201d them.<\/p>\n\n<p>From <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> comes our constructors:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">createImage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Image<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>These are functions that build opaque types from private types. We can extract the opaques types (the returned types) from them and we can use them directly like so:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">createEmptyText<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">of<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">({<\/span> <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"dl\">''<\/span> <span class=\"p\">});<\/span> <span class=\"c1\">\/\/ you must pass a $Text here<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> also gives us type guards and fold for free.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">is<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">isImage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"k\">is<\/span><span class=\"p\">.<\/span><span class=\"nx\">Image<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">fold<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">fold<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>With all these we have now built a convenient <code class=\"language-plaintext highlighter-rouge\">message.ts<\/code> module that can be used in our app like this:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">Message<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/path\/to\/message.ts<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">.<\/span><span class=\"nx\">createText<\/span><span class=\"p\">({<\/span> <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">hello world<\/span><span class=\"dl\">'<\/span> <span class=\"p\">});<\/span>\n\n<span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">Message<\/span><span class=\"p\">.<\/span><span class=\"nx\">isText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>This is a text<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;;<\/span>\n<span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>This is an image<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ or<\/span>\n\n<span class=\"k\">return<\/span> <span class=\"nx\">fold<\/span><span class=\"p\">({<\/span>\n  <span class=\"na\">Text<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>This is a text<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;,<\/span>\n  <span class=\"na\">Image<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>This is an image<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;,<\/span>\n<span class=\"p\">})(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div><\/div>\n<p>Constructors, type guards and fold help us a lot but the majority of the boilerplate of the previous example was in query and transformation functions, where we have to \u201cunopaque\u201d a variable to access or modify a property of the private type.<\/p>\n\n<p>To cut down on this tedium, <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> exposes some optics from <a href=\"https:\/\/github.com\/gcanti\/monocle-ts\">monocle-ts<\/a>. You don\u2019t have to be familiar with optics, lenses, prism or profunctor dark magic to use them.<\/p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> exposes two kinds of optics. The first one is <code class=\"language-plaintext highlighter-rouge\">Iso<\/code>. It helps us jump from a type <code class=\"language-plaintext highlighter-rouge\">A<\/code> to a type <code class=\"language-plaintext highlighter-rouge\">B<\/code> and back again (when there is a bidirectional link between them \u2014 an isomorphism, hence the name). Give them a type <code class=\"language-plaintext highlighter-rouge\">A<\/code>, it will give you a <code class=\"language-plaintext highlighter-rouge\">B<\/code> in return. Give them a type <code class=\"language-plaintext highlighter-rouge\">B<\/code>, it will give you an <code class=\"language-plaintext highlighter-rouge\">A<\/code>. You get the idea.<\/p>\n\n<figure>\n  <img style=\"max-width: 400px\" alt=\"Iso get\/reverseGet schema\" src=\"\/assets\/img\/entities2-iso.png\" \/>\n  <figcaption>\n    <p><code class=\"language-plaintext highlighter-rouge\">get<\/code> to go from A to B<\/p>\n\n    <p><code class=\"language-plaintext highlighter-rouge\">reverseGet<\/code> to go from B to A<\/p>\n  <\/figcaption>\n<\/figure>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> uses <code class=\"language-plaintext highlighter-rouge\">Iso<\/code> to help us jump from opaque to private, and from private to opaque. It replace the <code class=\"language-plaintext highlighter-rouge\">fromOpaque<\/code> and <code class=\"language-plaintext highlighter-rouge\">toOpaque<\/code> functions from the previous article.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">addSignature<\/span><span class=\"p\">(<\/span><span class=\"nx\">signature<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span><span class=\"nx\">text<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Text<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">$text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">iso<\/span><span class=\"p\">.<\/span><span class=\"nx\">unwrap<\/span><span class=\"p\">(<\/span><span class=\"nx\">text<\/span><span class=\"p\">);<\/span> <span class=\"c1\">\/\/ get private type<\/span>\n\n    <span class=\"kd\">const<\/span> <span class=\"nx\">$newText<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n      <span class=\"p\">...<\/span><span class=\"nx\">$text<\/span><span class=\"p\">,<\/span>\n      <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">}<\/span><span class=\"s2\"> - <\/span><span class=\"p\">${<\/span><span class=\"nx\">signature<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">,<\/span>\n    <span class=\"p\">};<\/span>\n\n    <span class=\"k\">return<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">iso<\/span><span class=\"p\">.<\/span><span class=\"nx\">wrap<\/span><span class=\"p\">(<\/span><span class=\"nx\">$newText<\/span><span class=\"p\">);<\/span> <span class=\"c1\">\/\/ get opaque type<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Iso even offers shortcuts, the same result can be achieved like so:<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">addSignature<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">signature<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">iso<\/span><span class=\"p\">.<\/span><span class=\"nx\">modify<\/span><span class=\"p\">(<\/span>\n  <span class=\"p\">(<\/span><span class=\"nx\">$text<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span> <span class=\"p\">...<\/span><span class=\"nx\">$text<\/span><span class=\"p\">,<\/span> <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">$text<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">}<\/span><span class=\"s2\"> - <\/span><span class=\"p\">${<\/span><span class=\"nx\">signature<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span> <span class=\"p\">})<\/span>\n<span class=\"p\">);<\/span>\n<\/code><\/pre><\/div><\/div>\n<p>The second kind of optics <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> exposes is <code class=\"language-plaintext highlighter-rouge\">Lens<\/code>. A <code class=\"language-plaintext highlighter-rouge\">Lens<\/code> allows you to look \u201cinside\u201d a type, either to get or to change something. <code class=\"language-plaintext highlighter-rouge\">MessageUnion<\/code> uses <code class=\"language-plaintext highlighter-rouge\">Lens<\/code> to access properties of the private types hidden in opaque types.<\/p>\n\n<p>This enables us to easily expose queries and transformation functions.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ text :: (text: Text) =&gt; string<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">text<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">text<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"kd\">get<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ updateText :: (content: string) =&gt; (text: Text) =&gt; Text<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">updateText<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">MessageUnion<\/span><span class=\"p\">.<\/span><span class=\"nx\">Text<\/span><span class=\"p\">.<\/span><span class=\"nx\">lensFromProp<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">text<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"kd\">set<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>The function <code class=\"language-plaintext highlighter-rouge\">text<\/code> here will read the corresponding property inside the opaque type Text it receives. <code class=\"language-plaintext highlighter-rouge\">updateText<\/code> will update the property, given a new content.<\/p>\n\n<p>Optics are hard to understand. There is a lot more to say on them (what are the other optics, how they compose well, etc.) but that\u2019s not the goal of the library. For our need \u2014 getter, setter, complex transformations on opaque types \u2014 Iso and Lens are enough.<\/p>\n\n<hr \/>\n\n<p>With this experimental library we hope to drastically reduce the time needed to write our domain files. We also hope its strong typing and helpful optic helpers will reduce the need to write tedious tests.<\/p>\n\n<p>This library is already available, feel free to use and experiment with it. Start for example by reading the documentation of the repo. Then a simple <code class=\"language-plaintext highlighter-rouge\">npm add @iadvize-oss\/opaque-union<\/code> <a href=\"https:\/\/github.com\/iadvize\/opaque-union-library\">will do<\/a>!<\/p>\n\n<hr \/>\n\n<p>A big thanks \u2764\ufe0f to all my iAdvize colleagues that helped me write this post: Axel Cateland, Ben,oit Rajalu and Nicolas\nBaptiste !<\/p>\n","pubDate":"Thu, 02 Jul 2020 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-07-02-entities-2","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-07-02-entities-2"},{"title":"How to model your entities with opaque and sum types in Typescript.","description":"<p><em>This post was <a href=\"https:\/\/medium.com\/iadvize-engineering\/how-to-model-your-entities-with-opaque-and-sum-types-in-typescript-d2fe0677ff53\">originally published on Medium<\/a>.<\/em><\/p>\n\n<p>Working on a new feature implies working on modeling the data that comes with it. This step provides a clean and readable basis to build from further down the line. Because of this we are putting a larger emphasis on making sure our model is as robust and maintainable as it could be. Here is how.<\/p>\n\n<figure>\n  <img alt=\"Rails\" src=\"\/assets\/img\/entities-illustration.jpeg\" \/>\n  <figcaption>\n    <p>Photo by <a href=\"https:\/\/unsplash.com\/@thesollers?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Anton Darius<\/a> on <a href=\"https:\/\/unsplash.com\/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Unsplash<\/a><\/p>\n  <\/figcaption>\n<\/figure>\n\n<h1 id=\"the-scenario\">The scenario<\/h1>\n\n<p>Imagine we are working on a fancy chat application in which we send many kinds of messages. We could model them this way:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ Message.ts<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">ImageMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">mimeType<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">AudioMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">mimeType<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n  <span class=\"nl\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>And we would define a union type <code class=\"language-plaintext highlighter-rouge\">Message<\/code> that could be any of them:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ Message.ts<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">ImageMessage<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Doing so helps a lot (we can for example define a list <code class=\"language-plaintext highlighter-rouge\">const messages: Message[] = \u2026<\/code> without having to deal with variations at that level) but it is not perfect.<\/p>\n\n<p>How should we deal with this union in a function that receives a <code class=\"language-plaintext highlighter-rouge\">Message<\/code> and that should act differently depending on the type of the message? We would have to set up a series of <code class=\"language-plaintext highlighter-rouge\">if<\/code>s and quickly code ourselves into a corner, like this:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Message<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/Message<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">renderMessage<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ ok we know it\u2019s a TextMessage<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">TextComponent<\/span> <span class=\"na\">text<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ Problem!<\/span>\n    <span class=\"c1\">\/\/ is it ImageMessage or AudioMessage?<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We went through the trouble of strongly typing our messages yet we have to rely on fragile internal structure inspection to differentiate them. We want to differentiate the types of messages effortlessly.<\/p>\n\n<h1 id=\"tagged-union\">Tagged Union<\/h1>\n\n<p>Enter tagged unions. In Typescript, a typed object is still only a classic Javascript object at runtime. We need to find a trick to differentiate two types with the same properties, like <code class=\"language-plaintext highlighter-rouge\">ImageMessage<\/code> and <code class=\"language-plaintext highlighter-rouge\">AudioMessage<\/code>.<\/p>\n\n<p>We can add a tag to our types manually:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">messageType<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">TEXT<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">...<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">ImageMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">messageType<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">IMAGE<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">...<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">AudioMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">messageType<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">AUDIO<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">...<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">Message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">ImageMessage<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Our <code class=\"language-plaintext highlighter-rouge\">Message<\/code> type can now be called a \u201ctagged union\u201d, which means a union of sub-types that all have a tag value (here, <code class=\"language-plaintext highlighter-rouge\">messageType<\/code>). Now when we write our function, Typescript will refine the type when we check the tag value.<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">renderMessage<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageType<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">TEXT<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ message is typed as TextMessage here<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">TextComponent<\/span> <span class=\"na\">text<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageType<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">IMAGE<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ message is typed as ImageMessage here<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">ImageComponent<\/span> <span class=\"na\">url<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"si\">}<\/span> <span class=\"na\">description<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">description<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageType<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">AUDIO<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"c1\">\/\/ message is typed as AudioMessage here<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">AudioComponent<\/span> <span class=\"na\">url<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"si\">}<\/span> <span class=\"na\">description<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">description<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>While this works great, it doesn\u2019t scale so well. We have to test <code class=\"language-plaintext highlighter-rouge\">message.messageType<\/code> everywhere in our codebase. This will necessarily create a lot of boilerplate and is not <a href=\"https:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\">D.R.Y<\/a>.<\/p>\n\n<h1 id=\"user-type-guard\">User type guard<\/h1>\n\n<p>We want to check the type of each message more easily. Typescript can help with that through <a href=\"http:\/\/www.typescriptlang.org\/docs\/handbook\/advanced-types.html#user-defined-type-guards\">user-defined type\nguards<\/a>.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ If this function returns true, TS will know the message variable is a TextMessage<\/span>\n<span class=\"kd\">function<\/span> <span class=\"nx\">isText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">):<\/span> <span class=\"nx\">message<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageType<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">TEXT<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Repeat for isImage and isAudio<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>It is now cleaner to check for a <code class=\"language-plaintext highlighter-rouge\">TextMessage<\/code>:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">isText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">TextComponent<\/span> <span class=\"na\">text<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>But we still have to do all our <code class=\"language-plaintext highlighter-rouge\">if<\/code>s each time we want to process messages differently based on type. In the end, we did not remove that much boilerplate so far \u2014 it is just moving it somewhere else \u2014 but at least it is D.R.Y: if the <code class=\"language-plaintext highlighter-rouge\">messageType<\/code> values change, we simply update the type guard function.<\/p>\n\n<h1 id=\"fold-on-things\">Fold on things<\/h1>\n\n<p>Inspired by functional programming and particularly the library <a href=\"https:\/\/gcanti.github.io\/fp-ts\/\">fp-ts<\/a>, we decided to give the fold\/match pattern a try for our entities sum types.<\/p>\n\n<p>If you are not familiar with the concept, here is what it looks like. Using fp-ts Option, we can can use the <code class=\"language-plaintext highlighter-rouge\">fold<\/code> function:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Option<\/span><span class=\"p\">,<\/span> <span class=\"nx\">fold<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fp-ts\/lib\/Option<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Option<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">User<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">...<\/span> <span class=\"c1\">\/\/ ie. None | Some&lt;User&gt;<\/span>\n\n<span class=\"k\">return<\/span> <span class=\"nx\">fold<\/span><span class=\"p\">(<\/span>\n    <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>oops, no user!<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;,<\/span>\n    <span class=\"p\">(<\/span><span class=\"nx\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">User<\/span><span class=\"p\">)<\/span>  <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">UserView<\/span> <span class=\"na\">user<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">user<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;,<\/span>\n<span class=\"p\">)(<\/span><span class=\"nx\">user<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>How does this help with our <code class=\"language-plaintext highlighter-rouge\">Message<\/code> sum-type? We need a function that takes as many parameters as there are sub-types. Each one of these parameters should be a function as well, one that takes a <code class=\"language-plaintext highlighter-rouge\">Message<\/code> and returns something. Finally, the parent function, given a <code class=\"language-plaintext highlighter-rouge\">Message<\/code>, should return the correct sub-function result depending on its sub-type.<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ Message.ts<\/span>\n<span class=\"kd\">function<\/span> <span class=\"nx\">fold<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">R<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span>\n  <span class=\"nx\">onText<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">TextMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">R<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">onImage<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ImageMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">R<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">onAudio<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">R<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span><span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">):<\/span> <span class=\"nx\">R<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">switch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageType<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n      <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">TEXT<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">return<\/span> <span class=\"nx\">onText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n\n      <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">IMAGE<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">return<\/span> <span class=\"nx\">onImage<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n\n      <span class=\"k\">case<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">AUDIO<\/span><span class=\"dl\">'<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">return<\/span> <span class=\"nx\">onAudio<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now our <code class=\"language-plaintext highlighter-rouge\">renderMessage<\/code> is way cleaner:<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">TextMessage<\/span><span class=\"p\">,<\/span> <span class=\"nx\">ImageMessage<\/span><span class=\"p\">,<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">,<\/span> <span class=\"nx\">fold<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/Message<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">renderMessage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">fold<\/span><span class=\"p\">(<\/span>\n  <span class=\"p\">(<\/span><span class=\"nx\">textMessage<\/span><span class=\"p\">:<\/span> <span class=\"nx\">TextMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nc\">TextComponent<\/span> <span class=\"na\">text<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">),<\/span>\n  <span class=\"p\">(<\/span><span class=\"nx\">imageMessage<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ImageMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nc\">ImageComponent<\/span>\n      <span class=\"na\">url<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"si\">}<\/span>\n      <span class=\"na\">description<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">description<\/span><span class=\"si\">}<\/span>\n    <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">),<\/span>\n  <span class=\"p\">(<\/span><span class=\"nx\">audioMessage<\/span><span class=\"p\">:<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nc\">AudioComponent<\/span>\n      <span class=\"na\">url<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"si\">}<\/span>\n      <span class=\"na\">description<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">description<\/span><span class=\"si\">}<\/span>\n    <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">),<\/span>\n<span class=\"p\">)<\/span>\n\n<span class=\"c1\">\/\/ Somewhere in a component far, far away... <\/span>\n<span class=\"k\">return<\/span> <span class=\"nx\">renderMessage<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Creating a fold for every sum-type and for every union type is fastidious. We created <a href=\"https:\/\/github.com\/iadvize\/foldable-helpers-library\">@iadvize-oss\/foldable-helpers<\/a> to help with that:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createFold<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">@iadvize-oss\/foldable-helpers<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ fold :: (TextMessage -&gt; R) -&gt; (ImageMessage -&gt; R) -&gt; (AudioMessage -&gt; R) -&gt; Message -&gt; R<\/span>\n<span class=\"c1\">\/\/ \u201cfeeding\u201d the fold-creator with our previously created type guards<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">fold<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createFold<\/span><span class=\"p\">(<\/span><span class=\"nx\">isText<\/span><span class=\"p\">,<\/span> <span class=\"nx\">isImage<\/span><span class=\"p\">,<\/span> <span class=\"nx\">isAudio<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Or, passing function with an object to give them names:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createFoldObject<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">@iadvize-oss\/foldable-helpers<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ fold :: ({<\/span>\n<span class=\"c1\">\/\/  isText: TextMessage -&gt; R,<\/span>\n<span class=\"c1\">\/\/  isImage: ImageMessage -&gt; R,<\/span>\n<span class=\"c1\">\/\/  isAudio: AudioMessage -&gt; R<\/span>\n<span class=\"c1\">\/\/ }) -&gt; Message -&gt; R<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">fold<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createFoldObject<\/span><span class=\"p\">({<\/span> <span class=\"nx\">isText<\/span><span class=\"p\">,<\/span> <span class=\"nx\">isImage<\/span><span class=\"p\">,<\/span> <span class=\"nx\">isAudio<\/span> <span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We now have a <code class=\"language-plaintext highlighter-rouge\">Message<\/code> sum-type with its <code class=\"language-plaintext highlighter-rouge\">fold<\/code> function to match on members.<\/p>\n\n<p>It is good enough to be used internally, but if we want to share the <code class=\"language-plaintext highlighter-rouge\">Message<\/code> sum-type with the world, for example in a public library, we would want to consider subtype properties as <em>private<\/em> or <em>opaque<\/em>.<\/p>\n\n<h1 id=\"expose-opaque-message-entities\">Expose opaque message entities<\/h1>\n\n<p>Let\u2019s say we want to share the <code class=\"language-plaintext highlighter-rouge\">Message<\/code>, <code class=\"language-plaintext highlighter-rouge\">TextMessage<\/code>, <code class=\"language-plaintext highlighter-rouge\">ImageMessage<\/code> and <code class=\"language-plaintext highlighter-rouge\">AudioMessage<\/code> entities in a library as well as functional function like below:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">createText<\/span><span class=\"p\">(<\/span><span class=\"nx\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">TextMessage<\/span>\n<span class=\"nx\">text<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">TextMessage<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span>\n\n<span class=\"nx\">createImage<\/span><span class=\"p\">(<\/span><span class=\"nx\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">,<\/span> <span class=\"nx\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">ImageMessage<\/span>\n<span class=\"nx\">createAudio<\/span><span class=\"p\">(<\/span><span class=\"nx\">url<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">,<\/span> <span class=\"nx\">description<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">AudioMessage<\/span>\n<span class=\"nx\">url<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ImageMessage<\/span> <span class=\"o\">|<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span><span class=\"p\">,<\/span>\n\n<span class=\"nx\">toText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ImageMessage<\/span><span class=\"p\">):<\/span> <span class=\"nx\">TextMessage<\/span>\n\n<span class=\"p\">...<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>We don\u2019t want to share externally how we have modeled our entities and which properties we have on each type: forcing users to use our functions allows us to update the internal implementation without breaking someone else\u2019s code.<\/p>\n\n<p>We use <a href=\"https:\/\/github.com\/iadvize\/opaque-type-library\">@iadvize-oss\/opaque-type<\/a> to hide our type in an opaque one. It will wrap our internal types in an opaque shell. It will also provide a \u201cruntime\u201d type representation like below:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">$TextMessage<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"na\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">toOpaque<\/span><span class=\"p\">,<\/span> <span class=\"nx\">fromOpaque<\/span><span class=\"p\">,<\/span> <span class=\"nx\">isOpaque<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createOpaqueAPI<\/span><span class=\"o\">&lt;<\/span>\n  <span class=\"dl\">'<\/span><span class=\"s1\">TextMessage<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">$TextMessage<\/span><span class=\"p\">,<\/span>\n<span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">TextMessage<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">type<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ReturnType<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">toOpaque<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">createText<\/span><span class=\"p\">(<\/span><span class=\"nx\">text<\/span><span class=\"p\">:<\/span> <span class=\"kr\">string<\/span><span class=\"p\">):<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">toOpaque<\/span><span class=\"p\">({<\/span> <span class=\"nx\">text<\/span> <span class=\"p\">});<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">text<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">TextMessage<\/span><span class=\"p\">):<\/span> <span class=\"kr\">string<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">$message<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">fromOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">$message<\/span><span class=\"p\">.<\/span><span class=\"nx\">text<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ We can use the isOpaque type guard without needing the `messageType` tag,<\/span>\n<span class=\"c1\">\/\/ free of charge!<\/span>\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">isText<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span><span class=\"p\">):<\/span> <span class=\"nx\">message<\/span> <span class=\"k\">is<\/span> <span class=\"nx\">TextMessage<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">isOpaque<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>The user of our fabulous Message library can use it without having to rely on internal details. Breaking changes will only be real, functional breaking changes and never petty implementation details.<\/p>\n\n<div class=\"language-tsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">createText<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">createImage<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">createAudio<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">AudioMessage<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">description<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">url<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">fold<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">fabulous-Message-library<\/span><span class=\"dl\">'<\/span><span class=\"p\">;<\/span>\n\n<span class=\"c1\">\/\/ data fetching<\/span>\n<span class=\"k\">async<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">fetchMessages<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">rawMessages<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">fetch<\/span><span class=\"p\">.<\/span><span class=\"kd\">get<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">http:\/\/messages.io<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">messages<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">rawMessages<\/span>\n    <span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"k\">if<\/span> <span class=\"p\">(...)<\/span> <span class=\"p\">{<\/span>\n         <span class=\"k\">return<\/span> <span class=\"nx\">createAudio<\/span><span class=\"p\">(...)<\/span>\n      <span class=\"p\">}<\/span> \n\n      <span class=\"k\">if<\/span> <span class=\"p\">(...)<\/span> <span class=\"p\">{<\/span>\n         <span class=\"k\">return<\/span> <span class=\"nx\">createImage<\/span><span class=\"p\">(...)<\/span>\n      <span class=\"p\">}<\/span> \n\n      <span class=\"k\">return<\/span> <span class=\"nx\">createText<\/span><span class=\"p\">(...);<\/span>\n    <span class=\"p\">})<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Views<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">AudioMessage<\/span><span class=\"p\">({<\/span> <span class=\"nx\">message<\/span> <span class=\"p\">}<\/span> <span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"nl\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">AudioMessage<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span><span class=\"si\">{<\/span><span class=\"nx\">description<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span><span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nc\">Player<\/span> <span class=\"na\">source<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">url<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">div<\/span><span class=\"p\">&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ ...<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">MessageComponent<\/span><span class=\"p\">({<\/span> <span class=\"nx\">message<\/span> <span class=\"p\">}:<\/span> <span class=\"p\">{<\/span> <span class=\"nl\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Message<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">fold<\/span><span class=\"p\">({<\/span>\n    <span class=\"na\">isText<\/span><span class=\"p\">:<\/span> <span class=\"nx\">message<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">TextMessage<\/span> <span class=\"na\">message<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;,<\/span>\n    <span class=\"na\">isImage<\/span><span class=\"p\">:<\/span> <span class=\"nx\">message<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">ImageMessage<\/span>  <span class=\"na\">message<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;,<\/span>\n    <span class=\"na\">isAudio<\/span><span class=\"p\">:<\/span> <span class=\"nx\">message<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">&lt;<\/span><span class=\"nc\">AudioMessage<\/span> <span class=\"na\">message<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">message<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;,<\/span>\n  <span class=\"p\">})(<\/span><span class=\"nx\">message<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Having modeled our functional entities with a tagged-union, opaque types and a foldable interface we can now share them freely, assured we are still in control of the internal details.<\/p>\n\n<p>Doing this certainly prevents technical \u201cbreakings\u201d down the line but, regardless of the publicity of our codebase, it also greatly facilitates maintenance and refactoring while enforcing a consistent, strongly typed data structure.<\/p>\n\n<hr \/>\n\n<p>A big thanks \u2764\ufe0f to all my iAdvize colleagues that helped me write this post: Wandrille Verlut, Victor Graffard, Axel Cateland and Ben oit Rajalu!<\/p>\n","pubDate":"Mon, 10 Feb 2020 00:00:00 +0000","link":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-02-10-entities","guid":"https:\/\/guillaume.wuips.com\/tech\/posts\/2020-02-10-entities"}]}}