<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Szymon Krajewski</title>
    <link>https://szymonkrajewski.pl/</link>
    <description>Recent content on Szymon Krajewski</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <copyright>Copyright © 2017 - 2026 Szymon Krajewski</copyright>
    <lastBuildDate>Mon, 09 Feb 2026 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://szymonkrajewski.pl/about/avatar.jpg</url>
      <title>Szymon Krajewski</title>
      <link>https://szymonkrajewski.pl/</link>
      <width>144</width>
      <height>144</height>
    </image><atom:link href="https://szymonkrajewski.pl/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>My Workflow for Agentic Coding</title>
      <link>https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/</link>
      <pubDate>Mon, 09 Feb 2026 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve experimented with agentic coding for a while and I&amp;rsquo;ve developed a set of techniques – and a bit of intuition – how to work with agent to deliver actual value without sacrificing my soul as a software engineer. Here&amp;rsquo;s one of them.&lt;/p&gt;
&lt;p&gt;The process I follow consists of 4 steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Talk&lt;/li&gt;
&lt;li&gt;ADR&lt;/li&gt;
&lt;li&gt;Plan&lt;/li&gt;
&lt;li&gt;Execute&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;ll discuss each of them in details. For simplicity, let&amp;rsquo;s assume I&amp;rsquo;ll add a new functionality to existing system. I use Cursor as agentic IDE, so it may influence the process by its system prompt.&lt;/p&gt;
&lt;h3 id=&#34;talk&#34;&gt;Talk&lt;/h3&gt;
&lt;p&gt;I start the session by talking to the agent. I describe&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what&amp;rsquo;s the &lt;strong&gt;problem&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;what&amp;rsquo;s the &lt;strong&gt;desired outcome&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;my hypotheses &lt;strong&gt;what we need to do&lt;/strong&gt; to achieve the final functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I ask the model to check those hypotheses by reading the existing files and form a sense of relationship between them. If the scope is known and small, I explicitly limit the agent to specific working directory. Otherwise, I intentionally force it to discover the structure, giving only a few keywords as a hint.&lt;/p&gt;
&lt;p&gt;Then I talk about potential solutions, the same way I talk to my fellow – but at high level. It helps me organise my thoughts and enrich the context further. After a while, I ask agent for the feedback. Then I propose another solution.&lt;/p&gt;
&lt;p&gt;The agent often continues my reasoning by writing example code. When I catch something tricky, e.g. potential performance issues, crossing domain boundaries or just misalignment with the original problem, I point that out, asking for clarification or change.&lt;/p&gt;
&lt;p&gt;The talk lasts until I run out of the ideas – or time. For now, I have a pretty solid understanding of the problem space, potential solution space but to gain actual benefit from working with agent I ask about the last thing – the potential options I miss but they would fit as well. I often mention something like &amp;ldquo;think out of the box&amp;rdquo; to let the agent takes different perspective.&lt;/p&gt;
&lt;p&gt;The agent prepares a list of alternative approaches. Some of them are totally abstract but other ones are worth consideration. So I continue the talking. I challenge a selected approach and I learn something new.&lt;/p&gt;
&lt;p&gt;This session lasts from 15 minutes – for small features – to one hour – when working with architecture or infrastructural code. This co-operation with agent not only gives my another perspective but also point out other places potentially affected by the changes I want to introduce.&lt;/p&gt;
&lt;p&gt;This talk is rich for useful information – problem, requirements, a few potential solution analysed and verified in the app-specific context. I don&amp;rsquo;t want to lose it, as this may be relevant for future changes as well. Or, it may be a solid initial context for another discussion. So I ask agent for creating the ADR from the session.&lt;/p&gt;
&lt;h3 id=&#34;adr---architecture-decision-record&#34;&gt;ADR - Architecture Decision Record&lt;/h3&gt;
&lt;p&gt;If you don&amp;rsquo;t know what is ADR, it&amp;rsquo;s a Markdown file sitting in the project repository, representing an significant architectural decision&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; – decisions that are usually difficult to change as project grows.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Should we use RabbitMQ or Kafka?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Classic entity-powered CRUD or Event Sourcing?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or smaller, domain-specific decisions like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;how to store calculations?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;how to model specific process?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;refactor to X, or just leave it as is?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Typical ADR consist of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Problem and context&lt;/li&gt;
&lt;li&gt;Decision Drivers&lt;/li&gt;
&lt;li&gt;Considered options, with pros and cons of each&lt;/li&gt;
&lt;li&gt;Decision Outcome&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After the previous step, the agent has most of the information to form a decent ADR. I try to maintain a consistent decision log, so I ask agent to use the template and fill it with relevant information.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&#34;figure center invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr_hu_e3426f779cb33c03.png 360w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr_hu_5ab6c9366b69d74e.png 640w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr_hu_7e61c5e1baef4da4.png 960w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr_hu_5cb1d4e0f11721cb.png 1200w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/talk-to-adr_hu_10dbf8d7a5eac0ab.png 1426w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1426&#34;
          height=&#34;677&#34;
           alt=&#34;Diagram showing transformation from chaotic talk concepts into the structured document&#34; 
           title=&#34;Creating ADR is like transforming sometimes chaotic talk into structured document&#34; 
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Creating ADR is like transforming sometimes chaotic talk into structured document
    &lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I read the Decision Record and – to be honest – I prune most of it. The agent captures the context and problem statement correctly, but some considered options and their pros and cons required cleanup so I remove irrelevant bullet points, example code and other noise. I leave the core – what are the options, the best and worse from each, and brief example if it&amp;rsquo;s valid for the final decision.&lt;/p&gt;
&lt;p&gt;I take the final decision with a bit of justification and I ask agent to update the ADR. We&amp;rsquo;re ready for the next step.&lt;/p&gt;
&lt;h3 id=&#34;plan&#34;&gt;Plan&lt;/h3&gt;
&lt;p&gt;To avoid distracting the agent, I start a new conversation and include the newly created ADR. Some models, like Sonnet or Opus 4.5 have smaller context window, so more space is needed to avoid auto-summarisation.&lt;/p&gt;
&lt;p&gt;I switch to the plan model and I ask agent to create a detailed plan for implementing the feature from the attached document. Sometimes, I add some details about tests or I point out specific directory but I like to let agent figure it out.&lt;/p&gt;
&lt;p&gt;The ADR gives agent the specific instructions what is the desired state and what it should avoid. At this stage, I should not be surprised of the plan it proposes – they should follow the decision.&lt;/p&gt;
&lt;p&gt;The plan usually contains specific files to create or modify, more or less detailed description of changes or even the ready-to-use code. Plan, comparing to ADR, it&amp;rsquo;s more sensitive – it refers to specific files, at specific locations, lines, etc. The plan for me is ephemeral. If I decide to postpone the change, I remove the plan but keep the ADR. ADR is about &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt;, the plan is about &lt;em&gt;how&lt;/em&gt; and &lt;em&gt;where&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&#34;figure center invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan_hu_6bf7a88d9cbc569.png 360w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan_hu_706347ba00b9e8ce.png 640w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan_hu_e634296b3cb8d584.png 960w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan_hu_fb5e1b33e16fe46b.png 1200w, https://szymonkrajewski.pl/tape-workflow-for-agentic-coding/adr-to-plan_hu_e21c097cc6686c89.png 1229w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1229&#34;
          height=&#34;524&#34;
           alt=&#34;Diagram showing transformation from ADR to Plan&#34; 
           title=&#34;ADR is about what and why, the plan is about how and where&#34; 
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      ADR is about what and why, the plan is about how and where
    &lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Yes, I read the plan. At this stage I want to be at least 90% sure that agent does what I want. It&amp;rsquo;s hard to be 100% sure when working with non-deterministic system. I talk with agent about the plan, ask for clarification, or I point it out to do things differently.&lt;/p&gt;
&lt;p&gt;Last but not least - &lt;a href=&#34;https://szymonkrajewski.pl/when-working-with-agent-close-the-loop/&#34;&gt;I try to close the loop&lt;/a&gt; by ensure that agent knows how to test their own changes. Sometimes I even let it commit, but I prefer to manually verify the outcome before.&lt;/p&gt;
&lt;p&gt;From time to time I use Cursor feature and spin up two agents in separate work-trees to see which one propose a better plan. I just pick the better and continue as usual.&lt;/p&gt;
&lt;h3 id=&#34;execute&#34;&gt;Execute&lt;/h3&gt;
&lt;p&gt;I let agent follow the plan while I grab the coffee and watch its during the work. Observing how agent read files and provide changes it&amp;rsquo;s sometimes breathtaking. But the more I do it this way, the new baseline for normal is set and I&amp;rsquo;m less surprised and more demanding.&lt;/p&gt;
&lt;p&gt;As I said, I shouldn&amp;rsquo;t be surprised by the outcome. The agent does it job when it follows the plan and it provides the working code according to ADR and plan. Sometimes it modifies the place we never talk about that were somehow related by the type system or to support backward compatibility. Even in well-crafted plan there may be blind spots and it looks interesting when agent can find them anyway. And sometimes not. That&amp;rsquo;s the trick.&lt;/p&gt;
&lt;p&gt;The finalisation is on my side. Quick checklist includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reading the code and verify results&lt;/li&gt;
&lt;li&gt;committing the work or letting agent commit – thanks to rich context agents provides a detailed commit messages.&lt;/li&gt;
&lt;li&gt;prepare the pull request, with the help of agent or not&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;this-is-tape&#34;&gt;This is TAPE&lt;/h2&gt;
&lt;p&gt;Talk, ADR, Plan, and Execution. This four step process gives me a fresh perspective and let me deliver faster. It works well in my current environment with things I&amp;rsquo;m working on. It surely doesn&amp;rsquo;t fit in every case, and this is the intuition I developed whether go with it or not.&lt;/p&gt;
&lt;p&gt;Is it effective? I don&amp;rsquo;t know, but for me it&amp;rsquo;s &lt;em&gt;impressive&lt;/em&gt;. I can freely form my thoughts in discussion with agent and it properly captures them, structures and follows. What matters more is the cost. I experimented with the frontier models with thinking capabilities, and some sessions costed at least a few bucks – maybe I just &lt;em&gt;talked&lt;/em&gt; too much.&lt;/p&gt;
&lt;p&gt;With agents or not, I think documenting your decisions is a good practice. At least those significant ones.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;ADR sometimes refer to Any Decision Record, may fit well if you want to store decision not strictly related to the architecture.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>My first steps into 3D printing</title>
      <link>https://szymonkrajewski.pl/my-first-steps-into-3d-printing/</link>
      <pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/my-first-steps-into-3d-printing/</guid>
      <description>&lt;p&gt;One of my hobbies is tinkering with electronics and cheap microcontrollers from the &lt;a href=&#34;https://szymonkrajewski.pl/tags/esp/&#34;&gt;ESP&lt;/a&gt; family. To complete a project, I often needed a case for things I built. One option is to buy a pre-made case, but instead I found it to be a good reason to buy a 3D printer.&lt;/p&gt;
&lt;p&gt;But this post is not about the decision process of selecting the printer. It&amp;rsquo;s about my initial experiences with this device and all those things I discovered on the way. Maybe it can help you answer &lt;em&gt;if you should buy a 3D printer or not.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-beginning&#34;&gt;The beginning&lt;/h2&gt;
&lt;p&gt;I had literally &lt;em&gt;zero&lt;/em&gt; experience with 3D printing. I&amp;rsquo;ve read about it, I&amp;rsquo;ve watched some reviews of printers, but I had never touched one before. I&amp;rsquo;ve explored communities like &lt;em&gt;Printables&lt;/em&gt; and &lt;em&gt;MakerWorld&lt;/em&gt;, and I couldn&amp;rsquo;t believe people made all of those things on their devices at home. There was something fascinating about the whole 3D printing technology that I decided to enter this world.&lt;/p&gt;
&lt;p&gt;I bought the printer, ran the slicer, and after importing the &lt;em&gt;Benchy&lt;/em&gt; model&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, I started my first print, waiting patiently and observing the process. It looked magical. The object just appeared right in front of my eyes and a few meters of plastic turned into something different, pretty, and somehow &lt;strong&gt;usable&lt;/strong&gt;. A few prints later I had a feeling that I was &lt;em&gt;downloading&lt;/em&gt; actual things from the Internet.&lt;/p&gt;
&lt;h2 id=&#34;learning-the-process&#34;&gt;Learning the process&lt;/h2&gt;
&lt;p&gt;Each subsequent print was like a test. A different color. Different brands of filament. Different material. Different parameters. Each failure has taught me something new. I learned how small details matter and how &lt;strong&gt;impatience can destroy&lt;/strong&gt; even the best model. Considering how many parameters you can set, what materials you can use, and the fact that each model is different, all of this makes the whole &lt;strong&gt;process&lt;/strong&gt; interesting and engaging.&lt;/p&gt;
&lt;p&gt;I think the real adventure starts with designing your own models. Being able to make literally anything you want or need (from plastic of course) gives a sense of freedom. For now I&amp;rsquo;m just remixing other people&amp;rsquo;s work, but maybe someday I will actually make something from scratch.&lt;/p&gt;
&lt;p&gt;Looking at the designer&amp;rsquo;s toolbox I guess it&amp;rsquo;s actually a totally &lt;em&gt;different&lt;/em&gt; skill. From my software engineer&amp;rsquo;s perspective, it&amp;rsquo;s the difference between &lt;strong&gt;using a program others wrote&lt;/strong&gt; and writing &lt;strong&gt;your own customized version&lt;/strong&gt;. Maybe you will find something that fulfills your needs, but if you know how to do it, the &lt;em&gt;limits are gone&lt;/em&gt; – at least those related to skills.&lt;/p&gt;
&lt;h2 id=&#34;reality-check-and-usability&#34;&gt;Reality check and usability&lt;/h2&gt;
&lt;p&gt;Ultimately, after some time, I learned that &lt;strong&gt;not every model is worth printing&lt;/strong&gt;. There are tons of free models, looking insanely good, that have &lt;em&gt;no real purpose&lt;/em&gt;, unless the possession is the main goal - all sorts of gadgets, toys or things you throw into the corner days after. On the other hand, I found a few useful projects like &lt;a href=&#34;https://gridfinity.xyz/&#34;&gt;Gridfinity&lt;/a&gt;, which helped me completely change my approach to organisation. Basically, when you can print anything, you also have to decide &lt;strong&gt;what not to print&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Surprisingly, the most &lt;em&gt;common&lt;/em&gt; question I get when 3D printing is mentioned is &amp;ldquo;&lt;em&gt;what do you print?&lt;/em&gt;&amp;rdquo;. It&amp;rsquo;s hard to answer it precisely, at least in my case. A 3D printer has no actual purpose – &lt;strong&gt;it&amp;rsquo;s a tool&lt;/strong&gt;. It prints stuff, the same way a standard paper printer prints. The outcome is all that matters.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_878b48edc8aafec2.webp 360w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_823b5e7b37b18e.webp 640w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_2b4fbe4469c67d39.webp 960w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_e9efd56c284bfc26.webp 1123w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_1201725d6101c2c8.jpeg 360w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_77f0256443be4e51.jpeg 640w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_4ba83388147e21c5.jpeg 960w, https://szymonkrajewski.pl/my-first-steps-into-3d-printing/drawer_hu_3c398115acf97d8c.jpeg 1123w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1123&#34;
          height=&#34;890&#34;
           alt=&#34;Drawer with electronic devices sorted in Gridfinity layout&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      One of my drawer with little electronics in Gridfinity layout
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The truth is, the process &lt;strong&gt;takes time&lt;/strong&gt;. I probably devote more time to my printer than I assumed I needed, but treating it as a hobby definitely helped me to not throw everything away. Curiosity was what drove me forward. Otherwise, I would pause after the 3rd print with no idea what to print next and why. If your goal is only the &lt;em&gt;final print&lt;/em&gt;, don&amp;rsquo;t waste your money and time. Just buy exactly what you need.&lt;/p&gt;
&lt;p&gt;Or enjoy the process, like I do.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;A little ship, classic &lt;em&gt;Hello World&lt;/em&gt; in the 3D printworld&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>When working with agent, close the loop</title>
      <link>https://szymonkrajewski.pl/when-working-with-agent-close-the-loop/</link>
      <pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/when-working-with-agent-close-the-loop/</guid>
      <description>&lt;p&gt;My first attempts with LLMs started in ChatGPT and were limited to silly debug prompts: questions containing stack trace, error logs and code snippets. The workflow was pure &lt;em&gt;copy-paste&lt;/em&gt; back and forth between the chat and editor, and – as you might guess – it didn&amp;rsquo;t &lt;strong&gt;scale&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Then agents showed up. They can execute commands and use tools. However, my first interaction with agents looked similar to the old ChatGPT flow, but instead I pasted everything into the sidebar in my editor – Cursor.&lt;/p&gt;
&lt;p&gt;At some point I asked myself: &lt;em&gt;if an agent can run arbitrary command, why can&amp;rsquo;t it also verify what it did, and fix it automatically when it fails&lt;/em&gt;? That insight - something that feels obvious today - was a breakthrough discovery for me a few months ago.&lt;/p&gt;
&lt;p&gt;Describing an agent as &amp;ldquo;&lt;em&gt;LLM using tools in the loop&lt;/em&gt;&amp;rdquo; makes sense. An agent uses whatever it can to do the task. However, I see engineers struggle with &lt;strong&gt;closing the loop&lt;/strong&gt;, like I did in the past. I asked for help and I got it, but there wasn&amp;rsquo;t a loop – that was a &lt;em&gt;ping-pong&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To close the loop, &lt;strong&gt;let agents verify what they&amp;rsquo;ve done&lt;/strong&gt;. Let them test, run commands, browse pages, capture output, and read logs. The agent shouldn&amp;rsquo;t have to ask you for those things. Try to &lt;em&gt;remove&lt;/em&gt; yourself from the flow.&lt;/p&gt;
&lt;p&gt;Removing yourself doesn&amp;rsquo;t mean giving back full control. I still manually verify results and – most of the time – I observe what agent does, but I don&amp;rsquo;t have to manually test the result after every prompt to tell the agent what&amp;rsquo;s wrong.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Using multiple WiFi networks in ESPHome</title>
      <link>https://szymonkrajewski.pl/multiple-wifi-networks-in-esphome/</link>
      <pubDate>Sat, 06 Dec 2025 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/multiple-wifi-networks-in-esphome/</guid>
      <description>&lt;p&gt;My current home network is still far away from what I would consider an ideal setup. Besides the main router, I also have a WiFi extender that creates a separate network. From time to time I change the router location, and many of my wirelessly connected devices require special attention often because I forget to update the credentials to the closest network &lt;em&gt;before&lt;/em&gt; the move. Not this time.&lt;/p&gt;
&lt;p&gt;ESPHome-powered devices used to be the most problematic case. To change WiFi configuration, you need to provide new credentials, recompile and flash the firmware. You can do this &lt;em&gt;over-the-air&lt;/em&gt;, but you only have one chance. If you forget to update the configuration before moving your access point (or if you provide a wrong password), you end up searching for the device to flash it the old-school way – over the wire – like I did many times.&lt;/p&gt;
&lt;p&gt;Recently, however, I was smart enough to check if &lt;a href=&#34;https://esphome.io/components/wifi/#connecting-to-multiple-networks&#34;&gt;ESPHome supports multiple WiFi networks in its configuration&lt;/a&gt;, and the answer is yes, it does. It works exactly as I expected.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can give ESPHome a number of WiFi networks to connect to. ESPHome will then attempt to connect to the one with the highest signal strength.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In my case I ended up with the definition for my main network and the extended one&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;wifi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;networks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ssid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;!&lt;span class=&#34;l&#34;&gt;secret wifi_ssid&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;!&lt;span class=&#34;l&#34;&gt;secret wifi_password&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;ssid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;!&lt;span class=&#34;l&#34;&gt;secret wifi_ext_ssid&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;!&lt;span class=&#34;l&#34;&gt;secret wifi_ext_password&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since then, I’ve had no issues after moving the network equipment.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still looking for a similar solution for my Raspberry Pis. Currently, I manually change the network configuration &lt;strong&gt;before&lt;/strong&gt; moving the access point – which works as long as I remember to do it in the &lt;strong&gt;right order&lt;/strong&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>How to boot Proxmox from USB using GRUB</title>
      <link>https://szymonkrajewski.pl/how-to-boot-proxmox-from-usb-using-grub/</link>
      <pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/how-to-boot-proxmox-from-usb-using-grub/</guid>
      <description>&lt;p&gt;The last time I was forced to install a fresh Linux system on my old laptop, I had opportunity to learn &lt;a href=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/&#34;&gt;how to steer GRUB to boot a system using a bootloader located on a USB drive&lt;/a&gt;. This little trick saved me dozens of hours – until I tried to do the same with a Proxmox VE image, where it initially failed.&lt;/p&gt;
&lt;p&gt;Eventually, I made it work. Here&amp;rsquo;s a short instruction – for me and for you.&lt;/p&gt;
&lt;h2 id=&#34;prepare-usb-with-proxmox-ve&#34;&gt;Prepare USB with Proxmox VE&lt;/h2&gt;
&lt;p&gt;One of my computer &lt;strong&gt;ignores&lt;/strong&gt; the order of boot devices, so the external USB device has always the lowest priority. I can&amp;rsquo;t simply insert the bootable CD or USB drive and expect it to start automatically.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this, it&amp;rsquo;s a non-zero chance you probably have the same problem and you&amp;rsquo;re looking for solution.&lt;/p&gt;
&lt;p&gt;For the record, I used &lt;em&gt;macOS&lt;/em&gt; and &lt;em&gt;balenaEtcher&lt;/em&gt; to prepare the USB drive. I&amp;rsquo;m not sure whether it makes a difference, but if your results differ from mine, that might be the reason. I used the &lt;strong&gt;Proxmox VE 8.0&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On macOS, you can install it using Homebrew:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install --cask balenaetcher
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Select the ISO and the target USB drive, then let the software do the rest.&lt;/p&gt;
&lt;h2 id=&#34;locate-the-usb-drive-and-boot-system&#34;&gt;Locate the USB drive and boot system&lt;/h2&gt;
&lt;p&gt;With the USB drive plugged in, reboot the computer.&lt;/p&gt;
&lt;p&gt;Once the GRUB screen appears, press &lt;code&gt;C&lt;/code&gt; or &lt;code&gt;ESC&lt;/code&gt; to open GRUB console. You&amp;rsquo;ll see the &lt;code&gt;grub&amp;gt;&lt;/code&gt; prompt waiting for your input.&lt;/p&gt;
&lt;p&gt;Start by listing available drives:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my case, the result showed &lt;code&gt;(hd0)&lt;/code&gt; and &lt;code&gt;(hd1)&lt;/code&gt; with multiple partitions. To inspect one, type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;hd0,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; and hit &lt;code&gt;TAB&lt;/code&gt; to see suggestions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;hd0,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Possible partitions are:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,gpt1: No known filesystem detected - Partition start at 32KiB &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,gpt2: Filesystem &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; fat, UUID 4238-7745 - Partition start at 302K1B &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,gpt3: Filesystem &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; hfsplus - Label &lt;span class=&#34;s1&#34;&gt;&amp;#39;PVE&amp;#39;&lt;/span&gt; - Last modification &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,gpt4: No known filesystem detected - Partition start at 1534730KiB &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,apple1: No known filesystem detected - Partition start at 2KiB &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,apple2: No known filesystem detected - Partition start at 32KiB &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,apple3: Filesystem &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt; hfsplus - Label &lt;span class=&#34;s1&#34;&gt;&amp;#39;PVE&amp;#39;&lt;/span&gt; - Last modification &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Partition hd0,apple4: No known filesystem detected - Partition start at 1534730KiB &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I got a pretty long list of unknown filesystems, but two of them – &lt;code&gt;gpt3&lt;/code&gt; and &lt;code&gt;apple3&lt;/code&gt; – looked promising. I picked &lt;code&gt;gpt3&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;hd0,gpt3&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point I learned that I couldn&amp;rsquo;t &lt;em&gt;chainload&lt;/em&gt; a bootloader, since there was no EFI path I expected to see. However, I found a &lt;code&gt;/boot&lt;/code&gt; directory with two interesting files: &lt;em&gt;linux26&lt;/em&gt; and &lt;em&gt;initrd.img&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To boot the Proxmox, I entered the following commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; linux /boot/linux26 ro
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; initrd /boot/initrd.img
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; boot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The system started to boot, and a few minutes later I got Proxmox VE Installer appeared on the screen.&lt;/p&gt;
&lt;h2 id=&#34;proxmox-is-built-for-bios-boot&#34;&gt;Proxmox is built for BIOS boot&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what I learned:&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;chainloader&lt;/code&gt; command is EFI-specific and won&amp;rsquo;t work here because Proxmox doesn&amp;rsquo;t include an EFI bootloader. Instead, GRUB has to &lt;strong&gt;load the kernel directly&lt;/strong&gt; – which is exactly what we do with the &lt;code&gt;linux&lt;/code&gt; and &lt;code&gt;initrd&lt;/code&gt; commands. The final &lt;code&gt;boot&lt;/code&gt; hands control to the kernel – and after a short while, the Proxmox VE installer appears.&lt;/p&gt;
&lt;p&gt;If you struggle with boot the EFI-based image, check &lt;a href=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/&#34;&gt;the short instruction from my another article&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Smart Home, My Way</title>
      <link>https://szymonkrajewski.pl/smart-home-my-way/</link>
      <pubDate>Tue, 30 Sep 2025 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/smart-home-my-way/</guid>
      <description>&lt;p&gt;When people ask me about my Smart Home, they often expect something high-tech. Instead, they find an ordinary house that just happens to be observable, controllable, and a little more convenient. The trick? A hidden layer that I can build, break, and rebuild however I want.&lt;/p&gt;
&lt;h2 id=&#34;whats-the-smart-home&#34;&gt;What&amp;rsquo;s the smart home&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t really like the term &amp;ldquo;Smart Home&amp;rdquo;. I&amp;rsquo;m not sure the official definition even exists, but most of the ones I&amp;rsquo;ve found mention &amp;ldquo;&lt;em&gt;remote control&lt;/em&gt;&amp;rdquo;, &amp;ldquo;&lt;em&gt;internet connection&lt;/em&gt;&amp;rdquo;, and &amp;ldquo;&lt;em&gt;automation&lt;/em&gt;&amp;rdquo;. Too broad, too vague.&lt;/p&gt;
&lt;p&gt;I prefer to think of the smart home as a home that is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Observable&lt;/strong&gt; – I know what&amp;rsquo;s going on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controllable&lt;/strong&gt; – I can influence it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But focusing on those two factors makes almost every home &amp;ldquo;smart&amp;rdquo;, as long as there&amp;rsquo;s a &lt;em&gt;human in the loop&lt;/em&gt;. People can manually control things when needed.&lt;/p&gt;
&lt;p&gt;What if we replace the human by automation, an algorithm, or AI? Then we need another way to access the data and control things – &lt;strong&gt;remotely&lt;/strong&gt;. And that doesn&amp;rsquo;t necessarily mean &lt;em&gt;via the Internet&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;no-vendor-lock-in&#34;&gt;No vendor lock-in&lt;/h2&gt;
&lt;p&gt;Technology lets us do impressive things, but it has one characteristic I personally can&amp;rsquo;t ignore – it gets old faster than almost anything else.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why I avoid relying on a single vendor promising to build an entire Smart Home ecosystem. I believe it works for some people and in some cases is the best choice for those who want a &lt;em&gt;controllable&lt;/em&gt; home. But I know it won&amp;rsquo;t work for me. I like tinkering much more than such systems allow, and &lt;strong&gt;I want the freedom to use whatever I want&lt;/strong&gt; – like I did with my &lt;a href=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/&#34;&gt;heat recovery system&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I set up my entire home using &lt;a href=&#34;https://www.home-assistant.io/&#34;&gt;Home Assistant&lt;/a&gt;, the open-source smart home platform. I mostly use devices that are &lt;strong&gt;cheap, easily upgradeable, and fun to play with&lt;/strong&gt;. Most of them communicate via Zigbee and Wi-Fi.&lt;/p&gt;
&lt;h2 id=&#34;a-hidden-layer&#34;&gt;A hidden layer&lt;/h2&gt;
&lt;p&gt;Some modern houses look &amp;ldquo;smart&amp;rdquo; from the first glance: ubiquitous displays, touch panels, dimmable lights controlled by quietly listening Google Home speaker in the corner of the living room.&lt;/p&gt;
&lt;p&gt;My goal was quite the opposite. I wanted to make a standard-looking house, where the &amp;ldquo;smart&amp;rdquo; components are carefully hidden. It&amp;rsquo;s just a &lt;strong&gt;regular home&lt;/strong&gt; – at the first, second, and even third glance.&lt;/p&gt;
&lt;p&gt;I like to think of it as a &lt;strong&gt;layer&lt;/strong&gt;. Something that exists on top of (or under) the things I already have. I can add things to this layer, I can remove them, but most importantly – &lt;strong&gt;I can drop everything without leaving visual changes&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dumb-devices-are-fine&#34;&gt;Dumb devices are fine&lt;/h2&gt;
&lt;p&gt;Not everything needs to be connected to the Internet. A smart oven is useless when there&amp;rsquo;s nothing inside. Also, I don&amp;rsquo;t leave such appliances running without supervision or at least one person at home.&lt;/p&gt;
&lt;p&gt;Smart fridge? I don&amp;rsquo;t buy it. Smart dishwasher that counts tabs? I always keep a spare pack in the laundry.&lt;/p&gt;
&lt;p&gt;Whenever a device asks me for my Wi-Fi password, the first question I ask myself is: what&amp;rsquo;s the point? Most of the time &lt;strong&gt;it brings no real benefit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s sometimes hard to buy appliances without &amp;ldquo;smart&amp;rdquo; features (try to buy a non-smart TV). But you don&amp;rsquo;t need to enable them or, at least, connect them to the Internet.&lt;/p&gt;
&lt;h2 id=&#34;exit-strategy&#34;&gt;Exit strategy&lt;/h2&gt;
&lt;p&gt;Because I&amp;rsquo;ve chosen the &amp;ldquo;&lt;em&gt;no vendor lock-in&lt;/em&gt;&amp;rdquo; approach, I lost the possibility of buying any kind of official support. I&amp;rsquo;m the only maintainer of my smart home layer.&lt;/p&gt;
&lt;p&gt;But being the only maintainer doesn&amp;rsquo;t mean that I&amp;rsquo;m the only user. &lt;strong&gt;My family uses it as well&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s nothing more annoying that things that &lt;em&gt;sometimes work&lt;/em&gt;. That&amp;rsquo;s why I design and build critical parts of the home in a specific way. Even if Home Assistant goes down, &lt;strong&gt;everything remains operational&lt;/strong&gt;. Lights and blinds are still controlled using switches, the alarm is unaffected, and smart plugs can be turned on and off using the button on the cover.&lt;/p&gt;
&lt;p&gt;What gets lost is only&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;observability&lt;/strong&gt; – unless some components are decentralised,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;controllability&lt;/strong&gt; – the remote part.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So turning everything off just degrades the Smart Home to regular Home. And at the end of the day, it even looks like one.&lt;/p&gt;
&lt;p&gt;This approach guarantees that even if I&amp;rsquo;m no longer here to update or fix things, my wife will continue living in the same cozy space – maybe just without a few handy automations.&lt;/p&gt;
&lt;h2 id=&#34;hobby-not-project&#34;&gt;Hobby, not project&lt;/h2&gt;
&lt;p&gt;I treat what I built as a &lt;strong&gt;hobby&lt;/strong&gt;. I like tinkering, adding components, removing old ones, testing automations, creating scenes, analysing the data, and so on. Some experiments stay as useful automation, others get disabled after a few tries – but that&amp;rsquo;s how I like it.&lt;/p&gt;
&lt;p&gt;I couldn&amp;rsquo;t predict every potential use case, but I can easily extend and adjust when needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automation is best when you don&amp;rsquo;t even notice it&lt;/strong&gt;. We sometimes do notice – especially when &lt;em&gt;it fails to trigger&lt;/em&gt;. That&amp;rsquo;s the moment when I smile to myself, thinking that there&amp;rsquo;s always room to improve.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Writing in the era of AI</title>
      <link>https://szymonkrajewski.pl/writing-in-the-era-of-ai/</link>
      <pubDate>Sat, 16 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/writing-in-the-era-of-ai/</guid>
      <description>&lt;p&gt;I started writing before the generative AI became popular. Even a simple article always took me a lot of time to write. Nowadays, authors can produce&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; and publish &lt;strong&gt;high-quality content&lt;/strong&gt; faster than ever before, with almost zero effort. On the other hand, consumers no longer have to search and browse the Internet for the answers – they can generate curated content on almost any topic just for themselves.&lt;/p&gt;
&lt;p&gt;If any answer can be instantly generated by an LLM, &lt;strong&gt;what&amp;rsquo;s the point of writing?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Today&amp;rsquo;s authors are &lt;strong&gt;content providers for AI models&lt;/strong&gt;, which crawl and digest every bit of the Internet. What happens next with this data is rather simple – it&amp;rsquo;s mixed with other human and AI-generated content to feed the next iteration of models.&lt;/p&gt;
&lt;p&gt;As a consumer, I don&amp;rsquo;t just read to acquire knowledge. I still use RSS as my primary (though not sole) source of information. What have I subscribed to? Mostly blogs. Written by &lt;strong&gt;actual people&lt;/strong&gt; who produce &lt;strong&gt;human-crafted content&lt;/strong&gt; about topics I care about and I find useful.&lt;/p&gt;
&lt;p&gt;What I enjoy most are articles with &lt;em&gt;an emotional and personal touch&lt;/em&gt;. Problem-solution articles where I can &lt;em&gt;feel the author&amp;rsquo;s struggle&lt;/em&gt; to find an answer. An opinion about a product or tool from &lt;em&gt;someone&amp;rsquo;s unique perspective&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I also browse some kind of news aggregator, like Hacker News. But instead of rabbit-holing into the pagination of the feed, I look for interesting articles and I try to learn more about their authors. &lt;em&gt;Who are they? What do they care about? Can their other writings be interesting to me?&lt;/em&gt; If so, I subscribe and wait.&lt;/p&gt;
&lt;p&gt;The content I enjoy the most has a common denominator. &lt;strong&gt;There&amp;rsquo;s a person behind&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Writing in the era of AI-generated content is more important than ever&lt;/strong&gt; – but the nature of content should evolve to reflect the new reality.&lt;/p&gt;
&lt;p&gt;I used to write articles that described ideas, but they were disconnected from who I am – I was invisible in most of the paragraphs. But since AI can generate arbitrary, expert-sounding content, it&amp;rsquo;s far more valuable to show up as a &lt;strong&gt;real person&lt;/strong&gt; with &lt;strong&gt;real opinions and thoughts&lt;/strong&gt;. It may be &lt;em&gt;less polished&lt;/em&gt; – but more &lt;em&gt;&lt;strong&gt;human&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When I started the blog, I had the idea that I&amp;rsquo;d write for myself – to &lt;em&gt;reconnect with my ideas, solidify my knowledge&lt;/em&gt;, and maybe help someone else along the way. But over the years, I realized it shouldn&amp;rsquo;t be just about &lt;em&gt;sharing knowledge&lt;/em&gt; – but about sharing &lt;em&gt;&lt;em&gt;how&lt;/em&gt; I arrived at it&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The knowledge is already out there – &lt;strong&gt;AI can spit out easily&lt;/strong&gt;. Even before AI, there was plenty of information on the Internet. What&amp;rsquo;s more valuable is &lt;strong&gt;the path to the conclusion&lt;/strong&gt; – showing the process.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How to connect the dots.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Just write as if no one is watching – and everything will be fine.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Let&amp;rsquo;s put aside the legacy concerns, such as copyrights and also privacy – but that’s a battle I don’t want to fight right now.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Monitoring Heat Recovery Performance with ESPHome and Home Assistant</title>
      <link>https://szymonkrajewski.pl/esp-heat-recovery-performance/</link>
      <pubDate>Sat, 26 Jul 2025 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/esp-heat-recovery-performance/</guid>
      <description>&lt;p&gt;In my home, I have a mechanical ventilation system with heat recovery powered by &lt;em&gt;Stiebel Eltron V-400&lt;/em&gt;. Basically, it&amp;rsquo;s a &lt;em&gt;large fan&lt;/em&gt; with air inflows and outflows ducts. Besides some built-in automation, it&amp;rsquo;s a pretty dumb device. Without a dedicated gateway (&lt;em&gt;which costs extra money&lt;/em&gt;), I can only control it or see its current parameters on the controller, either on the unit or in the hall.&lt;/p&gt;
&lt;p&gt;I thought it would be nice to &lt;strong&gt;collect some data&lt;/strong&gt; from this device, both for some kind of analysis and out of curiosity. I run Home Assistant, which can display dedicated graphs and dashboards for such purpose. And, because I recently played with ESP boards, the idea was born pretty quickly.&lt;/p&gt;
&lt;h2 id=&#34;parts&#34;&gt;Parts&lt;/h2&gt;
&lt;p&gt;For this project I used Dallas &lt;strong&gt;DS18B20&lt;/strong&gt; temperature sensors and a &lt;strong&gt;Wemos D1&lt;/strong&gt; with &lt;strong&gt;ESP8266&lt;/strong&gt; onboard. I picked the Wemos D1 because of &lt;strong&gt;5V&lt;/strong&gt; output, compact size, and because I had a one spare at this moment.&lt;/p&gt;
&lt;p&gt;The Dallas DS18B20 is well-known for its &lt;strong&gt;1-wire protocol&lt;/strong&gt;, which allows you to connect multiple temperature sensors to a &lt;strong&gt;single data line&lt;/strong&gt;. If you&amp;rsquo;ve ever wondered about the project where you can use &lt;em&gt;more than one DS18B20 temperature sensor on a single board&lt;/em&gt; – here&amp;rsquo;s one.&lt;/p&gt;
&lt;p&gt;I also had a &lt;strong&gt;6-pin LED wire&lt;/strong&gt;, which I split and used to connect the sensors to the board. For power, I used an old phone charger and a USB-C cable.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_799b66e7d573711c.webp 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_60fb0b764f1ad158.webp 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_3319950e155821d8.webp 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_363791ad27051cfd.webp 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_d947448595a18314.webp 1280w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_616588555cadac33.jpeg 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_8d7057fcd667d39e.jpeg 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_521b3975982581f7.jpeg 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_2dde58bf139997fe.jpeg 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/01_parts_hu_fe8b55b6ee02b4b3.jpeg 1280w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1280&#34;
          height=&#34;960&#34;
           alt=&#34;ESP8266 on Wemos D1, DS18B20 and pins&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Most important parts: Wemos D1 powered by ESP8266 and 5x DS18B20
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;plan&#34;&gt;Plan&lt;/h2&gt;
&lt;p&gt;The heat recovery unit has four air pipes – &lt;strong&gt;two pairs&lt;/strong&gt; of &lt;strong&gt;inflows&lt;/strong&gt; and &lt;strong&gt;outflows&lt;/strong&gt;. One pair handles air exchange with the outside, while the other circulates air within the house. The airflow setup is as follows:&lt;/p&gt;
&lt;figure class=&#34;figure center invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air_hu_99a63804b03fe951.png 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air_hu_9b433dd32bd9d88e.png 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air_hu_36690dc856318d61.png 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air_hu_a923bf076053ce8c.png 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/02_schema_pipes_air_hu_7ffebe372c0ba1d6.png 1559w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1559&#34;
          height=&#34;1041&#34;
           alt=&#34;Schema of Stiebel Eltron V-400 airflow&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Schematic diagram of Stiebel Eltron V-400 airflow pipes
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I expected these four pipes would carry air at different temperatures. Inside each one, I placed a single DS18B20 sensor. I added an extra one to measure the temperature in the attic, where the ventilation unit is installed.&lt;/p&gt;
&lt;figure class=&#34;figure center invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/03_schema_pipes_sensors.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/03_schema_pipes_sensors_hu_5857d6e37e03b2d8.png 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/03_schema_pipes_sensors_hu_2435b1214fce6f95.png 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/03_schema_pipes_sensors_hu_380fd3550073b43.png 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/03_schema_pipes_sensors_hu_4faa14be93424d42.png 1130w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1130&#34;
          height=&#34;1138&#34;
           alt=&#34;Schema of DS18B20 placement within the Stiebel Eltron V-400 airflow pipes&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Schematic diagram of DS18B20 placement within the Stiebel Eltron V-400 airflow pipes
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;As I mentioned earlier, the DS18B20 uses 1-wire interface, so all sensors can share the same data line. Instead of wiring sensors one into another in a form of chain, I connected them in a &lt;strong&gt;star topology,&lt;/strong&gt; where each sensor has its own dedicated wires, but shares a common data pin.&lt;/p&gt;
&lt;h2 id=&#34;preparation&#34;&gt;Preparation&lt;/h2&gt;
&lt;p&gt;First – the sensors. Soldering the first one was fun, but each subsequent one became a bit more &lt;em&gt;tedious&lt;/em&gt;. I soldered each sensor to a wire strip &lt;strong&gt;about half a meter long&lt;/strong&gt;.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_89f0d954bbf6a94f.webp 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_752cb1802d581656.webp 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_ce9a57a6c9d0ae09.webp 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_b1f7733b628aaa9e.webp 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_66f03c10aeb90286.webp 1280w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_48722f5ca3e57b13.jpeg 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_e451491a1cfe3ed8.jpeg 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_66b4626232a91077.jpeg 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_2b489ad170dccaa0.jpeg 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/04_prep_wires_hu_5c8e0f3f6a98bc4d.jpeg 1280w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1280&#34;
          height=&#34;960&#34;
           alt=&#34;Wires prepared to be soldered to DS18B20 sensor&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Wires prepared to be soldered to DS18B20 sensor
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_561206786d7614da.webp 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_11545d43862544aa.webp 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_70e1e65a0e8fb92e.webp 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_89b4dae15051d494.webp 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_34d7939fe0d186f9.webp 1280w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_f8f0f4245ff058d1.jpeg 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_dea43ec8231debe4.jpeg 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_e1e66854457f0e16.jpeg 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_54c8923dd1f2c8d2.jpeg 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/05_prep_soldering_hu_5d62578189796b22.jpeg 1280w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1280&#34;
          height=&#34;960&#34;
           alt=&#34;DS18B20 sensor with wires soldered&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      DS18B20 sensor with wires soldered
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_9de0209d33bae570.webp 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_64dee8f8c2c3c414.webp 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_8c70aaf239117ab3.webp 960w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_5d3af3c4ae4bf665.jpeg 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_6c8f4a0e7639ffc6.jpeg 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/06_prep_securing_hu_3cdefa0dbe644094.jpeg 960w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;960&#34;
          height=&#34;1280&#34;
           alt=&#34;DS18B20 sensor with soldered wires and secured connections&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      DS18B20 sensor with soldered wires and secured connections
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I wanted to connect everything on the breadboard. Unfortunately, I realised I was still missing the Dupont crimper needed to prepare proper pins for the sensor wires. I ended up using Wago connectors to make all connections.&lt;/p&gt;
&lt;p&gt;I connected the data line to pin &lt;strong&gt;D13&lt;/strong&gt; (&lt;em&gt;GPIO13&lt;/em&gt;) on the Wemos D1.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_22522d15a9bdbf74.webp 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_bd8144235e17d01a.webp 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_b585119db63f0e81.webp 951w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_e26374eeaeaaf82e.jpeg 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_64dad6c7405783ec.jpeg 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/07_breadcrumb_hu_d1ef0ced5eed2b81.jpeg 951w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;951&#34;
          height=&#34;966&#34;
           alt=&#34;ESP8266 Wemos D1 with the DS18B20 sensors connected using Wago connectors on breadcrumb board&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      ESP8266 Wemos D1 with the DS18B20 sensors connected using Wago connectors on breadcrumb board
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;firmware&#34;&gt;Firmware&lt;/h2&gt;
&lt;p&gt;I didn&amp;rsquo;t write the ESP firmware myself. Instead, I decided to use &lt;strong&gt;ESPHome&lt;/strong&gt; which works seamlessly with Home Assistant.&lt;/p&gt;
&lt;p&gt;Setting up the ESP with ESPHome was one of the &lt;em&gt;most magical experiences&lt;/em&gt; I&amp;rsquo;ve had. Even though I enjoy digging into programming and writing things from scratch, being able to generate firmware from a &lt;strong&gt;YAML&lt;/strong&gt; file is just wonderful.&lt;/p&gt;
&lt;h3 id=&#34;identifying-sensors&#34;&gt;Identifying sensors&lt;/h3&gt;
&lt;p&gt;I started with a basic ESPHome project, configured WiFi connection and set up the &lt;code&gt;one_wire&lt;/code&gt; protocol.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;one_wire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;gpio&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;pin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;GPIO13&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, after flashing the firmware, ESP printed the addresses of the connected sensors&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:021]:   Pin: GPIO13
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:080]:   Found devices:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:082]:     0x48e2a7bc12fa3651 (DS18B20)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:082]:     0xc903deab6719b425 (DS18B20)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:082]:     0x1f64b28d4ca713ff (DS18B20)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:082]:     0xa82fbac2e19d54e7 (DS18B20)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[13:47:40][C][gpio.one_wire:082]:     0x702c19f8ab65e038 (DS18B20)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All of my sensors were visible, but there was one problem. I wanted to name each sensor correctly, but I didn&amp;rsquo;t know which address was assigned to each one. Fortunately, the sensors are very sensitive and I just registered all of them, assigning the names. Then, I &lt;strong&gt;warmed up&lt;/strong&gt; a specific one and observed which temperature reading increased.&lt;/p&gt;
&lt;p&gt;That could have been enough, but I decided to use the different colored wires for inner flow and outer flow temperatures, so I needed to update the names accordingly. I ended up with the list as follows.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;sensor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dallas_temp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_inner_inflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0x48e2a7bc12fa3651&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_inner_inflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dallas_temp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_inner_outflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0xc903deab6719b425&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_inner_outflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dallas_temp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_outer_inflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0x1f64b28d4ca713ff&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_outer_inflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dallas_temp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0xa82fbac2e19d54e7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_attic&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dallas_temp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0x702c19f8ab65e038&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;temp_outer_outflow&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;temperature-efficiency&#34;&gt;Temperature efficiency&lt;/h3&gt;
&lt;p&gt;With all those temperatures available, I configured another sensor to calculate the &lt;em&gt;temperature efficiency&lt;/em&gt; of the heat exchanger.&lt;/p&gt;
&lt;p&gt;The formula is&lt;/p&gt;
&lt;p&gt;$$
\eta = \frac{T_2 - T_1}{T_3 - T_1} \times 100%$
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$T_1$ – outdoor temperature before heat exchanger&lt;/li&gt;
&lt;li&gt;$T_2$ – temperature of supply air after heat exchanger&lt;/li&gt;
&lt;li&gt;$T_3$ – temperature of extracted air from rooms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ESPHome definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;sensor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;platform&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;efficiency&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;filters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;unit_of_measurement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;state_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;measurement&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;accuracy_decimals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;lambda&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|-&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      float t1 = id(temp_outer_inflow).state;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      float t2 = id(temp_inner_inflow).state;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      float t3 = id(temp_inner_outflow).state;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      float eff = (t2 - t1) / (t3 - t1);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;      return eff * 100;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;update_interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;60s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I could have done the same using a Home Assistant template sensor. However, doing it directly on the ESP allowed me to &lt;strong&gt;offload&lt;/strong&gt; some work from the main instance and bring the calculation &lt;strong&gt;closer to the data source&lt;/strong&gt;. The data is already available to the ESP, so it can expose the pre-calculated value as part of its API.&lt;/p&gt;
&lt;h2 id=&#34;deployment&#34;&gt;Deployment&lt;/h2&gt;
&lt;p&gt;The initial deployment was &lt;em&gt;a bit different&lt;/em&gt; from a traditional software development workflow. I had to visit my attic, gently place all the sensors inside the air pipes according to the plan, and power up the Wemos D1 board.&lt;/p&gt;
&lt;p&gt;Once everything was in place, I switched over to Home Assistant.&lt;/p&gt;
&lt;h2 id=&#34;home-assistant-dashboard&#34;&gt;Home Assistant dashboard&lt;/h2&gt;
&lt;p&gt;Since I already had ESPHome integrated with Home Assistant, the new device was &lt;strong&gt;automatically discovered&lt;/strong&gt; and all exposed entities were registered. I tweaked the names and prepared a &lt;strong&gt;dashboard&lt;/strong&gt; to have a quick overview of my ventilation unit.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview_hu_8573be585107496.png 360w, https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview_hu_83fff82c8fd02e4.png 640w, https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview_hu_afc7388d663b6795.png 960w, https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview_hu_dec8000549d2e1bb.png 1200w, https://szymonkrajewski.pl/esp-heat-recovery-performance/08_dashboard_preview_hu_62775fc0b00f239a.png 1592w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1592&#34;
          height=&#34;1052&#34;
           alt=&#34;Custom Home Assistant showing temperature on each sensor&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The image shows low efficiency and temperatures, that don&amp;rsquo;t look great in terms of heat exchange but no worries – that&amp;rsquo;s because the unit was operating in &lt;em&gt;bypass mode&lt;/em&gt; at the time. I&amp;rsquo;ll provide a better image when it&amp;rsquo;s cooler.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;m not going to show you step-by-step configuration here, but if you think it would be useful for you, just &lt;a href=&#34;https://szymonkrajewski.pl/contact/&#34;&gt;drop me a message&lt;/a&gt;. I&amp;rsquo;ll be happy to share the details.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;This setup is working &lt;strong&gt;flawlessly&lt;/strong&gt; for half a year. In the next iteration, I&amp;rsquo;m going to give the ESP a dedicated case and replace the Wago connector. But once it’s working, the urge to improve it is lower than I expected.&lt;/p&gt;
&lt;p&gt;I hope you found this write-up useful. Thanks for reading!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Test your plans by reducing time</title>
      <link>https://szymonkrajewski.pl/test-your-plans-by-reducing-time/</link>
      <pubDate>Thu, 20 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/test-your-plans-by-reducing-time/</guid>
      <description>&lt;p&gt;I couldn&amp;rsquo;t find any better phrase that describes the approach I&amp;rsquo;ve learned in the last year. It&amp;rsquo;s a simple piece of advice: &lt;em&gt;Test your plans by reducing time&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easy to fall into the trap of thinking that the amount of our free time is stable and we&amp;rsquo;ll also be able to spend the same number of hours on &lt;em&gt;things&lt;/em&gt; in the future.&lt;/p&gt;
&lt;p&gt;Sooner or later, you&amp;rsquo;ll need to put most of your effort and attention in a specific direction. Eventually, you&amp;rsquo;ll push every insignificant activity in the background, and you may struggle to find even minutes to deal with the rest of your stuff. Starting of studies, new relationship, new job, starting a family – they are only examples of events that can change your priority. If you want to stick to your routines, you need to change the approach to performing tasks.&lt;/p&gt;
&lt;p&gt;The assumption behind the main idea leads to another rule, which I named &lt;em&gt;Plan for a fraction of your time&lt;/em&gt; and it goes as follows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When you plan to do something, do it in a way to derive value from this activity, even if you&amp;rsquo;ll spend only 10%&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; of the time you initially planned.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Take a look at the following examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you plan to learn a new language for 30 minutes each day? What if you have only 5?&lt;/li&gt;
&lt;li&gt;Do you want to read a book for one hour? Imagine you have only 10 minutes.&lt;/li&gt;
&lt;li&gt;Do you have an article idea and need a few hours? Imagine you have a few 15 minutes-long blocks of uninterrupted time.&lt;/li&gt;
&lt;li&gt;Do you do a weekly review for 30 minutes? Is it possible to do it in 5 minutes?&lt;/li&gt;
&lt;li&gt;Do you want to journal for 15 minutes? Try to sum up everything in 2 minutes.&lt;/li&gt;
&lt;li&gt;You have a side project – imagine you work on it only for 15 minutes per day.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do these activities still make sense for you if you spend less time on them? If not, try to optimize them and make them worthwhile even with limited time. It helps you &lt;strong&gt;focus on the essential part&lt;/strong&gt; of every task.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t mean that you should do everything in a hurry. Instead, you should prepare yourself to do your stuff even if you have not enough time.&lt;/p&gt;
&lt;p&gt;If there is no chance to finish a one-off task in a short time, think about producing &lt;strong&gt;intermediary results&lt;/strong&gt; that are still valuable. It would help if you could &lt;strong&gt;track&lt;/strong&gt; where you are and what to do next. Sometimes It&amp;rsquo;s hard to find an uninterrupted one-hour-long block, but it&amp;rsquo;s possible to find a few ten minutes-long intervals during the day. It&amp;rsquo;s not a deep-work-like experience, but the environment isn&amp;rsquo;t always perfect, and it&amp;rsquo;s your job to find a way to do your best even if conditions are unfavorable.&lt;/p&gt;
&lt;p&gt;You may think this is a problem with an organization. Could be. You can liberate some time by abandoning some self-imposed duties. However, if you find that your routines work well for you, it&amp;rsquo;s better to keep them small rather than drop them altogether.&lt;/p&gt;
&lt;p&gt;This article is what I&amp;rsquo;d like to tell my younger self. Now it acts as a reminder for me. Maybe also for you?&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Specific value does not matter, but it should be lower than expected.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Avoid using global git identity</title>
      <link>https://szymonkrajewski.pl/avoid-using-global-git-identity/</link>
      <pubDate>Mon, 29 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/avoid-using-global-git-identity/</guid>
      <description>&lt;p&gt;When I clone a new project, I put the proper email address (e.g., a company email) as an author email in the local config stored in the repository. It worked fine until I started working for a client whose project is spread across many repositories that I cloned when needed.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t count how many times I forgot to put the correct email into the local config of the newly cloned repository before committing anything into it – just because I was in a rush. In effect, my personal email (which occupies my global configuration) has been leaking many times into commits, sentenced to last forever in the git history. I&amp;rsquo;ve probably done it more times than I even realize.&lt;/p&gt;
&lt;p&gt;To avoid making these mistakes, &lt;strong&gt;don&amp;rsquo;t use a global git configuration for identity&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;remove-global-configuration&#34;&gt;Remove global configuration&lt;/h2&gt;
&lt;p&gt;To remove the global identity, use the following commands. If we use the same name everywhere, it makes sense to keep it set globally.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global --unset user.email
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global --unset user.name
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this is not enough. Git will try to guess the name and email address based on the user profile and hostname. We need to instruct git to use only configuration files.&lt;/p&gt;
&lt;h2 id=&#34;use-provided-configuration-only&#34;&gt;Use provided configuration only&lt;/h2&gt;
&lt;p&gt;Since &lt;a href=&#34;https://github.blog/2016-03-28-git-2-8-has-been-released/#dont-guess-my-identity&#34;&gt;version 2.8&lt;/a&gt;, git has a dedicated setting called &lt;code&gt;user.useConfigOnly&lt;/code&gt; to control if it should guess the identity or use the configuration only. According &lt;a href=&#34;https://git-scm.com/docs/git-config#Documentation/git-config.txt-useruseConfigOnly&#34;&gt;to documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Instruct Git to avoid trying to guess defaults for &lt;code&gt;user.email&lt;/code&gt; and &lt;code&gt;user.name&lt;/code&gt;, and instead retrieve the values only from the configuration. For example, if you have multiple email addresses and would like to use a different one for each repository, then with this configuration option set to &lt;code&gt;true&lt;/code&gt; in the global config along with a name, git will prompt you to set up an email before making new commits in a newly cloned repository. Defaults to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The funny thing is that even documentation points out this as a potential solution to my problem.&lt;/p&gt;
&lt;p&gt;To change this option, we can use the following command&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --global user.useConfigOnly &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, if we try to commit anything to the repository that has no configured author&amp;rsquo;s name and email, git shows us the message about missing configuration.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Author identity unknown
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*** Please tell me who you are.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  git config --global user.email &amp;#34;you@example.com&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  git config --global user.name &amp;#34;Your Name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;to set your account&amp;#39;s default identity.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Omit --global to set the identity only in this repository.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fatal: no email was given and auto-detection is disabled
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s hard to commit anything under a mistaken identity, but we need to configure each repository individually. To make it easier, we can prepare a few small scripts.&lt;/p&gt;
&lt;h2 id=&#34;prepare-scripts-to-set-up-identity&#34;&gt;Prepare scripts to set up identity&lt;/h2&gt;
&lt;p&gt;The idea is simple. Whenever we get the message about the unknown identity, we can invoke a simple bash script to configure the proper profile.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --local user.name &lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon Krajewski&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --local user.email &lt;span class=&#34;s2&#34;&gt;&amp;#34;git@yourdomain.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon Krajewski &amp;lt;git@yourdomain.com&amp;gt; applied as git user to this repository.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s call it &lt;code&gt;apply_personal_profile&lt;/code&gt; and make it executable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x apply_personal_profile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can create as many &lt;em&gt;profiles&lt;/em&gt; as you need. I prepared a personal profile, a company profile, and one related to the client I currently work for.&lt;/p&gt;
&lt;p&gt;I store my profiles in the dedicated directory &lt;code&gt;~/Utilities/git/profiles&lt;/code&gt;. I added this directory to &lt;code&gt;$PATH&lt;/code&gt; global variable in my &lt;code&gt;.zshrc&lt;/code&gt;, to make these scripts available from everywhere. Alternatively, you can symlink each script into &lt;code&gt;/usr/local/bin&lt;/code&gt;, and that&amp;rsquo;s also OK.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# .zshrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PATH&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:/Users/szymon/Utilities/git/profiles&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;apply-author-profile-to-repository&#34;&gt;Apply author profile to repository&lt;/h2&gt;
&lt;p&gt;Whenever you clone or init a new repository, run one of the created scripts. If you forget about this, don&amp;rsquo;t worry – git won&amp;rsquo;t let you commit without configured email.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apply_peronal_profile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;other-approaches&#34;&gt;Other approaches&lt;/h2&gt;
&lt;h3 id=&#34;the-simpler-one&#34;&gt;The simpler one&lt;/h3&gt;
&lt;p&gt;The script isn&amp;rsquo;t complicated, but it&amp;rsquo;s also not perfect. I&amp;rsquo;m sure it could be better – no duplications, basic checks, and so on – but I wanted to &lt;a href=&#34;https://szymonkrajewski.pl/think-big-start-small/&#34;&gt;start small&lt;/a&gt;. Surprisingly, this is a single line that matters&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git config --local user.email &lt;span class=&#34;s2&#34;&gt;&amp;#34;git@yourdomain.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can make an alias in your shell or search in command history for that one. The name is usually the same across profiles and can be configured globally.&lt;/p&gt;
&lt;h3 id=&#34;automatic&#34;&gt;Automatic&lt;/h3&gt;
&lt;p&gt;There are also tools that can do automatic profile switching based on the directory name, location, or existence of a particular file, e.g. &lt;a href=&#34;https://git-scm.com/docs/git-config#_conditional_includes&#34;&gt;gitconfig&amp;rsquo;s conditional includes&lt;/a&gt; or &lt;a href=&#34;https://direnv.net&#34;&gt;direnv&lt;/a&gt;, but I haven&amp;rsquo;t tested them. I took the most straightforward approach – improvements may come with time if needed.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>How to remove hard spaces from file using awk</title>
      <link>https://szymonkrajewski.pl/remove-hard-spaces-from-file-using-awk/</link>
      <pubDate>Sat, 07 Aug 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/remove-hard-spaces-from-file-using-awk/</guid>
      <description>&lt;p&gt;Some tasks seem easy when we perform them manually, but new things come to light once we want to automate them. What can go wrong if we remove hard spaces from a text file using a script?&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s a one-time operation, we run the editor, use the find-and-replace functionality, and that&amp;rsquo;s enough. Otherwise, we need to find a more &lt;em&gt;automatic-friendly&lt;/em&gt; approach.&lt;/p&gt;
&lt;h2 id=&#34;the-literal-approach&#34;&gt;The literal approach&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m a fan of simple tools, so I use &lt;strong&gt;awk&lt;/strong&gt; for that case. Let&amp;rsquo;s say that we want to clean up the CSV file. We likely end up with something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;awk -F&lt;span class=&#34;s2&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ gsub(/ /, &amp;#34;&amp;#34;); }1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;filename.csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it works, we can end our solution here, but there is still one issue. How can we be sure that the provided pattern is an actual hard space?&lt;/p&gt;
&lt;p&gt;I went into trouble because I copied the space character right from the terminal window, and after minutes of failing attempts, it turns out that the terminal printed soft spaces instead of hard ones. Be aware, that some software may also change whitespaces on their own.&lt;/p&gt;
&lt;p&gt;Even if we successfully copied and pasted the hard spaces into our script, we need to leave a comment to let our future self notice that there is a hard space, not &lt;em&gt;just&lt;/em&gt; space.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Hey, an important fact, this is a hard space!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#                 \ /&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;awk -F&lt;span class=&#34;s2&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ gsub(/ /, &amp;#34;&amp;#34;); }1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;filename.csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;hex-to-the-rescue&#34;&gt;Hex to the rescue&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;gsub&lt;/code&gt; function expects two parameters: the pattern and string to replace. However, we can provide a byte sequence instead of just characters.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s check the byte of the hard space. Just copy the hard space from the file using the app that preserves whitespaces characters into the terminal and run &lt;strong&gt;hexdump&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; hexdump -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output should be similar to this one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;00000000&lt;/span&gt;  c2 a0 0a                                          &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;...&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;00000003&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We got three bytes. The last one is &lt;code&gt;0a&lt;/code&gt;, which corresponds to &lt;em&gt;line feed&lt;/em&gt; and indicates &lt;em&gt;the end of the line&lt;/em&gt;. So, the hard space consists of two bytes: &lt;code&gt;c2&lt;/code&gt; and &lt;code&gt;a0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s use them within our script. To tell the &lt;code&gt;gsub&lt;/code&gt; function that we provide hexadecimal values, prepend the bytes with &lt;code&gt;\x&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;awk -F&lt;span class=&#34;s2&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ gsub(/\xc2\xa0/, &amp;#34;&amp;#34;); }1&amp;#39;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;filename.csv&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Great! We&amp;rsquo;ve removed hard spaces from the file. Use it also if you need to replace them with soft ones.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/a/27056408&#34;&gt;https://stackoverflow.com/a/27056408&lt;/a&gt; – use of hexdump tool&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>How to decrypt PDF files on macOS</title>
      <link>https://szymonkrajewski.pl/decrypt-pdf-on-macos/</link>
      <pubDate>Thu, 08 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/decrypt-pdf-on-macos/</guid>
      <description>&lt;p&gt;Some companies send encrypted PDF documents. If you&amp;rsquo;re a macOS user, &lt;em&gt;Preview&lt;/em&gt; will ask you for the password each time you use &lt;em&gt;quick look&lt;/em&gt; or open the document. Moreover, if you print that document, you&amp;rsquo;ll end up with a blank sheet of paper. At least I got it. So secure.&lt;/p&gt;
&lt;p&gt;To print an encrypted PDF file, you have to decrypt it first.&lt;/p&gt;
&lt;p&gt;In macOS, you may use a command-line tool called &lt;strong&gt;qpdf&lt;/strong&gt;. The easiest way to install it is to use homebrew.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install qpdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, navigate to the directory containing your encrypted PDF file and run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qpdf --decrypt --password&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;PASSWORD_TO_DECRYPT ENCRYPTED_FILE.pdf DECRYPTED_FILE.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you need to decrypt many PDF files which share the same password, you can use the loop. Below is an example loop in &lt;em&gt;zsh&lt;/em&gt; shell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; file in *.pdf&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; qpdf --decrypt --password&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;PASSWORD_TO_DECRYPT &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt; decrypted_&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;; done
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Missing article image on Twitter due to GetMatch in Hugo template</title>
      <link>https://szymonkrajewski.pl/missing-article-image-on-twitter-card/</link>
      <pubDate>Sun, 14 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/missing-article-image-on-twitter-card/</guid>
      <description>&lt;p&gt;Recently I received notification that &lt;a href=&#34;https://twitter.com/kjendrzyca/status/1370726043842121730&#34;&gt;somebody post information about my article on Twitter&lt;/a&gt; (thanks!). Twitter usually renders a link to blog posts as nice-looking cards. The card to my article shown up without a featured image.&lt;/p&gt;
&lt;p&gt;I checked my Twitter feed, and some of my other entries also suffer from missing featured images.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/missing-article-image-on-twitter-card/twitter-card-missing.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/missing-article-image-on-twitter-card/twitter-card-missing_hu_a3b3fa663eb5010.png 360w, https://szymonkrajewski.pl/missing-article-image-on-twitter-card/twitter-card-missing_hu_6d6c5459e0f84222.png 587w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;587&#34;
          height=&#34;250&#34;
           alt=&#34;Schematic responses from the API when user calls _/products_ endpoint&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Schematic responses from the API when user calls &lt;em&gt;/products&lt;/em&gt; endpoint
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;After checking some of my articles in &lt;a href=&#34;https://cards-dev.twitter.com/validator&#34;&gt;Twitter&amp;rsquo;s Card Validator&lt;/a&gt;, It turned out that some of them have missing metadata that twitter needs to render the cover image.&lt;/p&gt;
&lt;p&gt;I found that my theme uses &lt;a href=&#34;https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/twitter_cards.html&#34;&gt;Hugo&amp;rsquo;s internal template&lt;/a&gt; to render Twitter&amp;rsquo;s metadata. Unfortunately, nothing within the template gave me an answer at first glance. I found out that to render &lt;code&gt;twitter:image&lt;/code&gt; metadata correctly, there must be an image with &lt;em&gt;feature&lt;/em&gt;, &lt;em&gt;cover&lt;/em&gt;, or &lt;em&gt;thumbnail&lt;/em&gt; in the filename. I follow this convention, even though I didn&amp;rsquo;t know about it.&lt;/p&gt;
&lt;p&gt;Articles that have missing cover have one in common. Since I use &lt;em&gt;Page Bundles&lt;/em&gt;, after &lt;a href=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/&#34;&gt;migrating from WordPress to Hugo&lt;/a&gt;, I put images in the directory called &lt;code&gt;images&lt;/code&gt; for each article.&lt;/p&gt;
&lt;p&gt;As you see carefuly, there is a problem with &lt;code&gt;GetMatch&lt;/code&gt; function used in the template. Let&amp;rsquo;s look at the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;featured&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GetMatch&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;*feature*&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;featured&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}}{{&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;featured&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GetMatch&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;{*cover*,*thumbnail*}&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}}{{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://gohugo.io/content-management/page-resources/#pattern-matching&#34;&gt;According to the Hugo documentation&lt;/a&gt;, this specific usage of &lt;code&gt;GetMatch&lt;/code&gt; will work only for files in the root of each Page Bundle. The path &lt;code&gt;images/featured.jpg&lt;/code&gt; doesn&amp;rsquo;t match the &lt;code&gt;*feature*&lt;/code&gt; pattern.&lt;/p&gt;
&lt;p&gt;So I moved featured images to the root of each post directory. Now, my old articles have started rendering proper metadata, giving Twitter enough data to display the cover image on the feed.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Line break in Markdown</title>
      <link>https://szymonkrajewski.pl/line-break-in-markdown/</link>
      <pubDate>Fri, 12 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/line-break-in-markdown/</guid>
      <description>&lt;p&gt;There are a few different methods to enforce a line break in the same paragraph in Markdown syntax: HTML&amp;rsquo;s &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt;, backslash and double space. Let&amp;rsquo;s consider each case.&lt;/p&gt;
&lt;h2 id=&#34;html-tag&#34;&gt;HTML tag&lt;/h2&gt;
&lt;p&gt;First of all, because Markdown syntax allows using simple HTML, the &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt; tag is one option.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-md&#34; data-lang=&#34;md&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is the first line&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;br&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And this is another line...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Markdown, however, is a format that should be easy to write and easy to read. HTML tags, don&amp;rsquo;t matter how simple they are, in my opinion, make the document a bit messy.&lt;/p&gt;
&lt;p&gt;My rule of thumb is simple – I should be able to read the Markdown text comfortably even if it&amp;rsquo;s not parsed.&lt;/p&gt;
&lt;h2 id=&#34;backslash&#34;&gt;Backslash&lt;/h2&gt;
&lt;p&gt;The second option supported by some Markdown parsers (like &lt;em&gt;Goldmark&lt;/em&gt; that Hugo uses) is the &lt;strong&gt;backslash&lt;/strong&gt; as the last character in the line.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-md&#34; data-lang=&#34;md&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is the first line\
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And this is another line...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Much better than the line break tag in HTML. It&amp;rsquo;s a good choice for people who write single paragraphs across many lines and need a visual indicator of the line break.&lt;/p&gt;
&lt;h2 id=&#34;two-spaces&#34;&gt;Two spaces&lt;/h2&gt;
&lt;p&gt;Fortunately, there is also an option for me. &lt;strong&gt;Two spaces&lt;/strong&gt; at the end of the line also produce a line break.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-md&#34; data-lang=&#34;md&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is the first line{space}{space}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And this is another line...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t use to split paragraphs across multiple lines. That&amp;rsquo;s why I don&amp;rsquo;t need any particular indicator that tells me there is a line break. The actual line break is sufficient for me by itself.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://daringfireball.net/projects/markdown/syntax#p&#34;&gt;https://daringfireball.net/projects/markdown/syntax#p&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gist.github.com/shaunlebron/746476e6e7a4d698b373&#34;&gt;https://gist.github.com/shaunlebron/746476e6e7a4d698b373&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Restore files from encrypted iPhone backup</title>
      <link>https://szymonkrajewski.pl/restore-files-from-encrypted-iphone-backup/</link>
      <pubDate>Wed, 10 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/restore-files-from-encrypted-iphone-backup/</guid>
      <description>&lt;p&gt;I do not use iCloud backup for my iPhone. Instead, I do local encrypted backup, more or less regularly. I wanted to browse and find something from one of these backup.&lt;/p&gt;
&lt;p&gt;Did you know, that there is no native way on macOS to browse and restore something from your &lt;em&gt;encrypted&lt;/em&gt; iPhone backup? I didn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;If you have &lt;strong&gt;encypted backup&lt;/strong&gt;, you&amp;rsquo;ve a problem. The Internet is full of software that solve this issue. However, I haven&amp;rsquo;t want to deal with commercial, third party software.&lt;/p&gt;
&lt;p&gt;Fortunately, a few years ago, smart guys (&lt;em&gt;Jean-Baptiste Bédrune&lt;/em&gt; and &lt;em&gt;Jean Sigwald&lt;/em&gt;) &lt;a href=&#34;https://code.google.com/archive/p/iphone-dataprotection/&#34;&gt;found a way to access the data&lt;/a&gt;. User &lt;em&gt;andrewdotn&lt;/em&gt; on StackOverflow &lt;a href=&#34;https://stackoverflow.com/a/13793043&#34;&gt;described the situation&lt;/a&gt; and how to deal with encrypted backups – in theory and in practice. Ultimately, I found that GitHub&amp;rsquo;s user &lt;em&gt;jsharkey13&lt;/em&gt; shared a ready-to-go solution as a python library, called &lt;a href=&#34;https://github.com/jsharkey13/iphone_backup_decrypt&#34;&gt;iphone_backup_decrypt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had opportunity to test the process of restoring the files and can confirm, that everything worked great!&lt;/p&gt;
&lt;p&gt;Some context and environent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;encrypted local backup of iOS 14.4&lt;/li&gt;
&lt;li&gt;macOS Catalina 10.15.7&lt;/li&gt;
&lt;li&gt;Python 3.9.1 + libraries required by &lt;em&gt;iphone_backup_decrypt&lt;/em&gt; (installed by pip3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I utilized the example code from the repository.&lt;/p&gt;
&lt;p&gt;If you need to withdraw data from encrypted iPhone backup, it&amp;rsquo;s the one of potential solutions.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Using local DNS in dhcpcd causes to ignore others DNS</title>
      <link>https://szymonkrajewski.pl/dhcpcd-localhost-dns-problem/</link>
      <pubDate>Sat, 06 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/dhcpcd-localhost-dns-problem/</guid>
      <description>&lt;p&gt;I struggled with the strange behavior of &lt;em&gt;dhcpcd&lt;/em&gt; on my Raspberry Pi powered by Raspbian. The RPi does many things on my local network, including ad-blocking thanks to the &lt;em&gt;pihole&lt;/em&gt; service.&lt;/p&gt;
&lt;p&gt;I run pihole&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; as a docker container, and it does its job perfectly well. Thanks to the port binding, I could bind port 53 (DNS) directly to the host and use its address as a DNS server.&lt;/p&gt;
&lt;h2 id=&#34;network-configuration&#34;&gt;Network configuration&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;interface&lt;/span&gt; wlan0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; ip_address=192.168.1.102/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; routers=192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; domain_name_servers=127.0.0.1 &lt;span class=&#34;m&#34;&gt;1.1.1.1&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;interface&lt;/span&gt; eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; ip_address=192.168.1.2/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; routers=192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; domain_name_servers=127.0.0.1 &lt;span class=&#34;m&#34;&gt;1.1.1.1&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For both interfaces (&lt;em&gt;wlan0&lt;/em&gt; and &lt;em&gt;eth0&lt;/em&gt;), I configured static IP addresses and DNS, using my local pihole service as a primary DNS. I didn&amp;rsquo;t want to rely only on my local instance because in case of any error or even docker image update, I&amp;rsquo;d end up without DNS. Unfortunately, despite the configuration, the system saw only one DNS – &lt;em&gt;127.0.0.1&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I found in &lt;code&gt;resolv.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Generated by resolvconf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The same happened even if I reorder DNS in the configuration. Below change generated the same &lt;code&gt;resolv.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; domain_name_servers=1.1.1.1 &lt;span class=&#34;m&#34;&gt;127.0.0.1&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I removed the &lt;em&gt;127.0.0.1&lt;/em&gt; entirely from the configuration to test if it made any difference. After reboot, &lt;code&gt;resolv.conf&lt;/code&gt; presented a proper set of name servers.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Generated by resolvconf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1.1.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s form the problem: using localhost (&lt;em&gt;127.0.0.1&lt;/em&gt;) address in the &lt;code&gt;domain_name_servers&lt;/code&gt; option in &lt;em&gt;dhcpcd&lt;/em&gt; causes the rest of DNS to be ignored.&lt;/p&gt;
&lt;h2 id=&#34;the-resolvconfhead-file-as-a-solution&#34;&gt;The &lt;em&gt;resolv.conf.head&lt;/em&gt; file as a solution&lt;/h2&gt;
&lt;p&gt;After some research and reading docs, I stumbled upon a &lt;code&gt;resolv.conf.head&lt;/code&gt; file, which is a way to prepend an additional name server to the&lt;code&gt; resolv.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;I changed the configuration of &lt;em&gt;dhcpcd&lt;/em&gt; to use public DNS only.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;static&lt;/span&gt; domain_name_servers=1.1.1.1 &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Subsequently, I created a &lt;code&gt;/etc/resolv.conf.head&lt;/code&gt; with the localhost address as a nameserver.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# resolv.conf.head&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After reboot, it turned out that my localhost address defined in the additional header file was successfully added as the first nameserver in the &lt;code&gt;resolv.conf&lt;/code&gt;. Ultimately, it looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ApacheConf&#34; data-lang=&#34;ApacheConf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Generated by resolvconf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1.1.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;8.8.8.8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you ever struggle with this crazy behavior, treat it like a potential solution. If you know the actual reason or have a better solution – please let me know.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@erwanhesry?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Erwan Hesry&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/stop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;pihole is a self-hosted adblocker that works as a proxy for DNS queries, blocking requests related to tracking, advertisements, and other untrustworthy resources.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Specify timezone for tests in Node to get deterministic results</title>
      <link>https://szymonkrajewski.pl/nodejs-test-timezone/</link>
      <pubDate>Tue, 02 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/nodejs-test-timezone/</guid>
      <description>&lt;p&gt;In one of my side projects in Node, I wanted to test if the object with the date is formatted correctly as a Markdown entry. The setup of the test was the easiest part. However, comparing the outcome of the formatted &lt;em&gt;Date&lt;/em&gt; object wasn&amp;rsquo;t so convenient because of the timezone. Because I live in the &lt;em&gt;Europe/Warsaw&lt;/em&gt; timezone, I experience an offset relative to UTC.&lt;/p&gt;
&lt;p&gt;Consider this test scenario:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;it should parse link to valid markdown entry with title and tags&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;link&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://szymonkrajewski.pl&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon Krajewski&amp;#39;s Blog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blog&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;personal&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;createdAt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;2021-01-01T15:00:00Z&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMarkdown&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;- 2021-01-01 15:00 # https://szymonkrajewski.pl # Szymon Krajewski&amp;#39;s Blog @blog @personal&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After running this test case in &lt;em&gt;jest&lt;/em&gt; I got this result:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;received&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toBe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;expected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Object.is equality
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Expected&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;- 2021-01-01 15:00 # https://szymonkrajewski.pl # Szymon Krajewski&amp;#39;s Blog @blog @personal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Received&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;- 2021-01-01 16:00 # https://szymonkrajewski.pl # Szymon Krajewski&amp;#39;s Blog @blog @personal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a bit silly to arrange &lt;em&gt;15:00&lt;/em&gt; but expect the &lt;em&gt;16:00&lt;/em&gt;. However, I found that setting the timezone for tests is an excellent way to get deterministic results regardless of the local timezone. Just invoke tests with environment variable &lt;code&gt;TZ=utc&lt;/code&gt;, e.g. &lt;code&gt;TZ=utc jest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I also provide a change to &lt;em&gt;package.json&lt;/em&gt; file to be able to run &lt;code&gt;npm test&lt;/code&gt;  with proper timezone.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;TZ=utc jest&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The test scenario comes from my side project &lt;a href=&#34;https://github.com/skrajewski/bkmrx&#34;&gt;&lt;em&gt;bkmrx&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Synchronize two collections with different representations</title>
      <link>https://szymonkrajewski.pl/synchronized-collection/</link>
      <pubDate>Thu, 12 Nov 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/synchronized-collection/</guid>
      <description>&lt;p&gt;We get used to reading about big things – high-level architectural solutions, design patterns in action, or big data processing. However, most of our problems are small and lie at the low-level in our code. One of them is: how to synchronize a collection of entities with another collection, which is its simplified representation?&lt;/p&gt;
&lt;p&gt;Imagine you have a collection of entities. You receive an updated version of that collection, e.g., from the API endpoint. But what if data are transferred using DTOs? We should add new entities based on data in DTO, update existing entities, and remove elements that don&amp;rsquo;t appear in the edited collection.&lt;/p&gt;
&lt;p&gt;In this article, I&amp;rsquo;ll show you a generalized solution to this particular problem.&lt;/p&gt;
&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;
&lt;p&gt;Before we start working with the actual solution, let&amp;rsquo;s look at the problem itself. Image an array containing structures that represent the product in the shopping cart.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Apple iPhone 6&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Samsung Galaxy S6&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;USB C-HDMI Adapter&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because many things may change simultaneously, we don&amp;rsquo;t want to process each action separately. Just imagine a frontend app where you can modify the basket purely on the frontend side and send the updated version to the API&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So, user sends updated version of the cart:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Apple iPhone 6&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;USB C-HDMI Adapter&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;HDD 1TB&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What happened here?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User adds a new product to the cart, &lt;em&gt;HDD 1TB&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;User removes the product &lt;em&gt;Samsung Galaxy S6&lt;/em&gt; from the cart,&lt;/li&gt;
&lt;li&gt;User changes the quantity of product &lt;em&gt;Apple iPhone 6&lt;/em&gt; from 1 to 2.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, you may ask why we ever consider it as a problem? After all, we have the updated version of the array right away. So, yes and no. Yes – because it&amp;rsquo;s an updated version of the previous collection. No – because it&amp;rsquo;s only a simplified version of the data model that lives on the API side.&lt;/p&gt;
&lt;p&gt;The API may already have an entity called &lt;em&gt;Cart&lt;/em&gt; with assigned &lt;em&gt;Products&lt;/em&gt; with some additional data, like &lt;em&gt;quantity&lt;/em&gt; in this case. That&amp;rsquo;s why, based on the updated &lt;code&gt;$cart&lt;/code&gt; array, we need to update our &lt;strong&gt;underneath data model&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;basic-solution&#34;&gt;Basic solution&lt;/h2&gt;
&lt;p&gt;Forget about the generalization, and let&amp;rsquo;s develop a solution for this specific case. It may look as follows.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cart&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getProducts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// remove Product from Cart model
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// update existing Product
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_key_first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setQuantity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// remove processed product
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// add new elements to Cart model
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ProductRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// flush changes into the database
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We define each case (add, update, remove), so &lt;em&gt;Cart&lt;/em&gt; is working as expected. Is anything we can do better? Indeed, implementation is a moot point, but we want to prepare a tool for handling similar cases without worrying about these lookups and loops.&lt;/p&gt;
&lt;h2 id=&#34;abstraction-above-synchronization-of-collections&#34;&gt;Abstraction above synchronization of collections&lt;/h2&gt;
&lt;p&gt;The whole mechanism looks good, but to make it more generic, we need to replace specific operations with some abstracts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can we identify an updated version of the currently existing element?&lt;/li&gt;
&lt;li&gt;How can we add a new element into the collection (including &lt;em&gt;data model&lt;/em&gt;)?&lt;/li&gt;
&lt;li&gt;How can we remove an element from the collection?&lt;/li&gt;
&lt;li&gt;How can we update an element in the collection?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we extract the behaviors mentioned above outside the mechanism, we should end up with a generic solution to synchronize collections, no matter what they contain.&lt;/p&gt;
&lt;p&gt;Handling adding/updating/removing sounds like an excellent candidate to &lt;em&gt;Policy&lt;/em&gt;, also known as &lt;em&gt;Strategy&lt;/em&gt; pattern. The way how we can identify the corresponding element, we could provide as an argument during synchronization.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s an idea.&lt;/p&gt;
&lt;h2 id=&#34;synchronized-collection&#34;&gt;Synchronized collection&lt;/h2&gt;
&lt;p&gt;The recipe is simple: wrap your collection in a special object called &lt;em&gt;SynchronizedCollection&lt;/em&gt; and implement &lt;em&gt;SynchronizationPolicy&lt;/em&gt;. You can then use a method called &lt;em&gt;sync&lt;/em&gt; to provide another array that collection has to synchronize with and instruction on finding a corresponding element.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In the below snippets, I omitted some parts of code for readability. If you look for the whole code, I published it as &lt;a href=&#34;https://gist.github.com/skrajewski/2801cd20e8f41d4e3dbde74b8a6c9fa8&#34;&gt;GitHub Gist&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;SynchronizationPolicy.php&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizationPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @return mixed added element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @return mixed updated element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleUpdate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleRemove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;SynchronizedCollection.php&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SynchronizedCollection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$collection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizationPolicy&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @param array $elements Array of elements to synchronize
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @param callable $matcher Function that match element from collection to coresponding element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @return $this
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$elements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Callable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$matcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;collection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;collection&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// find updated version of origin in provided array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$elements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$matcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// if origin is not found, then handle removal
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handleRemove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// if origin is found then handle update
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_key_first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handleUpdate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;reset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$elements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// if origin is matched against more than one element, then throw exception
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AmbiguousElementException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Provided array contains ambiguous element.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;array_walk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$elements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handleAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;array_values&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$copiedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;SynchronizedCollection&lt;/em&gt; encapsulates the whole mechanism of synchronization. The policy is responsible for providing a &lt;em&gt;new&lt;/em&gt; or &lt;em&gt;updated&lt;/em&gt; version of entities. Under the hood, &lt;code&gt;sync&lt;/code&gt; composes a new array and wrap it in another &lt;em&gt;SynchronizedCollection&lt;/em&gt;. To retrieve the raw array from it, you can use e.g., &lt;code&gt;toArray&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;As you can see, it doesn&amp;rsquo;t follow the standard collection interface. However, it shouldn&amp;rsquo;t be a problem to implement missing methods or to use this method to extend your collection.&lt;/p&gt;
&lt;h3 id=&#34;finding-the-corresponding-element&#34;&gt;Finding the corresponding element&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s look yet at how we approach finding the corresponding element.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$updatedVersion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$elements&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$matcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This seemingly enigmatic piece of code uses function composition to let the user define a simple &lt;code&gt;$matcher&lt;/code&gt; function instead of enforcing him to implement &lt;code&gt;array_filter&lt;/code&gt; by himself. The matcher is a simple comparator and looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$toSyncElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$collectionElement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first argument holds data to sync, whereas the second element is an actual element in the collection. Matcher should answer whether &lt;code&gt;$toSyncElement&lt;/code&gt; concerns &lt;code&gt;$collectionElement&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;synchronizedcollection-in-action&#34;&gt;SynchronizedCollection in action&lt;/h2&gt;
&lt;p&gt;I refactor the previous code to use a &lt;em&gt;SynchronizedCollection&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cart&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$policy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizationPolicy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ProductRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleUpdate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setQuantity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleRemove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cartCollection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$newCartCollection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cartCollection&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// flush changes into the database
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the definition of &lt;code&gt;$policy&lt;/code&gt;  looks too complicated for you, you can set up a dynamic policy where you can provide needed callbacks dynamically:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DynamicSynchronizationPolicy&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizationPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$addCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$updateCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$removeCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Callable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$addCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Callable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$updateCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Callable&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$removeCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addCallback&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$addCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateCallback&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$updateCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeCallback&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$removeCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;call_user_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleUpdate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;call_user_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;updateCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handleRemove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;call_user_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeCallback&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you wonder why I marked this class as final, check my other article about &lt;a href=&#34;https://szymonkrajewski.pl/how-to-use-final-in-php/&#34;&gt;how to use &lt;em&gt;final&lt;/em&gt; keyword&lt;/a&gt;. Back to the topic, we can simplify our solution a bit more:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cart&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$policy&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DynamicSynchronizationPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ProductRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setQuantity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$cartCollection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;SynchronizedCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$newCartCollection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cartCollection&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newCart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$newProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// flush changes into the database
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Isn&amp;rsquo;t it simple?&lt;/p&gt;
&lt;h2 id=&#34;where-we-used-it&#34;&gt;Where we used it?&lt;/h2&gt;
&lt;p&gt;In one of our projects, we decided to use the &lt;em&gt;&lt;a href=&#34;https://symfony.com/doc/current/forms.html&#34;&gt;Symfony Form&lt;/a&gt;&lt;/em&gt; component to handle incoming requests to the API and perform initial data validation. Instead of letting &lt;em&gt;FormTypes&lt;/em&gt; filling entities directly, we utilized a DTO layer&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. But the actual problem was related to one form type, to be precise, &lt;a href=&#34;https://symfony.com/doc/current/reference/forms/types/collection.html&#34;&gt;&lt;em&gt;CollectionType&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a topic for a different article, but – let&amp;rsquo;s admit that – the &lt;em&gt;Symfony Form&lt;/em&gt; component was never designed to work as an API handling method. This use-case is possible because we use only a &lt;em&gt;backend side&lt;/em&gt; of the forms. Still, they are some visible differences between handling POST forms from browsers and API calls. A good example is &lt;em&gt;CollectionType&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://stackoverflow.com/questions/54292270/symfony-forms-collectiontype-index-by-id/54295135#54295135&#34;&gt;&lt;em&gt;CollectionType&lt;/em&gt; is &lt;em&gt;index-based&lt;/em&gt;&lt;/a&gt;. It means that the element from the request matches the corresponding element in the same position in the collection. If you remove an element from the array and send it to the API, you unintentionally place other objects in incorrect positions. Instead of removing that specific element, the component will update every entity/DTO based on its position and remove the last one with no equivalent in the original collection. That&amp;rsquo;s why we built &lt;em&gt;SynchronizedCollection&lt;/em&gt; – to avoid that behavior.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s only a brief hint of this problem. Perhaps in the future, the &lt;em&gt;Form&lt;/em&gt; component lets us define the indexes rather than use the element&amp;rsquo;s position every time.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Surprisingly, &lt;em&gt;SynchronizedCollection&lt;/em&gt; was settled down in our application in different places in different variations. It hides a complex problem behind a simple interface, and I think that&amp;rsquo;s the reason why it&amp;rsquo;s so useful.&lt;/p&gt;
&lt;p&gt;The whole code is available as &lt;a href=&#34;https://gist.github.com/skrajewski/2801cd20e8f41d4e3dbde74b8a6c9fa8&#34;&gt;GitHub Gist&lt;/a&gt;. Feel free to make comments, improvements, and so on. I don&amp;rsquo;t think that providing a composer package is something that our community needs, but it may change in the future.&lt;/p&gt;
&lt;p&gt;If this article was useful for you, please let me know. Btw, I haven&amp;rsquo;t found a better title for it 😶.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@johnmarkarnold?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;John Mark Arnold&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Our use case concerns collecting addresses, where the user could provide many addresses into his profile. The whole operation was contained in the &lt;em&gt;update user&lt;/em&gt; endpoint.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;At the moment of writing this article, Symfony&amp;rsquo;s documentation has opened PR regarding &lt;a href=&#34;https://github.com/symfony/symfony-docs/pull/10588/files&#34;&gt;usage DTOs in form handling&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Selective serialization using serialization groups</title>
      <link>https://szymonkrajewski.pl/serialization-groups-approach/</link>
      <pubDate>Wed, 04 Nov 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/serialization-groups-approach/</guid>
      <description>&lt;p&gt;When you ever develop an API in the Symfony framework&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, you probably heard of the &lt;code&gt;serializationGroup&lt;/code&gt; option. It allows you to define different groups of attributes to serialize and deserialize. However, defining these groups without proper consideration may make the whole development hard, e.g., if you need to deal with different scopes of the object&amp;rsquo;s attributes in many other endpoints.&lt;/p&gt;
&lt;p&gt;I want to show you our approach to serialization groups in one of our projects. Together with my team, we use them to control the serialization of related entities on various endpoints in our API.&lt;/p&gt;
&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;ll be more comfortable if I provide some context. Instead of talking about entities from the actual project, I&amp;rsquo;ll use a generic example: &lt;em&gt;Order&lt;/em&gt; and &lt;em&gt;Product&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Our case is a &lt;em&gt;RESTish&lt;/em&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; API for a relatively small application. Some endpoints respond with entities with a relation or collection of other related entities to avoid unnecessary calls. To generate responses, we use the &lt;a href=&#34;https://symfony.com/doc/current/components/serializer.html&#34;&gt;Serializer&lt;/a&gt; component. For handling requests, we rely on &lt;a href=&#34;https://symfony.com/doc/current/forms.html&#34;&gt;Symfony Form&lt;/a&gt; component&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The main challenge is to return mandatory data for each scenario.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the user calls for the &lt;em&gt;Products&lt;/em&gt; collection, he gets a collection with &lt;strong&gt;basic data&lt;/strong&gt; about each &lt;em&gt;Product&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the user calls for a specific &lt;em&gt;Product&lt;/em&gt;, he gets &lt;strong&gt;very detailed data&lt;/strong&gt; about the requested &lt;em&gt;Product&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview_hu_b611b3810cab2781.png 360w, https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview_hu_c53dbbafca3bd896.png 640w, https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview_hu_9f204db065f36165.png 960w, https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview_hu_c098621515a63edf.png 1200w, https://szymonkrajewski.pl/serialization-groups-approach/product-single-collection-preview_hu_56ff9338a625a64f.png 1422w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1422&#34;
          height=&#34;632&#34;
           alt=&#34;Schematic responses from the API when user calls _/products_ endpoint&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Schematic responses from the API when user calls &lt;em&gt;/products&lt;/em&gt; endpoint
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;But it&amp;rsquo;s not all.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the user calls for the &lt;em&gt;Orders&lt;/em&gt; collection, he gets a collection with a &lt;strong&gt;basic data&lt;/strong&gt; about each &lt;em&gt;Order&lt;/em&gt;. Each order has a collection containing &lt;strong&gt;basic data&lt;/strong&gt; about each &lt;em&gt;Product&lt;/em&gt; in the order.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the user calls for specific &lt;em&gt;Order&lt;/em&gt;, he gets &lt;strong&gt;very detailed data&lt;/strong&gt; about the requested &lt;em&gt;Order&lt;/em&gt;. Each order has a collection containing &lt;strong&gt;basic data&lt;/strong&gt; about each &lt;em&gt;Product&lt;/em&gt; in the order.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview_hu_ac1bb45297d558.png 360w, https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview_hu_c1cd2d99b1edd0a2.png 640w, https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview_hu_e36ec51f5e2e3080.png 960w, https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview_hu_981e3d0e7dc132d.png 1200w, https://szymonkrajewski.pl/serialization-groups-approach/order-single-collection-preview_hu_914747165ff6aeec.png 1542w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1542&#34;
          height=&#34;822&#34;
           alt=&#34;Schematic responses from the API when user calls _/orders_ endpoint&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Schematic responses from the API when user calls &lt;em&gt;/orders&lt;/em&gt; endpoint
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Product&lt;/em&gt; and &lt;em&gt;Order&lt;/em&gt; are only examples. Imagine many different cases where you need to attach some extra data to a resource, but you don&amp;rsquo;t want to connect the whole object. Sometimes you don&amp;rsquo;t need to show the related items at all. Another time, in the case of other endpoints, you need to include very detailed data.&lt;/p&gt;
&lt;h2 id=&#34;two-groups-for-entities-are-not-enough&#34;&gt;Two groups for entities are not enough&lt;/h2&gt;
&lt;p&gt;Our first solution consisted of two groups: &lt;em&gt;basic&lt;/em&gt; and &lt;em&gt;extend&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We classified each property for one of them, and they were mutually exclusive – a single field should be classified either as &lt;em&gt;basic&lt;/em&gt; or &lt;em&gt;extend&lt;/em&gt;. Unfortunately, this setup didn&amp;rsquo;t let us configure the properties of nested resources properly. Whenever we wanted to retrieve a full set of properties from the primary resource and some &lt;em&gt;basic&lt;/em&gt; data from related entities, we always wind up with an &lt;em&gt;extend&lt;/em&gt; version on each entity.&lt;/p&gt;
&lt;h2 id=&#34;two-groups-for-each-entity-individually&#34;&gt;Two groups for each entity individually&lt;/h2&gt;
&lt;p&gt;To have more control over the serialization, we decided to use an entity name as a prefix for each serialization group. For &lt;em&gt;Order&lt;/em&gt; and &lt;em&gt;Product&lt;/em&gt; entities, it looks as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;order_basic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order_extend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;product_basic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;product_extend&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using this approach, we can serialize, e.g., the full set of properties from &lt;em&gt;Order&lt;/em&gt; and only basic properties from its assigned &lt;em&gt;Products&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;separate-group-for-relationship&#34;&gt;Separate group for relationship&lt;/h2&gt;
&lt;p&gt;So, we split properties in each entity into &lt;em&gt;basic&lt;/em&gt; and &lt;em&gt;extend&lt;/em&gt; groups, but where does the collection of products belong?&lt;/p&gt;
&lt;p&gt;Basically, it depends on the use case. However, we came up with another convention for all types of relations between entities for the sake of flexibility. Each association has assigned a dedicated group where the name consists of both &lt;em&gt;owner&lt;/em&gt; and &lt;em&gt;related entity name&lt;/em&gt;, e.g., &lt;code&gt;order_with_products&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Serializer\Groups({&amp;#34;order_basic&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Serializer\Groups({&amp;#34;order_extend&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$createdAt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Serializer\Groups({&amp;#34;order_with_products&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$products&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// rest method omitted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;how-we-use-it&#34;&gt;How we use it&lt;/h2&gt;
&lt;p&gt;Thanks to this separation of groups, we could compose responses using &lt;strong&gt;specific subset of properties from entities and related objects&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So, come back to the scenarios I use in the &lt;a href=&#34;https://szymonkrajewski.pl/serialization-groups-approach/#context&#34;&gt;Context&lt;/a&gt; section. Let&amp;rsquo;s compose proper responses for each of them. I won&amp;rsquo;t describe all entities – details aren&amp;rsquo;t needed here. The &lt;code&gt;@Rest/View&lt;/code&gt; annotation comes from the &lt;em&gt;&lt;a href=&#34;https://symfony.com/doc/current/bundles/FOSRestBundle/index.html&#34;&gt;FOSRestBundle&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;First, &lt;em&gt;ProductController&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ProductController&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AbstractController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Rest/View(serializationGroups={&amp;#34;product_basic&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     **/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;showAllProducts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Rest/View(serializationGroups={&amp;#34;product_basic&amp;#34;, &amp;#34;product_extends&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     **/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;showProduct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Product&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$product&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, &lt;em&gt;OrderController&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;OrderController&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AbstractController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Rest/View(serializationGroups={
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;order_basic&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;order_with_products&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;product_basic&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     **/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;showAllOrders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @Rest/View(serializationGroups={
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;order_basic&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;order_extend&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;order_with_products&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *     &amp;#34;product_basic&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     **/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;showOrder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Order&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;serializationGroup&lt;/code&gt; option is powerful, not only when it comes to generating responses like this but also when dealing with different responses that depend on the user&amp;rsquo;s role or status. There&amp;rsquo;re a lot of cases when we can use it.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know how it&amp;rsquo;ll work in a bigger project with many more entities. Still, in my opinion, as long as the relations between entities are carefully designed, everything should be manageable. In our case, it does the job.&lt;/p&gt;
&lt;p&gt;If you have more experience with it, or if you follow another approach, let me know. I&amp;rsquo;m curious how people use it in their projects.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@tamara_photography?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Tamara Gak&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Or any other API that relies on the Serializer component.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;It&amp;rsquo;s not a pure REST API – we broke some REST assumptions for convenience.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;API request with data could be mapped as a standard form, so why not?&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Why deprecation process is important, on the example of restic</title>
      <link>https://szymonkrajewski.pl/restic-update-broke-process/</link>
      <pubDate>Sun, 18 Oct 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/restic-update-broke-process/</guid>
      <description>&lt;p&gt;I use restic as my backup solution, and I haven&amp;rsquo;t had any problem so far, but recently one of my health checks suddenly stopped working, telling me that something is wrong with my backup. On the other side, restic has done its job, and everything works fine, so what was the problem?&lt;/p&gt;
&lt;p&gt;I run the whole backup using following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/restic &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --verbose &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  --files-from &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MACHINE_NAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/restic_backup/include.txt&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  backup &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl --retry &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$RESTIC_BACKUP_HEALTCHECK_ENDPOINT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I realized that it started to occur after the last &lt;code&gt;brew upgrade&lt;/code&gt;, and I noticed that brew also updated the restic binary to the version &lt;code&gt;0.10.0&lt;/code&gt;. I suspected that something changed in the restic exit code. I went through the &lt;a href=&#34;https://github.com/restic/restic/releases/tag/v0.10.0&#34;&gt;changelog&lt;/a&gt;, and I saw this change:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chg &lt;a href=&#34;https://github.com/restic/restic/pull/2546&#34;&gt;#2546&lt;/a&gt;: Return exit code 3 when failing to backup all source data&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Oh, dear.&lt;/p&gt;
&lt;p&gt;Yes, I back up a lot of directories, and some of them in the hierarchy are not accessible without proper permission, e.g., if you want to back up the entire APFS volume, you probably get errors as below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;error: Open: open /Volumes/Photos/.Spotlight-V100: operation not permitted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;error: NodeFromFileInfo: Listxattr: xattr.list /Volumes/Photos/.TemporaryItems : permission denied
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;error: NodeFromFileInfo: Listxattr: xattr.list /Volumes/Photos/.Trashes : permission denied
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before version &lt;code&gt;0.10&lt;/code&gt; it didn&amp;rsquo;t impact the output from a command, but for now, restic distinguishes between the full and incomplete snapshot and returns a different exit code.&lt;/p&gt;
&lt;p&gt;How to fix it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exclude problematic directories and files.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;lesson-learned&#34;&gt;Lesson learned&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;As a User&lt;/em&gt;: Read the changelog before updating software you depend on.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;As a Developer&lt;/em&gt;: Changing the already existing behavior should be treated as breaking change, and it requires some &lt;strong&gt;deprecation process&lt;/strong&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. It could be simple as below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First step is marking the current functionality as deprecated and warn the user when he uses it. Additionally, developers may provide a feature flag turned by, e.g., environment variable to enforce a new behavior.&lt;/li&gt;
&lt;li&gt;The second step would be replacing the new functionality with the old one but leaving a feature flag to use the old behavior.&lt;/li&gt;
&lt;li&gt;Third step will be removing the old functionality entirely.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the case of restic, the change was small and recorded correctly in the changelog. But it revealed the main &lt;a href=&#34;https://szymonkrajewski.pl/the-hidden-cost-of-self-hosted-services/&#34;&gt;drawback of self-hosted solutions&lt;/a&gt; – if anything stops working, you have to change your plans and check what&amp;rsquo;s wrong, especially if your workflow depends on the process or the tool.&lt;/p&gt;
&lt;p&gt;I still use restic because it fulfills all my needs, and it requires little maintenance. Seriously, it was the first time I had to investigate what happened since I create my backup script. &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/&#34;&gt;You can read how to automate your backup based on restic in my other article&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Restic still does not reach the 1.0 version, so according to SemVer 2.0, anything may change at any time. The deprecation process, in this case, depends on the goodwill of developers.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;I wrote about the need of having deprecation process but in a slighly different context. If this topic sounds interesting for you, you can &lt;a href=&#34;https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/&#34;&gt;read it here&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Following in Social Media</title>
      <link>https://szymonkrajewski.pl/following/</link>
      <pubDate>Mon, 10 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/following/</guid>
      <description>&lt;p&gt;The problem with Social Media lies in its &lt;em&gt;social&lt;/em&gt; component, where we treat everyone as a friend. Some people take it seriously, and they share almost everything with people who often don&amp;rsquo;t really care. As long as the publisher does it consciously, know the consequences, and have fun with sharing – it&amp;rsquo;s OK. However, the problem occurs on the consumer&amp;rsquo;s side because not everyone is interested in somebody else&amp;rsquo;s life details, political opinion, religion, or other stuff that &lt;em&gt;friends&lt;/em&gt; actively share. Why people let that content appear in front of them? Because of &lt;em&gt;friendship&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dont-confuse-friendship-with-following&#34;&gt;Don&amp;rsquo;t confuse friendship with following&lt;/h2&gt;
&lt;p&gt;Each time we make friends with someone on Facebook, follow someone on Twitter, or connect with someone on LinkedIn, we start receiving updates from that person. Same, we let other people influence what kind of information we consume without even thinking about the quality of them. These updates are another stream of information that we need to manage, but the remedy is actually quite simple. To stay healthy in the social media world, I stick to the one specific principle that sound: &lt;strong&gt;following does not mean friendship&lt;/strong&gt; .&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. Following does not mean friendship. The following means that you receive updates from another person you follow. Friendship is something more profound, and I don&amp;rsquo;t even try to define it. It usually exists outside of Social Media.&lt;/p&gt;
&lt;p&gt;Let me give you an example.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You may not be my friend, but I follow you because you publish valuable content I enjoy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You may be my friend, but I don&amp;rsquo;t follow you because the content you post doesn&amp;rsquo;t match my current interests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re my friend and I don&amp;rsquo;t follow you, it doesn&amp;rsquo;t mean that I don&amp;rsquo;t like you. On the other hand, if I barely know you (and vice versa) but I follow you, It doesn&amp;rsquo;t mean that we are friends now – I enjoy the content you share.&lt;/p&gt;
&lt;h2 id=&#34;be-selective&#34;&gt;Be selective&lt;/h2&gt;
&lt;p&gt;The possibility to choose who&amp;rsquo;s content we want to receive is at our fingertips. However, people are reluctant to stop following their friends, because of FOMO&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, or because it&amp;rsquo;s emotionally hard. Once you realize that the following is not the same as friendship, it will be much easier to handle the &lt;em&gt;alleged loss&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Being selective for the information you may receive is much more important than you think. First, people produce way more content than each of us can ever consume, so it&amp;rsquo;s worth to choose wisely what we spend time on. Secondly, everything you read, listen, watch, and every news and update from people you follow influence you. &lt;strong&gt;These things shape your understanding and image of the world&lt;/strong&gt;. How to stay happy reading continuously about calamities, deaths, climate changes, and other sad things? How to stay productive if you&amp;rsquo;re stumbling upon memes, gifs, and other viral videos everyone is sharing?&lt;/p&gt;
&lt;p&gt;In Twitter, I follow people who publish things that resonate with me and enrich my thoughts and knowledge. It&amp;rsquo;s OK to unfollow people whose content doesn&amp;rsquo;t give you value anymore. Your interest may change in time as the people you follow.&lt;/p&gt;
&lt;p&gt;In Facebook, the medium I rarely use, I stopped observing almost everyone besides a few friends and family members. I still have contact with people that I know, but again, the following is not the same as friendship.&lt;/p&gt;
&lt;p&gt;LinkedIn is much more problematic. It advertises itself like a business network. For me, it works like a job advertisement website with a built-in communicator. Maybe it&amp;rsquo;ll change when I stop observing people from my network I don&amp;rsquo;t even know (mainly recruiters).&lt;/p&gt;
&lt;p&gt;You have the power to choose. Use it. Your friends will understand, and majority don&amp;rsquo;t even notice.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@jaysung?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Jehyun Sung&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/follow?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;em&gt;Fear Of Missing Out&lt;/em&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Setup end-to-end encrypted cloud drive using rclone</title>
      <link>https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/</link>
      <pubDate>Fri, 07 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/</guid>
      <description>&lt;p&gt;Last time I&amp;rsquo;ve experimented with applying various encryption solutions to the public cloud storage such as Google Drive or OneDrive. They are relatively cheap online storage. However, I don&amp;rsquo;t consider them the best place for all my files, especially sensitive ones. To make them more useful to me, I was looking for an end-to-end encryption mechanism I could use with clouds in tandem, and I finally discovered rclone.&lt;/p&gt;
&lt;p&gt;In this article, I&amp;rsquo;d like to show you my approach to use rclone as the way to have an end-to-end encrypted cloud drive.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not my first endeavour with cloud storage and encryption. Some time ago, I wrote about &lt;a href=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/&#34;&gt;syncing files with cryptomator vault&lt;/a&gt;. Cryptomator is also an excellent tool, but for the current case, I found rclone to be more useful. Maybe I&amp;rsquo;ll write about it in another article.&lt;/p&gt;
&lt;p class=&#34;box danger has-title&#34;&gt;&lt;strong&gt;‼️ IMPORTANT NOTE ‼️&lt;/strong&gt;&lt;br&gt;
This article demonstrates how to set up an encrypted cloud drive to &lt;strong&gt;protect your data and privacy&lt;/strong&gt; from your cloud storage provider. &lt;strong&gt;I do not recommend&lt;/strong&gt; and &lt;strong&gt;I do not encourage&lt;/strong&gt; to treat it as a way to hide and store illegal files.&lt;/p&gt;
&lt;h2 id=&#34;why-even-bother&#34;&gt;Why even bother?&lt;/h2&gt;
&lt;p&gt;Almost every storage provider encrypts your data (Google, Dropbox, OneDrive) or gives you control over encryption (S3). Thanks to them, your data is encrypted in transit and at rest. It means, no breach on your provider&amp;rsquo;s server will reveal your files. Your account works like a key – only you and your provider have access to the unencrypted data (because the service also has access to encryption key).&lt;/p&gt;
&lt;p&gt;On the other hand, your credentials are the only thing that lies between the public Internet and your data. &lt;strong&gt;Your data is safe as long as your login and password are secure&lt;/strong&gt;. Turning on two-factor authentication improves the protection even more, but still, there is only an authentication procedure that is under someone&amp;rsquo;s else control.&lt;/p&gt;
&lt;p&gt;When something is end-to-end encrypted, no one but you can view the data. It&amp;rsquo;s an entirely different level of &lt;strong&gt;protection and privacy&lt;/strong&gt; – even the service where you store your files knows nothing about them because decryption takes place only on your devices. It comes with a cost – the fancy web interface where you can browse files, preview images and documents, or even edit them is no longer usable. The cloud is only a container for your encrypted data.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive_hu_92bbed0c8bae1a17.png 360w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive_hu_dce6c55f7355cfc9.png 640w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive_hu_3af6349cfddb8261.png 960w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive_hu_b073d91e8be7002b.png 1200w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/encrypted_files_onedrive_hu_837dee1bd81fd248.png 1564w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1564&#34;
          height=&#34;662&#34;
           alt=&#34;Part of OneDrive interface shows list of encrypted files.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      List of encrypted files on OneDrive web-interface.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;So, is it worth even bother? You have to answer it by yourself. Using end-to-end encryption on your own isn&amp;rsquo;t the most convenient way of utilizing cloud storage. You lose a lot of features it provides. Unfortunately, convenience rarely goes hand-in-hand with security, and, what is more relevant today – privacy. Remember that &lt;strong&gt;it&amp;rsquo;s not zero-one decision&lt;/strong&gt; – you can still use encryption for your most sensitive data (what I highly recommend) and store other files as usual.&lt;/p&gt;
&lt;h2 id=&#34;my-motivations&#34;&gt;My motivations&lt;/h2&gt;
&lt;p&gt;I used to avoid keeping my data outside devices I&amp;rsquo;m able to control. I didn&amp;rsquo;t need to store things online because most of the time, I had my computer in my backpack, so my data was almost always available. But things changed. Mostly because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I started using two different computers,&lt;/li&gt;
&lt;li&gt;I slowly run out of space on my private MacBook,&lt;/li&gt;
&lt;li&gt;I began to treat my phone as my second computer, and I concluded that it would be convenient to access the same files I keep on my laptop,&lt;/li&gt;
&lt;li&gt;besides my backup, I wanted to have another place to store my files safely,&lt;/li&gt;
&lt;li&gt;I pay for Microsoft365, where I have 1TB of OneDrive included – it would be nice to take advantage of it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I decided to take simple criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;End-to-end encryption&lt;/strong&gt; – data must be encrypted before leaving my device,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard disk is not the limitation&lt;/strong&gt; – I should be able to store gigabytes of data, even if the size of encrypted data exceeds the size of my hard drive,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OS integration&lt;/strong&gt; – it should be usable with Finder, e.g., using FUSE,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hassle-free&lt;/strong&gt; – it should be a hassle-free, or at the very least, it should require as minimum maintenance-work as possible &lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-Source&lt;/strong&gt; – it should be an open-source solution,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-platform&lt;/strong&gt; – it should work on macOS, but shouldn&amp;rsquo;t be limited to this system – even better if it&amp;rsquo;s possible to access the data on the smartphone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn&amp;rsquo;t perform exhaustive tests, but I found rclone to be the most suitable solution to build the encrypted cloud drive on top of it. I don&amp;rsquo;t answer if this solution is efficient, so if you&amp;rsquo;re looking for charts and numbers, it&amp;rsquo;s not the right place.&lt;/p&gt;
&lt;p&gt;In this article, I&amp;rsquo;ll show you how to set up everything on macOS as I did. Still, almost everything (besides installation and &lt;em&gt;launchd&lt;/em&gt; part) is also applicable to every other Linux system.&lt;/p&gt;
&lt;h2 id=&#34;what-is-rclone&#34;&gt;What is rclone?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://rclone.org&#34;&gt;Rclone&lt;/a&gt; promotes itself as a &lt;em&gt;rsync for cloud storage&lt;/em&gt;. It lets you synchronize files between your computer and cloud storage providers or even between various cloud providers using your computer as a proxy.&lt;/p&gt;
&lt;p&gt;File synchronization isn&amp;rsquo;t the only thing that rclone does. It also has a bunch of functionalities that allow you to expose and manipulate the data. For instance, you can mount a cloud drive directly to your operating system and use it as another drive. Or you can expose the content of the cloud drive by WebDAV, FTP, HTTP, or even DLNA protocol.&lt;/p&gt;
&lt;h2 id=&#34;preparation&#34;&gt;Preparation&lt;/h2&gt;
&lt;h3 id=&#34;install-rclone&#34;&gt;Install rclone&lt;/h3&gt;
&lt;p&gt;First, you need to install a rclone. The simplest way is to do this is using &lt;strong&gt;brew&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew install rclone
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;configure-remote-storage&#34;&gt;Configure remote storage&lt;/h3&gt;
&lt;p&gt;Rclone has built-in support for &lt;a href=&#34;https://rclone.org/overview/&#34;&gt;plenty of cloud storage providers&lt;/a&gt;. As I mentioned in [My Motivations section][#My Motivations], I use OneDrive, because I have 1TB online storage attached to the Microsoft365 subscription.&lt;/p&gt;
&lt;p&gt;To change the configuration of rclone, run the following command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a new configuration by following instructions on the screen.&lt;/p&gt;
&lt;p&gt;In my case, I created a new configuration named &lt;em&gt;&lt;strong&gt;onedrive&lt;/strong&gt;&lt;/em&gt;. I had to authorize rclone to use my OneDrive account. After that, I could view and manipulate my data using rclone commands.&lt;/p&gt;
&lt;p&gt;To view the list of files in your account, use the below command (replace &lt;em&gt;onedrive&lt;/em&gt; with your configuration name).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone lsd onedrive
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also mount this storage into your filesystem, but on this stage, it doesn&amp;rsquo;t have any encryption attached so that it will be working more or less like any other client. The main difference is that the &lt;strong&gt;mounted files aren&amp;rsquo;t actually on your computer&lt;/strong&gt;, so they don&amp;rsquo;t take up your hard drive space.&lt;/p&gt;
&lt;h3 id=&#34;add-encryption-layer&#34;&gt;Add encryption layer&lt;/h3&gt;
&lt;p&gt;Rclone provides an encryption layer by remote called &lt;em&gt;crypt&lt;/em&gt;. I called it &lt;em&gt;layer&lt;/em&gt; because it&amp;rsquo;s a wrapper around another remote.&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s technically possible to encrypt the whole backend, it&amp;rsquo;s not recommended practice because some providers keep special files or directories in the root directory. Also, it may collide with data you already keep on the cloud – rclone will try to decrypt them, showing you a bunch of messy files. Instead, &lt;strong&gt;create a dedicated directory for the encrypted data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To add the encryption layer, go to the rclone configuration once again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a new configuration, but this time choose &lt;em&gt;crypt&lt;/em&gt; from the list of available backends. I named it &lt;strong&gt;&lt;em&gt;onedrive-crypt&lt;/em&gt;&lt;/strong&gt; to highlight that I use a previously configured &lt;strong&gt;&lt;em&gt;onedrive&lt;/em&gt;&lt;/strong&gt; backend but with an additional encryption layer.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt_hu_5a53b4ba34e1848c.png 360w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt_hu_1c098d5cbd777f5b.png 640w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt_hu_18bf2987e8a285cd.png 960w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt_hu_c83421da058d21cf.png 1200w, https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/terminal_rclone_crypt_hu_b7030c159a4e2b4c.png 1420w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1420&#34;
          height=&#34;934&#34;
           alt=&#34;The terminal shows available remotes in rclone.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The terminal shows available remotes. The crypt has number 10.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Rclone will ask about the &lt;em&gt;remote to encrypt/decrypt&lt;/em&gt;. It&amp;rsquo;s time to select the place for encrypted files. I configured it to use &lt;code&gt;onedrive:/encrypted&lt;/code&gt;, where &lt;em&gt;encrypted&lt;/em&gt; will be a directory on &lt;strong&gt;&lt;em&gt;onedrive&lt;/em&gt;&lt;/strong&gt; remote.&lt;/p&gt;
&lt;p&gt;After that, you have to provide additional details related to encryption, like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file name encryption mode&lt;/li&gt;
&lt;li&gt;directory name encryption&lt;/li&gt;
&lt;li&gt;password and salt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s talk briefly about them.&lt;/p&gt;
&lt;h4 id=&#34;file-name-encryption-mode&#34;&gt;File name encryption mode&lt;/h4&gt;
&lt;p&gt;Using encryption, you can be sure that only people who know the key (probably only you) can view the content of the file. But what about its name? Does it even matter?&lt;/p&gt;
&lt;p&gt;Name is one of the metadata. Metadata is just &lt;em&gt;data about data&lt;/em&gt;. The name may disclose what could be in the file and plausible format, however, it&amp;rsquo;s only a guess. To avoid leaking this information, &lt;strong&gt;you can encrypt file names&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Rclone also has the option to obfuscate the file name. Although it hides the file name at first glance, it&amp;rsquo;s easy to recover the original title, so it&amp;rsquo;s more like an impediment to all sorts of scanners than real protection.&lt;/p&gt;
&lt;p&gt;If you really want to hide a filename, you have to use filename encryption. It has at least two consequences.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The encrypted name will be much longer than the original file name.&lt;/li&gt;
&lt;li&gt;Some cloud storage providers let you restore deleted files or previous versions of files. By encrypting the filenames, you may find it difficult to locate the proper file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use the standard encryption for file names.&lt;/p&gt;
&lt;h4 id=&#34;directory-name-encryption&#34;&gt;Directory name encryption&lt;/h4&gt;
&lt;p&gt;Similarly to file names, &lt;strong&gt;you can encrypt the directory names&lt;/strong&gt;. It&amp;rsquo;s worth considering turning on encryption because directory structure can say much more about your data than just files. It&amp;rsquo;s possible to evaluate what do you keep on the cloud by checking directory names.&lt;/p&gt;
&lt;p&gt;Unfortunately, encryption goes with cost, and the price is the longer file path. If you want to have a complex directory structure inside your encrypted remote, e.g., your entire hard drive, you may hit the maximum file path length making your data unable to sync. When I write this article, OneDrive supports max length to 255 characters (&lt;em&gt;see &lt;a href=&#34;https://szymonkrajewski.pl/encrypted-cloud-drive-rclone/#onedrive-specific-problems&#34;&gt;OneDrive-specific problems&lt;/a&gt;&lt;/em&gt;). Not so much, when encryption comes into the game. Fortunately, most providers support more characters in the file path than OneDrive.&lt;/p&gt;
&lt;p&gt;Despite the limitation, I decided to encrypt directory names.&lt;/p&gt;
&lt;h4 id=&#34;password-and-salt&#34;&gt;Password and salt&lt;/h4&gt;
&lt;p&gt;Each encryption consists of the algorithm and key. Since the algorithm is usually public, &lt;strong&gt;the key is the factor that secures your data&lt;/strong&gt;. Rclone uses &lt;em&gt;password&lt;/em&gt; and optional &lt;em&gt;salt&lt;/em&gt; as the encryption key.&lt;/p&gt;
&lt;p&gt;I recommend generating random strings both for encryption and salt. Rclone can do it for you. You&amp;rsquo;ll only need to choose a password strength. The recommendation is 128 bits, which is fine, but I use 256 bits – just in case.&lt;/p&gt;
&lt;p&gt;Remember, &lt;strong&gt;put these passwords in a safe place&lt;/strong&gt;, e.g., in a password manager.&lt;/p&gt;
&lt;h3 id=&#34;test-configuration&#34;&gt;Test configuration&lt;/h3&gt;
&lt;p&gt;My configuration looks as follows (I stripped the secrets):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[onedrive]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;onedrive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;*** JSON CONTAINING ACCESS TOKEN ***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;drive_id *** DRIVE_ID ***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;drive_type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;personal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[onedrive-crypt]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;crypt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;remote&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;onedrive:/encrypted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;filename_encryption&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;standard&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;directory_name_encryption&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;password&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;*** ENCRYPTED ***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;password2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;*** ENCRYPTED ***&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you finish the configuration part, you can check how your new encrypted storage works. You can mount it to the filesystem, move some files there and check how the content looks from, e.g., the web interface of your cloud storage.&lt;/p&gt;
&lt;h2 id=&#34;mount-encrypted-volume&#34;&gt;Mount encrypted volume&lt;/h2&gt;
&lt;p&gt;Since it&amp;rsquo;s technically possible to mount storage system-wide, it requires a little different approach to storing configuration. In this example, I configure rclone, launchd agent, and mount path in the scope of my user profile.&lt;/p&gt;
&lt;p&gt;Rclone offers a few different ways to browse files on your remote, but I found it convenient to &lt;strong&gt;browse it as another drive using a file explorer&lt;/strong&gt; like Finder. I created a script to mount encrypted remote with some additional options.&lt;/p&gt;
&lt;p&gt;Open new file, e.g. &lt;code&gt;mount_encrypted_storage.sh&lt;/code&gt; and start writing your mount command. After some attempts, I&amp;rsquo;ll end up with something that satisfies me enough. You have to change some details like the &lt;em&gt;volume name&lt;/em&gt; and the &lt;em&gt;mount directory&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/rclone mount &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --rc &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --transfers &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --volname TwoDrive &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --allow-other &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --log-level INFO &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --buffer-size 32M &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --vfs-cache-mode writes &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --vfs-cache-max-size 16G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    onedrive-crypt:/ /Users/szymon/.mount/onedrive_crypt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure you have a directory where you can mount the encrypted remote.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mkdir -p ~/.mount/onedrive_crypt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make this file executable and test if it&amp;rsquo;s working correctly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo chmod +x mount-encrypted-storage.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ./mount-encrypted-storage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If there is no error in your terminal, you should be able to reach the new volume using Finder. It should appear on your computer, or you can enter it through the mounted directory.&lt;/p&gt;
&lt;p&gt;Depending on &lt;code&gt;--log-level&lt;/code&gt; option, rclone prints various information into &lt;strong&gt;stderr&lt;/strong&gt;. Instead of using the &lt;code&gt;--log-file&lt;/code&gt; parameter in the command, I&amp;rsquo;ll tell &lt;em&gt;launchd&lt;/em&gt; to redirect all information pushed on this stream into the file.&lt;/p&gt;
&lt;h2 id=&#34;mount-encrypted-volume-on-startup&#34;&gt;Mount encrypted volume on startup&lt;/h2&gt;
&lt;p&gt;If something should happen automatically, the first thought is usually &lt;em&gt;cron&lt;/em&gt;, whereas we have a set of more suitable tools such as &lt;em&gt;systemd&lt;/em&gt; for Linux and &lt;em&gt;launchd&lt;/em&gt; for macOS.&lt;/p&gt;
&lt;p&gt;Since I use Mac, I&amp;rsquo;ll show you how I set it up using a launchd job.&lt;/p&gt;
&lt;p&gt;I created a new &lt;em&gt;plist&lt;/em&gt; file called &lt;code&gt;pl.skrajewski.rclone.mount.onedrive_crypt.plist&lt;/code&gt;. This name follows the &lt;em&gt;reverse domain name notation&lt;/em&gt;, and it can be used to categorize and sort jobs or other stuff. I mentioned about it in the article about &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/&#34;&gt;configuring restic backup for macOS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The definition looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/scripts/mount_encrypted_storage.sh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;pl.skrajewski.rclone.mount.onedrive_crypt&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WorkingDirectory&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;SuccessfulExit&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;lt;false/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/Library/Logs/local.rclone.mount.onedrive_crypt.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since my rclone configuration exists only in the context of my user, I want to tell the &lt;em&gt;launchd&lt;/em&gt; to treat it as a &lt;strong&gt;local agent&lt;/strong&gt;. Daemons, in contrast to agents, are global and could be executed on behalf of either root or specified user.&lt;/p&gt;
&lt;p&gt;To register definition as the local agent, I copied it into the &lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt; directory and loaded it using &lt;code&gt;launchctl&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cp pl.skrajewski.rclone.mount.onedrive_crypt.plist ~/Library/LaunchAgents/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ launchctl load ~/Library/LaunchAgents/pl.skrajewski.rclone.mount.onedrive_crypt.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the agent is loaded, it immediately starts the rclone process, and the new resource will be available in Finder.&lt;/p&gt;
&lt;h2 id=&#34;using-your-encrypted-cloud&#34;&gt;Using your encrypted cloud&lt;/h2&gt;
&lt;p&gt;So, now you have the encrypted cloud drive. Thanks to FUSE, you can use it as any other storage attached to your computer. However, you should know how it works and how to use it to avoid running yourself in trouble.&lt;/p&gt;
&lt;h3 id=&#34;reliability-of-filesystem&#34;&gt;Reliability of filesystem&lt;/h3&gt;
&lt;p&gt;First of all, &lt;strong&gt;the filesystem needs to be reliable&lt;/strong&gt;. Mounting the encrypted drive using rclone and FUSE adds another abstraction layer to mimic the filesystem – every action in this space evokes a proper command in the rclone process. Sometimes it doesn&amp;rsquo;t work correctly, especially during dealing with large files. Finder isn&amp;rsquo;t aware that it works with remote storage, and it doesn&amp;rsquo;t have any mechanism to recover from unexpected network errors or timeouts.&lt;/p&gt;
&lt;p&gt;For day to day usage, I&amp;rsquo;ve had no problems so far. I follow simple rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I use mounted volume mostly for &lt;strong&gt;accessing&lt;/strong&gt; the data,
&lt;ul&gt;
&lt;li&gt;but avoid using quick look on large files, e.g., movies,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I don&amp;rsquo;t work directly on mounted volume&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;,&lt;/li&gt;
&lt;li&gt;I upload large files using the command line.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;initial-synchronization&#34;&gt;Initial synchronization&lt;/h3&gt;
&lt;p&gt;Since your drive is empty, I suppose that the very first thing will be to upload some files. &lt;a href=&#34;https://szymonkrajewski.pl/think-big-start-small/&#34;&gt;You can start small&lt;/a&gt; from a few megabytes, but If you&amp;rsquo;re going to upload a massive set of data, e.g., your entire hard drive, I recommend using CLI.&lt;/p&gt;
&lt;p&gt;I started by creating a simple script that runs synchronization for selected directories.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;OPTS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;--rc --transfers 8 --progress&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Movies onedrive-crypt:/Movies &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Archive onedrive-crypt:/Archove &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Music onedrive-crypt:/Music &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Pictures onedrive-crypt:/Pictures &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Utilities onedrive-crypt:/Utilities &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rclone sync &lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/Photos onedrive-crypt:/Photos &lt;span class=&#34;nv&#34;&gt;$OPTS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It doesn&amp;rsquo;t look pretty and isn&amp;rsquo;t so optimal, but for initial upload, it&amp;rsquo;s OK. Thanks to the &lt;code&gt;--rc&lt;/code&gt; option, you can control the running rclone process. It could be useful if you need &lt;strong&gt;to limit the bandwidth when synchronization is underway&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Save the file as, e.g., &lt;code&gt;init-sync.sh&lt;/code&gt; and make it executable. I recommend running it using a utility called &lt;strong&gt;caffeinate&lt;/strong&gt;, which prevents the system from idle sleeping. Before you do that, &lt;strong&gt;you need to unmount your encrypted drive&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ chmod +x init-sync.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ launchctl unload ~/Library/LaunchAgents/pl.skrajewski.rclone.mount.onedrive_crypt.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ caffeinate -i init-sync.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you need to limit the bandwidth during synchronization, use a proper &lt;code&gt;rc&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone rc core/bwlimit &lt;span class=&#34;nv&#34;&gt;rate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;512K &lt;span class=&#34;c1&#34;&gt;# limit the bandwidth to 512KB/s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone rc core/bwlimit &lt;span class=&#34;nv&#34;&gt;rate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off  &lt;span class=&#34;c1&#34;&gt;# disable bandwidth limitation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After finished synchronization, you can mount your encrypted remote again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ launchctl load ~/Library/LaunchAgents/pl.skrajewski.rclone.mount.onedrive_crypt.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;onedrive-specific-problems&#34;&gt;OneDrive-specific problems&lt;/h2&gt;
&lt;p&gt;OneDrive isn&amp;rsquo;t the best choice if you want to sync your entire hard drive regularly. Below I write three potential problems you may encounter during your attempts with this cloud storage.&lt;/p&gt;
&lt;h3 id=&#34;file-name-too-long&#34;&gt;File Name Too Long&lt;/h3&gt;
&lt;p&gt;OneDrive has a significant limitation when it comes to max file length, which is a limitation on the entire path to file, starting with the root of the OneDrive directory. A lot of people &lt;a href=&#34;https://onedrive.uservoice.com/forums/262982-onedrive-archive/suggestions/6325774-enable-long-file-paths-aka-file-name-too-long-p&#34;&gt;complain about this issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To check the actual limitation, you can run the below command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rclone info --check-control&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt; --check-length&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt; --check-normalization&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt; --check-streaming&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt; onedrive-crypt:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After a couple of seconds, the answer will be print to stdout:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;// onedrive-crypt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;maxFileLength&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;159&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a href=&#34;https://rclone.org/crypt/#file-name-encryption-modes&#34;&gt;rclone documentation&lt;/a&gt; also point to this issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cloud storage systems have various limits on file name length and total path length which you are more likely to hit using &amp;ldquo;Standard&amp;rdquo; file name encryption. If you keep your file names to below 156 characters in length then you should be OK on all providers.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;the-need-to-empty-trash-manually&#34;&gt;The need to empty trash manually&lt;/h3&gt;
&lt;p&gt;According to &lt;a href=&#34;https://rclone.org/onedrive/#deleting-files&#34;&gt;rclone documentation&lt;/a&gt;, OneDrive doesn&amp;rsquo;t provide a method to delete files permanently.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any files you delete with rclone will end up in the trash. Microsoft doesn&amp;rsquo;t provide an API to permanently delete files, nor to empty the trash, so you will have to do that with one of Microsoft&amp;rsquo;s apps or via the OneDrive website.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s worth knowing about this limitation if you&amp;rsquo;re going to sync a lot of files, and you have a lot of file removals.&lt;/p&gt;
&lt;h3 id=&#34;ransomware-detection-mechanism&#34;&gt;Ransomware detection mechanism&lt;/h3&gt;
&lt;p&gt;OneDrive provides a &lt;a href=&#34;https://support.microsoft.com/en-us/office/ransomware-detection-and-recovering-your-files-0d90ec50-6bfd-40f4-acc7-b8c12c73637&#34;&gt;ransomare detection mechanism&lt;/a&gt; and notifies you if you&amp;rsquo;re a victim of an attack. However, from Microsoft&amp;rsquo;s perspective, &lt;strong&gt;upload a large set of encrypted files looks suspicious enough to classify it as ransomware&lt;/strong&gt;, so you&amp;rsquo;ll end up with notification about this &lt;em&gt;false-positive&lt;/em&gt; case from time to time. Unfortunately, OneDrive doesn&amp;rsquo;t allow turning off this functionality.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s do a quick recap to check if we meet the criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;End-to-end encryption&lt;/strong&gt; – it was the main goal,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard disk is not the limitation&lt;/strong&gt; – data are stored online, the hard drive works as cache,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OS Integration&lt;/strong&gt;– with limited support, but it&amp;rsquo;s possible,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hassle-free&lt;/strong&gt; – could be subjective, but for me, it&amp;rsquo;s &lt;em&gt;manageable&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-platform&lt;/strong&gt; – rclone works on various operating systems. For mobile access, I use a WebDAV resource exposed in my private VPN.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you know one tool to create your encrypted cloud drive, where you can &lt;strong&gt;store files without worrying about the security and privacy&lt;/strong&gt; – your data will be safe as long as your password and salt are secure. Moreover, you still have security that comes by default from your cloud storage provider.&lt;/p&gt;
&lt;p&gt;You may ask: &lt;em&gt;Isn&amp;rsquo;t it an exaggeration to encrypt everything?&lt;/em&gt; No, if you really need it. And the need should &lt;strong&gt;come from you&lt;/strong&gt;, not from the data you want to hide. If you don&amp;rsquo;t feel comfortable storing things online, but you want to benefit from that, maybe end-to-end encryption is for you. Whatever your choice, &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/&#34;&gt;remember about regular backups&lt;/a&gt; – and &lt;strong&gt;encrypt&lt;/strong&gt; them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Icons used in featured image: OneDrive logo, Padlock icon by &lt;a href=&#34;https://www.flaticon.com/authors/freepik&#34;&gt;Freepik&lt;/a&gt; from &lt;a href=&#34;https://www.flaticon.com/&#34;&gt;flaticon.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Having a maintenance-free solution when you&amp;rsquo;re wiring everything by yourself sounds like a wish, but it&amp;rsquo;s sometimes possible. I set a backup using restic some time ago, and it just works. But yes, better to be prepared that things like that need your attention from time to time. Remember also about Murphy&amp;rsquo;s Law – if it can stop working occasionally, it stops in the worst possible moment. But isn&amp;rsquo;t it true for all services at all?&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Working directly on remote encrypted storage is something I still test. However, I use different tools to synchronize files I often need. I&amp;rsquo;ve tried to use a remote photo library (not Photos app), but the performance wasn&amp;rsquo;t satisfying.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Rethinking writing</title>
      <link>https://szymonkrajewski.pl/rethinking-writing/</link>
      <pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/rethinking-writing/</guid>
      <description>&lt;p&gt;This blog is my third approach to regular writing. It&amp;rsquo;s almost three years old now, so it&amp;rsquo;s a good indicator, that this attempt was the successful one. Over time, I published more than 40 articles, which is many and few at the same time, especially when I&amp;rsquo;ll consider my approach to writing.&lt;/p&gt;
&lt;p&gt;The start wasn&amp;rsquo;t easy since I hadn&amp;rsquo;t picked any niche or the main topic. I collected some ideas for articles I could write about. I narrowed the scope to the programming field, but as time went by, I realized that there are plenty of other exciting topics, which, unfortunately, don&amp;rsquo;t fit into my blog.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;d like to set up new rules for myself to liberate my writings and thoughts, making this blog my &lt;strong&gt;unique&lt;/strong&gt; place on the Internet, once again.&lt;/p&gt;
&lt;p&gt;Writing is still not easy for me because of two kinds of factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My ability to write comprehensively on the chosen topic&lt;/li&gt;
&lt;li&gt;Rules I put on myself&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the first point concerns more the practising of writing, the second one is something that could support you or limit you.&lt;/p&gt;
&lt;p&gt;At the beginning of my journey as a writer, I decided to write about web technologies, because it&amp;rsquo;s something related to my work and hobby. I had a clear vision of how my articles should look like – both when it comes to the content and appearance. Each of my writing should &lt;strong&gt;tell the story&lt;/strong&gt;, so I tried to embed them in the proper context. Moreover, they should have the right structure, an eye-catching topic, encouraging introduction, featured photo attuned to the content of the article and, of course, summary.&lt;/p&gt;
&lt;p&gt;It works well for most of the time, but the whole writing process was tiring and time-consuming. Sometimes I wanted to write a few sentences to share something exciting, but this kind of content hasn&amp;rsquo;t suited into my blog in the way I previously assumed.&lt;/p&gt;
&lt;p&gt;Now, when I have even less time to write, I need to rethink my approach to blogging. I want to &lt;strong&gt;treat writing as a form of relaxation&lt;/strong&gt; and the way of &lt;strong&gt;sorting thoughts&lt;/strong&gt;. To rediscover the fun behind it, I want to loosen my rules and start to treat this blog more like a place for me, rather than the site about a specific topic. It should be the space, where I can put things I&amp;rsquo;m interested in, and you or anyone else who can reach this site, e.g. by a search engine can take advantage of the content I publish.&lt;/p&gt;
&lt;p&gt;Out of 43 articles I published till now, a few of them gained some popularity. Surprisingly, they aren&amp;rsquo;t that articles I&amp;rsquo;m most proud of, but the ones that &lt;strong&gt;respond to the problem I struggled with&lt;/strong&gt; or these which presenting one, sometimes obvious idea and my understanding of it.&lt;/p&gt;
&lt;p&gt;So from now, instead of enforcing myself to write, I&amp;rsquo;ll more use the momentum of things I&amp;rsquo;m interested in or these which resonate with me the most. It doesn&amp;rsquo;t mean that the quality of my texts will drop. On the contrary, adding more authenticity to my writings will help me develop and &lt;strong&gt;release my own unique voice&lt;/strong&gt;. Eventually, it allows me to be just me. I&amp;rsquo;m not always right, but I&amp;rsquo;m excited about the things I do.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@craftedbygc?utm_source=unsplash&amp;amp;amp;utm_medium=referral&amp;amp;amp;utm_content=creditCopyText&#34;&gt;Green Chameleon&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/writing?utm_source=unsplash&amp;amp;amp;utm_medium=referral&amp;amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>How to type letters with accents and diacritics in Emacs</title>
      <link>https://szymonkrajewski.pl/type-letters-with-accents-in-emacs/</link>
      <pubDate>Thu, 18 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/type-letters-with-accents-in-emacs/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve heard a lot of good things about Emacs and Org-mode package as the approach to write and organize &lt;a href=&#34;https://szymonkrajewski.pl/taking-notes-in-plaintext/&#34;&gt;plain-text notes&lt;/a&gt;. I recently found a doom-emacs project and I decided to give it a try. Unfortunately, in the default configuration, I was unable to type polish characters. Here are two solutions, how to fix it.&lt;/p&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;p&gt;The option key in macOS is used to input non-ASCII characters, e.g., polish letters like &amp;ldquo;&lt;em&gt;ąęść&lt;/em&gt;&amp;rdquo;. However, in emacs, this key is used as a &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; &lt;a href=&#34;https://www.gnu.org/software/emacs/manual/html_node/emacs/Modifier-Keys.html&#34;&gt;modifier&lt;/a&gt;. Thankfully, emacs is highly configurable, so it&amp;rsquo;s not a problem to change its behavior, including &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; key binding.&lt;/p&gt;
&lt;h2 id=&#34;preparation&#34;&gt;Preparation&lt;/h2&gt;
&lt;p&gt;You need to locate your configuration file. I use the &lt;a href=&#34;https://github.com/hlissner/doom-emacs&#34;&gt;doom-emacs&lt;/a&gt; configuration framework, so my configuration file is placed in &lt;code&gt;~/.doom.d/config.el&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After each change, you should &lt;strong&gt;sync&lt;/strong&gt; your local configuration with the emacs configuration using the following command. I assume that you store your emacs configuration in the default directory. Otherwise, you have to locate the &lt;strong&gt;doom&lt;/strong&gt; binary in your setup.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~/.emacs.d/bin/doom sync
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But first, let&amp;rsquo;s go to your configuration. You can go to the list of your configuration files directly from doom-emacs by typing sequentially &lt;code&gt;SPC f P&lt;/code&gt; (case is important). Then, select the &lt;strong&gt;config.el&lt;/strong&gt; file and press &lt;code&gt;RET&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Go to the bottom of the configuration file by pressing the &lt;code&gt;g&lt;/code&gt; key once.&lt;/p&gt;
&lt;h2 id=&#34;solution-1-use-command-key-as-meta-modifier&#34;&gt;Solution 1: Use command key as &amp;lt;Meta&amp;gt; modifier&lt;/h2&gt;
&lt;p&gt;Since you need &lt;em&gt;option key&lt;/em&gt; to behave in a standard way, you need to assign &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; modifier to another key. The &lt;a href=&#34;https://www.emacswiki.org/emacs/MetaKeyProblems#toc18&#34;&gt;emacs wiki&lt;/a&gt; proposes using &lt;em&gt;command key&lt;/em&gt; as &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;Put this snippet in your configuration file and sync it with emacs-doom.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elisp&#34; data-lang=&#34;elisp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;setq&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mac-option-key-is-meta&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;setq&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mac-command-key-is-meta&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;setq&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mac-command-modifier&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;&amp;#39;meta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;setq&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mac-option-modifier&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, you&amp;rsquo;ll lose another modifier called &amp;lt;Super&amp;gt;. If you want to keep both &amp;lt;Meta&amp;gt; and &amp;lt;Super&amp;gt; modifiers bounded, you can use the second solution.&lt;/p&gt;
&lt;h2 id=&#34;solution-2-use-only-left-option-key-as-meta-modifier&#34;&gt;Solution 2: Use only left option key as &amp;lt;Meta&amp;gt; modifier&lt;/h2&gt;
&lt;p&gt;I use the &lt;em&gt;right option key&lt;/em&gt; to write accents. It means that I can still use the &lt;em&gt;left option key&lt;/em&gt; as the &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; modifier. Emacs allows you to change the behavior of &lt;a href=&#34;https://www.emacswiki.org/emacs/EmacsForMacOS#toc31&#34;&gt;different keys&lt;/a&gt;, so you can turn off a binding of &lt;em&gt;right option key&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elisp&#34; data-lang=&#34;elisp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;setq&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mac-right-option-modifier&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;&amp;#39;none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, you can use &lt;em&gt;left option&lt;/em&gt; as &lt;code&gt;&amp;lt;Meta&amp;gt;&lt;/code&gt; and &lt;em&gt;right option&lt;/em&gt; to write accents.&lt;/p&gt;
&lt;p&gt;Have fun with Emacs but remember to get your things done!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Draw.io real-time collaboration using Visual Studio Code and Live Share</title>
      <link>https://szymonkrajewski.pl/drawio-real-time-collaboration-in-vscode/</link>
      <pubDate>Tue, 19 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/drawio-real-time-collaboration-in-vscode/</guid>
      <description>&lt;p&gt;You&amp;rsquo;ve probably heard about &lt;em&gt;draw.io&lt;/em&gt; which is one of the best free, open-source diagramming software. The tool is powerful. It supports many storage backends and could be integrated into other apps. But the one crucial feature is still missing – it has lack of &lt;em&gt;collaborative editing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Remote collaboration is something we need these days. Last time, &lt;em&gt;Visual Studio Code&lt;/em&gt; announced extension called &lt;em&gt;Live Share&lt;/em&gt;, allowing developers to share their workspace with other people. In consequences, it opens a bunch of new possibilities!&lt;/p&gt;
&lt;p&gt;Thanks to the &lt;em&gt;VSCode&lt;/em&gt;, &lt;em&gt;LiveShare&lt;/em&gt; and &lt;em&gt;draw.io integration&lt;/em&gt;, &lt;strong&gt;you can bring the power of collaboration into &lt;em&gt;draw.io&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;drawio-collaborative-editing-in-vscode&#34;&gt;Draw.io collaborative editing in VSCode&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;a href=&#34;https://code.visualstudio.com&#34;&gt;Visual Studio Code&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Find and install aditional extensions: &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare-pack&#34;&gt;Live Share&lt;/a&gt; and &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio&#34;&gt;draw.io integration&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.microsoft.com/en-gb/visualstudio/liveshare/quickstart/share#2-sign-in&#34;&gt;Sign in to Live Share&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a new workspace and &lt;a href=&#34;https://docs.microsoft.com/en-gb/visualstudio/liveshare/quickstart/share#5-start-a-collaboration-session&#34;&gt;share the session&lt;/a&gt; with your friend.&lt;/li&gt;
&lt;li&gt;Create new &lt;code&gt;.drawio&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Enjoy!&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube-nocookie.com/embed/9GnTZRSzo4c?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://visualstudio.microsoft.com/services/live-share/&#34;&gt;Live Share&lt;/a&gt; in &lt;em&gt;Visual Studio Code&lt;/em&gt; allows you to create a collaboration session. Depending on the settings, you can share your workspace in write or read-only mode. Anyone, who connects to your session will see your project in its own, customized editor.&lt;/p&gt;
&lt;p&gt;Diagrams produced by &lt;em&gt;draw.io&lt;/em&gt; are XML files. Thanks to &lt;em&gt;draw.io integration&lt;/em&gt; extension, you can edit them directly in the VSCode using the visual editor. If you have &lt;code&gt;.drawio&lt;/code&gt; files stored in your workspace, others also have access to them. Each participant who has draw.io integration will see diagram instead of the XML file.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;Live Share&lt;/em&gt;, every change made by collaborators is sent immediately to all participants. Because diagrams are just XML files, every change in the visual editor are reflected in the file and sent to all participants. You&amp;rsquo;ll see everything in real-time!&lt;/p&gt;
&lt;h2 id=&#34;limitation-of-collaboration-in-drawio-using-vscode&#34;&gt;Limitation of collaboration in draw.io using VSCode&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You will not see the cursors of other participants in the visual editor.&lt;/li&gt;
&lt;li&gt;You will see only the performed changes. It means that you won&amp;rsquo;t see the move of &lt;em&gt;block&lt;/em&gt; but only its final position.&lt;/li&gt;
&lt;li&gt;You may experience issues when more than one collaborator is working on the same element.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Do you know any other limitations or problems? Please let me know!&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://draw.io&#34;&gt;Draw.io&lt;/a&gt; (or should I use &lt;a href=&#34;https://www.diagrams.net&#34;&gt;diagrams.net&lt;/a&gt;?) is one of the best tools for creating diagrams. Real-time collaboration feature would be just the cherry on top. According to the documentation, this tool &lt;a href=&#34;https://desk.draw.io/support/solutions/articles/16000039382-how-to-share-a-draw-io-online-diagram-with-another-user-&#34;&gt;offers collaboration when you use a Google Drive storage&lt;/a&gt;, but I&amp;rsquo;m not sure if it&amp;rsquo;s a real-time collaboration – I haven&amp;rsquo;t tested it. Anyway, &lt;strong&gt;the need is real&lt;/strong&gt;. By using a combination of &lt;em&gt;VSCode&lt;/em&gt;, &lt;em&gt;Live Share&lt;/em&gt; and &lt;em&gt;draw.io&lt;/em&gt; you can reach the collaborative experience with relatively low cost.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>How to convince your boss to let you write tests</title>
      <link>https://szymonkrajewski.pl/how-to-convince-your-boss-to-let-you-write-tests/</link>
      <pubDate>Wed, 06 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/how-to-convince-your-boss-to-let-you-write-tests/</guid>
      <description>&lt;p&gt;When we talk about software development, sooner or later someone starts a discussion about tests. In our world, tests are considered as something helpful and crucial to our work. However, sometimes we need to face with people who have a different opinion about it. Moreover, it&amp;rsquo;s hard to find a proper argument, especially for a person who pays you and who wants to utilize your time effectively.&lt;/p&gt;
&lt;p&gt;How can you convince your boss to let you write tests? Should you even ask for permission?&lt;/p&gt;
&lt;h2 id=&#34;convince-yourself-first&#34;&gt;Convince yourself first&lt;/h2&gt;
&lt;h3 id=&#34;you-already-test-the-app-manually&#34;&gt;You already test the app manually&lt;/h3&gt;
&lt;p&gt;You can compile the application, run it and check if your changes work by clicking through the app. Your feature may not work after the first trial, so you need to fix the code and go through the same process – compiling, running and checking. &lt;strong&gt;You already perform manual tests&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This testing process gives you feedback about your work. If the solution works and matches the requirements, you can go ahead. Otherwise, you need to go back to the development phase.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s unreliable to write the code and then commit it without testing if it works. Even simple manual tests are vital to confirm that your change is working correctly.&lt;/p&gt;
&lt;h3 id=&#34;youre-responsible-for-writing-good-quality-software&#34;&gt;You&amp;rsquo;re responsible for writing good-quality software&lt;/h3&gt;
&lt;p&gt;Your job is to deliver working, good-quality software. Some people say about good-enough level, but it still software that is reliable, &lt;a href=&#34;https://szymonkrajewski.pl/write-code-for-people/&#34;&gt;easy to maintain and develop&lt;/a&gt;. When the company hires you, people expect that you&amp;rsquo;ll do your best to deliver software, not worse that it currently is.&lt;/p&gt;
&lt;h3 id=&#34;tests-are-necessary-to-write-reliable-software&#34;&gt;Tests are necessary to write reliable software&lt;/h3&gt;
&lt;p&gt;I don&amp;rsquo;t know any good piece of software which was writing without tests. As long as the application is small and simple, finding bugs and problems isn&amp;rsquo;t a big deal. But the more complex app, the more time you have to spend testing it manually. Ultimately, it&amp;rsquo;s almost impossible to check every affected use-case in a reasonable amount of time, so developers test only the most crucial part of the app. It should no longer be considered reliable – &lt;strong&gt;nobody can prove it&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;writing-tests-is-a-part-of-the-development-process&#34;&gt;Writing tests is a part of the development process&lt;/h3&gt;
&lt;p&gt;Tests, especially unit and integration tests, support you with day-to-day development. You can write them before the code (according to TDD), but you can also create them after the production code. Anyway, they are still part of the development process. You may don&amp;rsquo;t know which strategy would be better in the specific case.&lt;/p&gt;
&lt;p&gt;You should keep the development and testing&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; parts under the same sign – software development. Mark features as done, but &lt;strong&gt;only if you finished both&lt;/strong&gt;: production code and tests.&lt;/p&gt;
&lt;h2 id=&#34;how-to-convince-your-boss-to-let-you-write-tests&#34;&gt;How to convince your boss to let you write tests?&lt;/h2&gt;
&lt;p&gt;Simple answer: &lt;strong&gt;do not do this&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you convinced yourself first, you should know that &lt;strong&gt;tests are for you&lt;/strong&gt;. Your job is to deliver working, good-quality software. How could you do that without testing if the code you wrote works correctly? Do you provide untested solutions? I know you don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;What would say your boss, if you would ask him if the feature you work on should be tested? Would he be surprised? For sure, he pays you to deliver working features. &lt;strong&gt;He expects the outcome&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;People expect that they get a good quality product. In the restaurant, the chief wouldn&amp;rsquo;t ask you if the dish you&amp;rsquo;ve ordered should be tasty or not.&lt;/p&gt;
&lt;h2 id=&#34;writing-tests-should-be-your-decision&#34;&gt;Writing tests should be your decision&lt;/h2&gt;
&lt;p&gt;If it doesn&amp;rsquo;t convince you, ask yourself, who will be responsible if something goes wrong? The code is the common good, so &lt;a href=&#34;https://szymonkrajewski.pl/the-code-is-a-common-good/&#34;&gt;the whole team should take responsibility&lt;/a&gt; for each failure and mistake. Not your boss, who doesn&amp;rsquo;t let you write tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tests are for you&lt;/strong&gt;. They should help you with your work. Saying &amp;ldquo;&lt;em&gt;you shouldn&amp;rsquo;t write tests, it&amp;rsquo;s a waste of time&lt;/em&gt;&amp;rdquo; have roughly the same meaning as &amp;ldquo;&lt;em&gt;you shouldn&amp;rsquo;t use design patterns, it&amp;rsquo;s a waste of time&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Having tests is like having contracts for requirements to the developed feature. Having automated tests is like having a partner to check if the code meets all requirements.&lt;/p&gt;
&lt;p&gt;Remember, you already test your software, anyway. You can also automate this process. Is it in anyone business to discourage you not to test software you produce?&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t ask for permission to write good-quality software. &lt;strong&gt;It&amp;rsquo;s your job&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;change-the-way-you-communicate&#34;&gt;Change the way you communicate&lt;/h2&gt;
&lt;p&gt;We all know that tests should be an integral part of any feature we produce, any new class we write and any other development endeavour. &lt;strong&gt;Tests are part of our craft, not extras to it&lt;/strong&gt;. However, this is not consistent with the way how we use to communicate the testing part to the outside world. Have you heard this?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Yes, this feature is ready, but I have to write some tests for it.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;It&amp;rsquo;s done, yet I need to write unit tests.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Everything is working, but I need a couple of hours to write automated tests.&amp;rdquo;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Since we agreed that tests are an integral part of the development process, &lt;strong&gt;why we talk about them as if they were a separate part?&lt;/strong&gt; How could something be done if a part of the whole is missing?&lt;/p&gt;
&lt;p&gt;By talking that the feature is done without tests, you communicate that development and tests are two separate parts&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. Moreover, if the first part could be finished and delivered without touching the latter, it shows that tests are redundant.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not about hiding the fact you&amp;rsquo;re writing tests. Your goal should be not to deceive the management but to show the actual situation honestly.&lt;/p&gt;
&lt;p&gt;Change the way you talk about tests. Treat them as a part of the development process.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@earbiscuits?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Juan Rumimpunu&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Unit tests and integration tests.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;On a higher level, when we talk about the QA process, they actually could be two separate parts.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Why I migrated my blog from WordPress to Hugo</title>
      <link>https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/</link>
      <pubDate>Thu, 16 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/</guid>
      <description>&lt;p&gt;Before you start your blog, you have to perform a set of essential choices – topics you want to write about, domain name, and the way you&amp;rsquo;ll publish your content. I choose the self-hosted WordPress platform with a slightly customized theme that I found on the Internet.&lt;/p&gt;
&lt;p&gt;After more than two years of blogging, I migrated all my content to Hugo. In this article, I would like to show you why I decided to move on from WordPress to Hugo – a static page generator.&lt;/p&gt;
&lt;h2 id=&#34;why-i-started-with-wordpress&#34;&gt;Why I started with WordPress&lt;/h2&gt;
&lt;p&gt;Before I tell you why I changed WordPress to Hugo, let me explain why I choose WP as my leading platform.&lt;/p&gt;
&lt;p&gt;When it comes to writing a blog, we have two definitions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write a blog – write a whole blog engine from scratch.&lt;/li&gt;
&lt;li&gt;Write a blog – regularly publish new content to share knowledge.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you want to start blogging and you begin with creating your own CMS, blogging platform, or something like that, you&amp;rsquo;re doing it wrong. You just create another piece of software, side-project, or something that you should maintain in time. I had tried this path, and it turned out that I wanted to test myself if I can write a blog engine rather than a blog itself.&lt;/p&gt;
&lt;p&gt;Does it mean that creating a blog engine from scratch is bad? – No, if your goal is to create a blog engine.&lt;/p&gt;
&lt;p&gt;When I started my current blog, I decided to focus on content. I wanted to write and publish regularly, and I knew that I don&amp;rsquo;t want to spend time maintaining a platform. &lt;a href=&#34;https://szymonkrajewski.pl/think-big-start-small/&#34;&gt;I thought big, but I wanted to start small&lt;/a&gt;. I choose the safe option – WordPress with a free, easy to customize template. Easy to modify because it&amp;rsquo;s PHP. Easy to extend because of lots of plugins. It worked.&lt;/p&gt;
&lt;p&gt;So the reason was simple – &lt;strong&gt;content is more important than platform&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-i-changed-wordpress&#34;&gt;Why I changed WordPress&lt;/h2&gt;
&lt;h3 id=&#34;wordpress-needs-php-powered-server&#34;&gt;WordPress needs PHP-powered server&lt;/h3&gt;
&lt;p&gt;My blog was simple. I hosted my articles with comments feed, and I needed some space for images and other assets. Besides that, I had a few pages with extra information, e.g., &lt;em&gt;contact&lt;/em&gt; and &lt;em&gt;about me&lt;/em&gt;. That&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;Since WordPress is written in PHP, it needs a server to operate. It&amp;rsquo;s not a high cost, but it&amp;rsquo;s still the cost I incurred. For me, it was no reason to pay money for it knowing that I can reflect the same functionality on a statically generated page an host it for free.&lt;/p&gt;
&lt;h3 id=&#34;wordpress-needs-constant-maintenance&#34;&gt;WordPress needs constant maintenance&lt;/h3&gt;
&lt;p&gt;WordPress is a living project. It gets frequent updates, as well as its plugins and themes. It happened that I had updated them a few times per week. It wasn&amp;rsquo;t necessary, but it&amp;rsquo;s better to keep everything up-to-date.&lt;/p&gt;
&lt;p&gt;When it comes to the themes, I used a child theme that derivates from another template. It was a rare situation, but when I updated the parent template, something break on my page. It happened twice, but I&amp;rsquo;ve never been ready to put extra time to fix it.&lt;/p&gt;
&lt;h3 id=&#34;wordpress-is-well-known-for-bots&#34;&gt;WordPress is well-known for bots&lt;/h3&gt;
&lt;p&gt;WordPress is a very popular, open-source project which powers millions of websites. They serve the comments system, login form, and many other things in the same way. Bots that browse the Internet know how to scrape data, write comments, and how to attack login form with some default credentials.&lt;/p&gt;
&lt;p&gt;Thanks to plugins like Akismet and Wordfence, which mitigated the effects of bots and potential attackers, my blog instance operated normally. However, my website is so simple that I don&amp;rsquo;t want to worry about security.&lt;/p&gt;
&lt;h3 id=&#34;my-wordpress-powered-blog-was-slow&#34;&gt;My WordPress-powered blog was slow&lt;/h3&gt;
&lt;p&gt;I didn&amp;rsquo;t take care of the speed site at the beginning of my blogging journey. If you have little content and no visitors, the speed is not a factor you should improve.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m interested in optimization, so I knew that plenty of things in my theme could be better. Instead of investing time to fix them, I set up Cloudflare as a proxy for my website. Thanks to it, it was caching most of the resources, increasing the overall site speed in the outcome.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/speed_insights_old_mobile_website.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/speed_insights_old_mobile_website_hu_5b48411bd07b3f8.png 360w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/speed_insights_old_mobile_website_hu_23c80fab3b013d1e.png 640w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/speed_insights_old_mobile_website_hu_82b869a5fae2d11f.png 960w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/speed_insights_old_mobile_website_hu_f81a07c4298f76e3.png 1086w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1086&#34;
          height=&#34;486&#34;
           alt=&#34;The result of PageSpeed Insight for my old, WordPress powered blog. Score: 56 for mobile.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The result of PageSpeed Insight for my old, WordPress powered blog.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I didn&amp;rsquo;t test any cache-related plugin in WordPress, but maybe it could solve the problem with speed?&lt;/p&gt;
&lt;h2 id=&#34;why-i-moved-to-static-page&#34;&gt;Why I moved to static page?&lt;/h2&gt;
&lt;p&gt;I heard about static-page generators way before I started my blog. I didn&amp;rsquo;t have any experience with them, but the whole idea was promising. During years, a lot of &lt;a href=&#34;https://www.staticgen.com&#34;&gt;static page generators&lt;/a&gt; emerged, and more blogs were conducted this way.&lt;/p&gt;
&lt;p&gt;I tested Gatsby for a while to check what it offers. However, I dropped out of it and picked the Hugo instead. I read a lot about how fast and simple it is. Unlike Gatsby, Hugo doesn&amp;rsquo;t provide any plugin system, so everything depends on the used theme. For some people, this is a huge limitation. For me, it was a nod to simplicity I like.&lt;/p&gt;
&lt;p&gt;Ultimately, I migrated to Hugo because it fits perfectly in my workflow, whereas &lt;a href=&#34;https://szymonkrajewski.pl/form-may-limit-you/&#34;&gt;publishing with WordPress just limits me&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;code-is-a-single-source-of-truth&#34;&gt;Code is a single source of truth&lt;/h3&gt;
&lt;p&gt;Hugo generates the page from files, so everything is reflected in the code base and versioned in a git repository. There is no external database providing additional configuration and admin panel, where you can change some settings. &lt;strong&gt;Code is a single source of truth&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If I want to change something in layout, I don&amp;rsquo;t have to look for a proper hook in WordPress lifecycle – I just go to the file and modify it. If I need to update CSS, I go to the stylesheet without thinking if &lt;em&gt;Additional CSS in theme customization&lt;/em&gt; will be better for this change or not.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a programmer, and working with the code is more convenient for me than jumping across the interface. It is also valid for my writings – I prefer to write using Markdown syntax, instead of formatting options in a rich-text editor.&lt;/p&gt;
&lt;h3 id=&#34;one-place-for-my-writings&#34;&gt;One place for my writings&lt;/h3&gt;
&lt;p&gt;I used to store my writings in a separate repository to have a history of all changes. Because I published with WordPress, I had to convert articles to HTML and then put them to blog.&lt;/p&gt;
&lt;p&gt;Writings are &lt;strong&gt;living things&lt;/strong&gt;. Although I like to write timeless materials, sometimes I need to update them because my understanding of a given topic got better. Sometimes I just notice a language mistake I want to fix. Having the same content in two places was inconvenient, especially when I had to provide a change. I often forgot to move updates from WordPress to origin files.&lt;/p&gt;
&lt;p&gt;Hugo supports Markdown. It works purely on text files, so all of my writings are in &lt;strong&gt;one place in one format&lt;/strong&gt;. I can edit them using my favorite editor and publish them without worrying about how to move it to WordPress&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;why-i-migrated-to-hugo&#34;&gt;Why I migrated to Hugo?&lt;/h3&gt;
&lt;p&gt;The features I mentioned above are not exclusively in Hugo but almost in every other static page generator. However, Hugo met all my criteria: is fast and straightforward, has content powered by Markdown, its engine isn&amp;rsquo;t in JavaScript. The last one is related to the problem of longevity. I want to have something reliable for years. I don&amp;rsquo;t want to be surprised that I can&amp;rsquo;t generate my blog because of issues with dependencies after an update. The second reason was more &lt;em&gt;prosaic&lt;/em&gt; – I want to try something new.&lt;/p&gt;
&lt;h2 id=&#34;migration&#34;&gt;Migration&lt;/h2&gt;
&lt;p&gt;The whole migration process took me more time than I expected, but I should consider it as a normal situation – estimation is a hard thing. The migration process had three main parts: &lt;strong&gt;template migration&lt;/strong&gt;, &lt;strong&gt;content migration&lt;/strong&gt;, and &lt;strong&gt;deployment&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;template-migration&#34;&gt;Template migration&lt;/h3&gt;
&lt;p&gt;Previously my blog was powered by a customized version of template called &lt;em&gt;&lt;a href=&#34;https://wordpress.org/themes/themelia/&#34;&gt;Themelia&lt;/a&gt;&lt;/em&gt;. It&amp;rsquo;s a simple theme, and I wanted to keep the same simplicity.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_260eee534dd60c8c.webp 360w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_5a98e404053b7cfe.webp 640w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_fbd283a0ed7fd79d.webp 960w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_2171e6ca37992446.webp 1200w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_632b132c43453e59.jpg 360w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_7d83b9af98a2d84c.jpg 640w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_55c0b92afb66305a.jpg 960w, https://szymonkrajewski.pl/why-i-migrated-from-wordpress-to-hugo/themelia_old_blog_screenshot_hu_f0ece833d5899acf.jpg 1200w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1200&#34;
          height=&#34;820&#34;
           alt=&#34;My blog on customized Themelia template.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      My blog on customized Themelia template.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I had been looking for the template for Hugo, and I found the project &lt;a href=&#34;https://github.com/panr/hugo-theme-hello-friend&#34;&gt;hello-friend&lt;/a&gt;. It&amp;rsquo;s a blog template with a clean design, very similar to what I needed. I decided to use it as a foundation for my custom theme. Its author, Radek Kozieł (&lt;a href=&#34;http://radoslawkoziel.pl/&#34;&gt;@panr&lt;/a&gt;), has done an outstanding job – Thank you, Radek!&lt;/p&gt;
&lt;p&gt;After a few modifications, the blog started looking similar to the WordPress version.&lt;/p&gt;
&lt;h3 id=&#34;content-migration&#34;&gt;Content migration&lt;/h3&gt;
&lt;p&gt;Although I kept drafts of my posts in Markdown, most of them were not updated with the live version. To avoid publishing old content, I decided to migrate everything from WordPress.&lt;/p&gt;
&lt;p&gt;In the beginning, I tried to do everything manually, since I didn&amp;rsquo;t publish lots of articles. I was coping and pasting the content from WordPress and manually changing HTML tags to Markdown and tweaking &lt;em&gt;YAML Front Matter&lt;/em&gt;. After three posts (or a few hours), I thought that it had to be an easier way to do this.&lt;/p&gt;
&lt;p&gt;I had installed &lt;a href=&#34;https://github.com/SchumacherFM/wordpress-to-hugo-exporter&#34;&gt;WordPress to Hugo Exporter&lt;/a&gt; plugin, and thanks to it, I saved a lot of time. It did 90% of the work I had previously done manually. Great tool.&lt;/p&gt;
&lt;p&gt;In exported articles, I had to change yet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;YAML Front Matter&lt;/em&gt; and settings related to the used Hugo theme&lt;/li&gt;
&lt;li&gt;Hardcoded internal links to links generated by &lt;a href=&#34;https://gohugo.io/functions/ref/&#34;&gt;ref Hugo&amp;rsquo;s function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Display images by &lt;a href=&#34;https://gohugo.io/content-management/shortcodes/#figure&#34;&gt;&lt;em&gt;figure&lt;/em&gt; shortcode&lt;/a&gt;, instead of Markdown syntax (to have a caption built-in)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;yaml-front-matter&#34;&gt;YAML Front Matter&lt;/h4&gt;
&lt;p&gt;My &lt;em&gt;YAML Front Matter&lt;/em&gt; looks as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Why should you return early?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ld&#34;&gt;2017-11-20T09:00:25&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;+00&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;slug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;why-should-you-return-early&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;cover&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;images/Featured-Return-early.jpg&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;useRelativeCover&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;Software Development&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;Code Style&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;PHP&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;Software Design&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The migration tool put the &lt;code&gt;url&lt;/code&gt; parameter for each post. However, I prefer to use a &lt;code&gt;slug&lt;/code&gt; instead. Properties &lt;code&gt;cover&lt;/code&gt; and &lt;code&gt;useRelativeCover&lt;/code&gt; are used by the template to display the post&amp;rsquo;s featured image.&lt;/p&gt;
&lt;h3 id=&#34;content-organization&#34;&gt;Content organization&lt;/h3&gt;
&lt;h4 id=&#34;group-articles-together&#34;&gt;Group articles together&lt;/h4&gt;
&lt;p&gt;The migration of the content was the one problem, but how to organize them in Hugo is a different story. By default, Hugo reflects the structure of the &lt;em&gt;content&lt;/em&gt; directory during page build. However, links to my articles consist of domain and slug – they are in the &lt;em&gt;&amp;ldquo;root directory&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d not like to keep everything in one single directory. Fortunately, Hugo allows configuring &lt;strong&gt;permalinks&lt;/strong&gt; to the specific type of content.&lt;/p&gt;
&lt;p&gt;So, i put all of my posts to &lt;code&gt;/content/posts&lt;/code&gt; directory and in &lt;code&gt;config.toml&lt;/code&gt; I configured a proper permalink:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;permalinks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;posts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;:slug/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;content-ordering&#34;&gt;Content ordering&lt;/h4&gt;
&lt;p&gt;Most of the IDEs keep file structure ordered alphabetically. When it comes to my writings, It&amp;rsquo;s more comfortable for me to order articles by &lt;strong&gt;published date&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I prefixed each article with the date to retain the order. Without it, old content was mixed with the new one, and everything looked messy. The one downside is that I have to override the &lt;code&gt;slug&lt;/code&gt; property in &lt;em&gt;YAML Front Matter&lt;/em&gt; for each post. Otherwise, the URL will contain a date as well.&lt;/p&gt;
&lt;h4 id=&#34;store-assets-using-page-bundles&#34;&gt;Store assets using Page Bundles&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://gohugo.io/content-management/page-bundles/&#34;&gt;Page Bundles&lt;/a&gt; in Hugo allowed me to keep together everything related to the single article. It means that I don&amp;rsquo;t have to put every image in &lt;code&gt;static&lt;/code&gt; directory, which was default place for pictures and other non-processed stuff. Moreover, &lt;a href=&#34;https://gohugo.io/content-management/image-processing/#readout&#34;&gt;images stored as page resources could be processed&lt;/a&gt; (&lt;em&gt;resize, crop, transformation&lt;/em&gt;), so it&amp;rsquo;s a way for future optimizations.&lt;/p&gt;
&lt;p&gt;So, instead of keeping markdown files directly in the &lt;code&gt;content/posts&lt;/code&gt; directory, I created a separate directory for each article and other related assets.&lt;/p&gt;
&lt;h3 id=&#34;comments&#34;&gt;Comments&lt;/h3&gt;
&lt;p&gt;Comments were the big unknown for me since that was the only dynamic part on my website. However, I realized that most of the comments I got were spam filtered by Akismet. Besides that, I got a few valuable comments, which were a big inspiration for me – thank you.&lt;/p&gt;
&lt;p&gt;I read a lot about the Disqus, and I saw the pros and cons, but I don&amp;rsquo;t want to bloat my site with ads, trackers, and so on&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. I would set up my own comments system, but this is another piece of software I would have to maintain, but you know – &lt;a href=&#34;https://szymonkrajewski.pl/the-hidden-cost-of-self-hosted-services/&#34;&gt;self-hosted services, although free, have a hidden cost&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I explored the &lt;a href=&#34;https://indieweb.org&#34;&gt;Indieweb&lt;/a&gt; and found feature called &lt;a href=&#34;https://indieweb.org/Webmention&#34;&gt;Webmentions&lt;/a&gt;. It looks promising because it would let me collecting mentions about the post from external channels, like e.g., Twitter. Yet, this way of commenting is not obvious for everyone, and I would treat it more like an extra rather than a default manner.&lt;/p&gt;
&lt;p&gt;So, for now, I don&amp;rsquo;t have comments. Maybe I&amp;rsquo;ll add them in the future. If you want to refer to my article, the best option to do it for know is Twitter – you can reach me out there by mentioning &lt;a href=&#34;https://twitter.com/skrajewski_pl&#34;&gt;@skrajewski&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;hosting-and-deployment&#34;&gt;Hosting and deployment&lt;/h2&gt;
&lt;p&gt;Since I move to the static page, I don&amp;rsquo;t think I need the whole LAMP stack any longer. I paid for the hosting (not so much, but this investment was not paying back), but for now, it would be overkill.&lt;/p&gt;
&lt;p&gt;I did a little research, and it turned out that lots of services offer free (but limited) hosting of static pages. I thought about the &lt;a href=&#34;https://pages.github.com&#34;&gt;GitHub pages&lt;/a&gt;, but eventually, I landed on &lt;a href=&#34;https://www.netlify.com&#34;&gt;Netlify&lt;/a&gt;, mostly because they provide CI solution for Hugo websites. Moreover, Netlify manages its CDN, so it&amp;rsquo;s no need to use services like Cloudflare. It&amp;rsquo;s can also generate and renew SSL certificates using LetsEncrypt.&lt;/p&gt;
&lt;p&gt;What is also important, Netlify handles redirects, so providing a custom 404 template or redirect from old article URL is not a problem.&lt;/p&gt;
&lt;h2 id=&#34;what-i-forgot-or-didnt-realize&#34;&gt;What I forgot or didn&amp;rsquo;t realize&lt;/h2&gt;
&lt;p&gt;I prepared myself for most of the situations, but there are some I wish to know about it earlier. Not big deals, but worth considering.&lt;/p&gt;
&lt;h3 id=&#34;e-mail-hosting&#34;&gt;E-mail hosting&lt;/h3&gt;
&lt;p&gt;On my old hosting, besides the HTTP server, I took advantage of e-mail hosting. Once I dropped out of it, I lost e-mails addresses in my domain. I haven&amp;rsquo;t used them so much. However, for me, they were a nice addition.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve looked for e-mail providers, but I can&amp;rsquo;t find any affordable solution. It&amp;rsquo;s not crucial for me. Therefore I don&amp;rsquo;t want to spend too much money on things I don&amp;rsquo;t urgently need – otherwise, I probably would stay on previous my hosting. If you know worth recommended e-mail hosting providers, please let me know on &lt;a href=&#34;https://twitter.com/skrajewski_pl&#34;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;markdown-is-less-markdown&#34;&gt;Markdown is less Markdown&lt;/h3&gt;
&lt;p&gt;Markdown is simple. If you need to write text with headings, some tables, and images – it&amp;rsquo;s a perfect choice. However, the more custom features you want to implement in your articles, the less Markdown it becomes.&lt;/p&gt;
&lt;p&gt;In Hugo, if you need to display something custom in the article, you need to use a shortcode. The one shortcode may display a responsive image, other an alert, or an infobox. But in any other Markdown editor, they have no representation, and they are just text. Moreover, ugly text.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&amp;lt; figure &lt;span class=&#34;nv&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;057_window_keyboard_shortcuts_help.jpg&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;alt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;The Keyboard Settings Window on macOS with the enabled shortcut on the list.&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;caption&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Keyboard Settings Window. Make sure you have \&amp;#34;Show Help menu\&amp;#34; action enabled and assigned to the proper shortcut.&amp;#34;&lt;/span&gt;&amp;gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to keep your articles in Markdown, you&amp;rsquo;ll need to limit the number of shortcodes you use. Hugo allows to set up &lt;a href=&#34;https://gohugo.io/getting-started/configuration-markup#markdown-render-hooks&#34;&gt;&lt;em&gt;Markdown render hooks&lt;/em&gt;&lt;/a&gt;, so you can provide templates for images and link that Hugo&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt; uses during parsing native Markdown syntax.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The whole process was fun, and I learned a lot by migrating from WordPress to Hugo. It&amp;rsquo;s still a lot of things to do and ideas to implement and test, but it&amp;rsquo;s not crucial at this moment – the more valuable is content – articles that I produce, and you read.&lt;/p&gt;
&lt;p&gt;Do I regret that I started with WordPress? No. Using WordPress as a platform is convenient, and if your goal is to write, you don&amp;rsquo;t need anymore. I didn&amp;rsquo;t worry about the technical details, images, and so on – everything was managed by the blog engine.&lt;/p&gt;
&lt;p&gt;But WordPress allowed me to meet my needs. I saw that some things are not for me, others are fantastic and some others could be better. &lt;strong&gt;I decided on migration based on the experience, not the hype.&lt;/strong&gt; WordPress is still a choice for people with no technical background.&lt;/p&gt;
&lt;p&gt;Remember that the essential part of each blog is the content it serves. Not template, not an engine, but the author&amp;rsquo;s effort to assemble thoughts in writing.&lt;/p&gt;
&lt;p&gt;I hope you&amp;rsquo;ll find value in this article.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@webaroo?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Webaroo&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/wordpress?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I heard about the JetPack plugin that brings Markdown support to WordPress. However, it&amp;rsquo;s not kind of plugin I wanted to use anyway.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;You can find more details about the Disqus privacy and performance concerns in &lt;a href=&#34;https://markosaric.com/remove-disqus/&#34;&gt;Marco Saric&amp;rsquo;s article&lt;/a&gt; or &lt;a href=&#34;https://victorzhou.com/blog/replacing-disqus/&#34;&gt;Victor Zhou&amp;rsquo;s artcile&lt;/a&gt;, or by looking for phrase &amp;ldquo;&lt;em&gt;Why should you remove Disqus&amp;rdquo;&lt;/em&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;Hugo uses Goldmark renderer, which supports this kind of hooks.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>How to boot system from USB using GRUB</title>
      <link>https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/</link>
      <pubDate>Thu, 02 Apr 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/</guid>
      <description>&lt;p&gt;Almost every modern computer has a dedicated menu to choose from which device the system should boot. If not, there is an option in BIOS/UEFI to change that. But what if both options failed for some reason?&lt;/p&gt;
&lt;p&gt;One of my old computers runs Linux with GRUB as a bootloader. I use it from time to time, but one day the filesystem crashed, and even &lt;code&gt;fsck&lt;/code&gt; couldn&amp;rsquo;t repair it. I prepared a USB stick with the new system, but I couldn&amp;rsquo;t boot from it – BIOS was restoring the booting order on every reboot. Moreover, ever since I installed GRUB, the shortcut to summon booting device selection stopped working.&lt;/p&gt;
&lt;p&gt;I had an opportunity to learn how to boot from USB using GRUB.&lt;/p&gt;
&lt;h2 id=&#34;how-to-boot-from-usb-using-grub&#34;&gt;How to boot from USB using GRUB&lt;/h2&gt;
&lt;p&gt;I assume that you already have a bootable USB drive with your favorite Linux distro. If not, you can use a tool called &lt;a href=&#34;https://unetbootin.github.io&#34;&gt;Unetbootin&lt;/a&gt; to create a bootable USB drive. In this case, I use a Ubuntu distro.&lt;/p&gt;
&lt;p&gt;If you have your bootable USB drive, insert it into the computer.&lt;/p&gt;
&lt;h3 id=&#34;boot-from-proper-device&#34;&gt;Boot from proper device&lt;/h3&gt;
&lt;p&gt;When the GRUB screen appear, press &lt;kbd&gt;C&lt;/kbd&gt; or &lt;kbd&gt;ESC&lt;/kbd&gt; to go to the GRUB console. You should see the &lt;code&gt;grub&amp;gt;&lt;/code&gt; prompt.&lt;/p&gt;
&lt;p&gt;First, check if GRUB detects your USB drive. Type command &lt;code&gt;ls&lt;/code&gt; to see available devices.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; ls
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;lvm/ubuntu-vg-swap_1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;lvm/ubuntu--vg-root&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;hd0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;hd0,msdos1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;hd1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;hd1,gpt2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;hd1,gpt1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;cd0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_d9286a0830da7dc.webp 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_33e1d829446b96b8.webp 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_c708a87239173db.webp 800w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_eb81ae9146efae7c.jpg 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_d01825e4c16398a.jpg 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_ls_hu_22f917f6a21d1268.jpg 800w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;800&#34;
          height=&#34;242&#34;
           alt=&#34;Screen from the GRUB console presenting output of ls command.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The output of &lt;em&gt;ls&lt;/em&gt; command in GRUB console.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;GRUB recognizes my USB stick as &lt;code&gt;(hd0,msdos1)&lt;/code&gt;. I know it because I recently formatted it and created &lt;em&gt;MS-DOS (FAT)&lt;/em&gt; partition.&lt;/p&gt;
&lt;p&gt;Now, change the &lt;em&gt;root&lt;/em&gt; to this device.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;hd0,msdos1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can check if you&amp;rsquo;re using a proper device. By using the command &lt;code&gt;find&lt;/code&gt; and pressing &lt;kbd&gt;TAB&lt;/kbd&gt; key, you can preview the list of files on it.&lt;/p&gt;
&lt;p&gt;Next step is to locate the bootloader on USB stick to &lt;a href=&#34;https://www.gnu.org/software/grub/manual/grub/html_node/Chain_002dloading.html&#34;&gt;chain-load&lt;/a&gt;. In my case, the path to the bootloader is &lt;code&gt;/efi/boot/grubx64.efi&lt;/code&gt;. You can use &lt;kbd&gt;TAB&lt;/kbd&gt; to invoke auto-completion and type the path faster.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; chainloader /efi/boot/grubx64.efi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_19791f39033c2624.webp 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_b66c18c257cf8abf.webp 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_38c97975b5483347.webp 800w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_4872cfdb26a609af.jpg 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_4f79b22cc2cf1947.jpg 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_chainloader_hu_7f227f066600441a.jpg 800w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;800&#34;
          height=&#34;292&#34;
           alt=&#34;Screen from the GRUB console presenting preparation for chainloader command.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Screen from the GRUB console where I executed &lt;em&gt;chainload&lt;/em&gt; command.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;To invoke the new bootloader, run the command &lt;code&gt;boot&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grub&amp;gt; boot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_2ab21e9a53bc5f06.webp 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_175de804b5564d91.webp 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_f78de155215df49e.webp 800w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_327c90dadd910ce9.jpg 360w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_a07130951ded273d.jpg 640w, https://szymonkrajewski.pl/how-to-boot-system-from-usb-using-grub/grub_from_usb_hu_b47fc36baf1b3bd.jpg 800w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;800&#34;
          height=&#34;325&#34;
           alt=&#34;GRUB booted from the SB drive.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      GRUB booted from the USB drive.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This process doesn&amp;rsquo;t look so complicated. However, it took me almost two hours before I finally figured it out. I tried different ways to fix the filesystem first, but starting from scratch was the easiest one. Fortunately, I hadn&amp;rsquo;t had essential data there, but it showed that &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/&#34;&gt;it&amp;rsquo;s worth having a backup&lt;/a&gt; of your most important files. You have it, right?&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://superuser.com/questions/349633/boot-from-usb-using-grub&#34;&gt;Boot from USB using GRUB – SuperUser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gnu.org/software/grub/manual/grub/html_node/Chain_002dloading.html&#34;&gt;Chain-loading – GRUB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://unetbootin.github.io&#34;&gt;Unetbootin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>The form may limit you</title>
      <link>https://szymonkrajewski.pl/form-may-limit-you/</link>
      <pubDate>Fri, 20 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/form-may-limit-you/</guid>
      <description>&lt;p&gt;Sometimes I feel a surge of creativity. It&amp;rsquo;s a shame not to use this state to create something remarkable. However, once I sit down to my desk trying to write some words or code, I realize that I should do this in a particular way. Why? Because it&amp;rsquo;s how I use to do these things. And you know what? The form may &lt;em&gt;limit you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If I want to write about something, I usually don&amp;rsquo;t care about images, text decorations, even a title. &lt;em&gt;The content is valuable&lt;/em&gt;, whereas the rest is supportive. But I took a form, that needs the title, sometimes images and one or two paragraphs of introduction. I have to select the appropriate category and tags. I analyze how the text fits the rest of the topics. Moreover, I decided to write in English, whereas it would be much easier for me to write in Polish.&lt;/p&gt;
&lt;p&gt;I take a lot of notes using &lt;a href=&#34;https://szymonkrajewski.pl/taking-notes-in-plaintext/&#34;&gt;old-school plaintext&lt;/a&gt;. To be able to find them sometime in the future, I have to tag them and link to other entries. Sometimes it&amp;rsquo;s too much because the only thing I need to is &lt;em&gt;to write something down&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I summary almost every book I read. I don&amp;rsquo;t reach for the next one until I write down conclusions from the one I currently read.&lt;/p&gt;
&lt;p&gt;The form is also &lt;strong&gt;behaviour&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes I need to keep something online, but I don&amp;rsquo;t use public cloud services. Because of that, I have to put extra effort to, e.g. to keep my files in sync between my devices or to back up my most important data&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;For developers: imagine, that client requests a small feature, probably one-off, but you need to write a few extra classes to fit it into your solid, object-oriented code.&lt;/p&gt;
&lt;p&gt;This list may have no end. The form mentioned above – a set of rules, choices and good practices – is sometimes restrictive and kills the creative process. But we need to establish it. Otherwise, we&amp;rsquo;d drown in the ocean of inconsistent work surrounded by loosely floated pieces of information. &lt;strong&gt;The form gives cohesion and personal character to your work&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The form may limit you. When it happens, &lt;strong&gt;change the form&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@markusspiske?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Markus Spiske&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/limit?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;The less free time I have, the more I appreciate the cloud services without worry about maintenance. It&amp;rsquo;s still far for me to be fully convinced.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Don&#39;t read later. Read it now.</title>
      <link>https://szymonkrajewski.pl/dont-read-later/</link>
      <pubDate>Thu, 05 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/dont-read-later/</guid>
      <description>&lt;p&gt;I would like to confess a small lie that I told myself each time when I found something interesting on the Internet, but I didn&amp;rsquo;t have enough time to read it. It was &lt;em&gt;Read later&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For sure, reading is my primary way to acquire new knowledge. I read a lot. I read books and blogs articles, observe people on Twitter, receive newsletters and, browse &lt;em&gt;Hacker News&lt;/em&gt;. Each time I find something I consider that is interesting for me and I want to know more about it. Sometimes it&amp;rsquo;s a book recommendation, fresh news, tutorial or a new app announcement.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t want to miss that stuff.&lt;/p&gt;
&lt;h2 id=&#34;tools-to-the-rescue&#34;&gt;Tools to the rescue?&lt;/h2&gt;
&lt;p&gt;Since I remember, I took advantage of different tools to store interesting content. Although the idea was right, I realized that something was broken in my workflow. Over the years, I collected &lt;em&gt;thousands of things&lt;/em&gt; – articles, videos, whole blogs and websites, tools, and services I wished to got to know because I thought they would be useful in the future.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t have a solid habit of reading collected stuff. I read one article and add five new. Every time I started processing my list, I discovered that most of the items got out of date since they were here for more than half year or even more.&lt;/p&gt;
&lt;p&gt;Even if I performed a cleanup, the story had come full circle.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/dont-read-later/058_pocket_600_article_selected.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/dont-read-later/058_pocket_600_article_selected_hu_6c281bf334aaea4f.png 360w, https://szymonkrajewski.pl/dont-read-later/058_pocket_600_article_selected_hu_aa5fa86be53e2f75.png 640w, https://szymonkrajewski.pl/dont-read-later/058_pocket_600_article_selected_hu_bb9b1cc9c4429690.png 957w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;957&#34;
          height=&#34;345&#34;
           alt=&#34;Image displayed read later app&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      I removed 600 articles since they no longer interest me.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;this-is-no-problem-with-tools&#34;&gt;This is no problem with tools&lt;/h2&gt;
&lt;p&gt;I thought I&amp;rsquo;d be able to solve the problem with the overwhelming amount of content only by using a proper tool. But to be honest, no tool can help you with a massive pile of articles if you don&amp;rsquo;t process the collected stuff on a regular basis. It&amp;rsquo;s no problem with tools.&lt;/p&gt;
&lt;p&gt;I started thinking about why I store tons of links, but I don&amp;rsquo;t need to read them. It turned out that I needed to remember that &lt;em&gt;something like that&lt;/em&gt; exists. Moreover, pushing it to &lt;em&gt;Read Later&lt;/em&gt; heap gave me the conviction that I didn&amp;rsquo;t lose what I found, so I pushed almost everything there. Ultimately, I created a bad habit.&lt;/p&gt;
&lt;p&gt;So in short, I put most of the stuff to the big box called &amp;ldquo;&lt;em&gt;I wish to get to know more about it, but I wait for the time I&amp;rsquo;ll need it&lt;/em&gt;&amp;rdquo;. I didn&amp;rsquo;t know if I&amp;rsquo;d remember that I had them. I wasn&amp;rsquo;t even sure if I&amp;rsquo;d be able to find them. But &lt;em&gt;Fear of Missing Out&lt;/em&gt; was stronger.&lt;/p&gt;
&lt;h2 id=&#34;dont-read-everything&#34;&gt;Don&amp;rsquo;t read everything&lt;/h2&gt;
&lt;p&gt;The crucial thing was to understand that &lt;strong&gt;I can&amp;rsquo;t read everything&lt;/strong&gt;. It&amp;rsquo;s impossible. So if it&amp;rsquo;s impossible, why I even tried?&lt;/p&gt;
&lt;p&gt;Lots of blogs, news aggregators, social media platforms and newsletters suggest you what you can read, watch or listen. &lt;strong&gt;They produce a continuous stream of content&lt;/strong&gt;. In most cases, the content is neither urgent nor important.&lt;/p&gt;
&lt;p&gt;The only reasonable way to deal with streams is to process fresh content without postponing only when you have enough time. If you&amp;rsquo;ll find something interesting, check it, take note if needed, and go on. &lt;strong&gt;But If you don&amp;rsquo;t have time, you lose nothing.&lt;/strong&gt; It&amp;rsquo;s more than sure that you&amp;rsquo;ll find another exciting content next time you&amp;rsquo;ll process the &lt;em&gt;stream&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Generally speaking, &lt;strong&gt;don&amp;rsquo;t postpone processing information from streams&lt;/strong&gt; unless you&amp;rsquo;re sure that topic is your area of interests. The rule of thumb is: &lt;em&gt;check it now or never&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;be-aware-of-what-you-read-and-why&#34;&gt;Be aware of what you read and why&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m no opponent of &lt;em&gt;Read Later&lt;/em&gt; list. I still use it but in a more conscious way. What does it mean?&lt;/p&gt;
&lt;p&gt;When I do research, I sometimes push some articles and materials to my &lt;em&gt;Read Later&lt;/em&gt; list to come back to them in a relatively short time. I know that I seek this information, so it&amp;rsquo;s more likely that I&amp;rsquo;ll process the article. When somebody recommends me something, I usually push it to the &lt;em&gt;Read Later&lt;/em&gt; list as well.&lt;/p&gt;
&lt;p&gt;When it comes to casual Internet browsing, I have a simple strategy. I try to read stuff without postponing unless they&amp;rsquo;re related to one of my active research projects.&lt;/p&gt;
&lt;p&gt;What if I find &lt;em&gt;something interesting&lt;/em&gt;, but I don&amp;rsquo;t have time to read it? In most cases – nothing. &lt;strong&gt;I just let it go&lt;/strong&gt;. If you have a similar problem, take into account that if you hadn&amp;rsquo;t visited the stream, you wouldn&amp;rsquo;t have seen this &lt;em&gt;interesting stuff&lt;/em&gt; and for sure nothing wrong would happen. If you need this information, you&amp;rsquo;ll find this one or even better source.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Read Later&lt;/em&gt; list should be your partner, not the enemy. Don&amp;rsquo;t feed it by random things you found on the Internet.&lt;/p&gt;
&lt;p&gt;Of course, some people don&amp;rsquo;t care about the number of elements things on the &lt;em&gt;Read Later&lt;/em&gt; list. 500 or 5000 – who cares? If you&amp;rsquo;re one of this person – it&amp;rsquo;s OK. Don&amp;rsquo;t solve a problem you don&amp;rsquo;t have.&lt;/p&gt;
&lt;p&gt;I recently filtered 600 elements from my read later list. They were things that no longer interested me. Subsequently, I cleared my list at all.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/dont-read-later/058_pocket_empty.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/dont-read-later/058_pocket_empty_hu_56838de9fcf5b4da.png 360w, https://szymonkrajewski.pl/dont-read-later/058_pocket_empty_hu_6b449657d00ad0ef.png 612w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;612&#34;
          height=&#34;301&#34;
           alt=&#34;Screen displayed empty list in Pocket app&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Empty Pocket. First time after a few years.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Right now, I maintain a small &lt;a href=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/&#34;&gt;running note&lt;/a&gt; with useful links and I take advantage of Safari &lt;em&gt;Read Later&lt;/em&gt; feature to keep some information for a moment.&lt;/p&gt;
&lt;p&gt;Next time, before you&amp;rsquo;ll add something to your &lt;em&gt;Read Later&lt;/em&gt; list, Pocket, Instapaper, Bookmarks or another tool, ask yourself: &lt;strong&gt;Is it really for me?&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@timmossholder?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Tim Mossholder&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Find and insert reference to note on macOS using Alfred</title>
      <link>https://szymonkrajewski.pl/find-reference-to-note-using-alfred/</link>
      <pubDate>Wed, 12 Feb 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/find-reference-to-note-using-alfred/</guid>
      <description>&lt;p&gt;Create connections between notes is the essential part of the whole note-taking process, and for sure, it leads to discovering outstanding ideas in the future. However, it requires extra effort during writing: you have to find a proper note, copy the reference and paste it to the original memo. Some software has a built-in feature to handle it, but when it comes to plaintext notes, the implementation depends on the user.&lt;/p&gt;
&lt;p&gt;In this article, I’ll show you how to create Alfred workflow that automates the process of finding and inserting a reference to other notes.&lt;/p&gt;
&lt;p&gt;First of all, you need to have macOS and app called Alfred. I mentioned Alfred before in &lt;a href=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/&#34;&gt;my article about running notes&lt;/a&gt;. It’s one of the most useful application on my computer.&lt;/p&gt;
&lt;h2 id=&#34;why-its-worth-to-connect-notes&#34;&gt;Why it’s worth to connect notes?&lt;/h2&gt;
&lt;p&gt;Notes are rarely independent. Like neurons in our brains, they are more powerful if they contain connections to other notes. We can put the reference to different note when we feel, that the particular piece of information is related to another one. For sure, sooner or later, you’ll need a way to create connections between notes.&lt;/p&gt;
&lt;h2 id=&#34;how-to-create-a-reference&#34;&gt;How to create a reference?&lt;/h2&gt;
&lt;p&gt;Notes should be long-lasting – that’s &lt;a href=&#34;https://szymonkrajewski.pl/taking-notes-in-plaintext/&#34;&gt;why I use plaintext to take them&lt;/a&gt;. The most common practice, in this case, is to use a &lt;em&gt;wiki-style syntax&lt;/em&gt; to link from one note to another one. Most software, like &lt;em&gt;nvALT&lt;/em&gt;, &lt;em&gt;FSNotes&lt;/em&gt; or &lt;em&gt;The Archive&lt;/em&gt; can parse this tag to create a link to the referenced note. Regardless of how old-school this method is, it’s resistant to time and doesn’t lock you in the specific service or note-taking software.&lt;/p&gt;
&lt;p&gt;The reference itself is related to your naming convention. I put a unique &lt;em&gt;DateTime&lt;/em&gt; marker as an ID to the name of my note. Thanks to it, I can create a reference using either full name or only ID – whatever is more convenient at the moment.&lt;/p&gt;
&lt;p&gt;So to create a reference from one note to another one, I put the id of the referred memo in &lt;strong&gt;double-square brackets&lt;/strong&gt; somewhere in the text of the first one. Here’s an example:&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_example_note_with_reference.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_example_note_with_reference_hu_7b2893f68d96127a.png 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_example_note_with_reference_hu_1f5cc58047af768.png 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_example_note_with_reference_hu_a749a41326e8ae5.png 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_example_note_with_reference_hu_fe103e0ae255115.png 1042w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1042&#34;
          height=&#34;586&#34;
           alt=&#34;Example note with reference to another note visible in FSNotes app.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Example note with reference to another note visible in FSNotes app.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The biggest problem is to find the ID of the note I want to refer to. Without a dedicated feature, you need to change the context of your work, find the proper note, copy the ID and finally insert it into the original note. Let’s Alfred does this work.&lt;/p&gt;
&lt;h2 id=&#34;alfred-workflow-to-find-and-insert-reference-to-note&#34;&gt;Alfred workflow to find and insert reference to note&lt;/h2&gt;
&lt;p&gt;I created an Alfred workflow to simulate autocompletion of the note reference. You can set your directory for notes, so Alfred will limit the results. Whole workflow looks like below.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_785b86b862beb7ad.webp 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_e652fcda01307883.webp 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_41e174cbc33c67ac.webp 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_6696922c7e3d9918.webp 1200w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_fd8ef3cb8980f9a7.jpg 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_6a3ccebe3f3b22e4.jpg 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_ae17405f49e70a1e.jpg 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_note_reference_search_optimized_hu_ab14fac41f02b211.jpg 1200w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1200&#34;
          height=&#34;480&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The view of whole Alfred workflow for Note Reference Search.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The workflow is triggered by a snippet. I use the snippet instead of keyword or hotkey to be able to pass configuration to &lt;a href=&#34;https://www.alfredapp.com/help/workflows/inputs/file-filter/&#34;&gt;&lt;em&gt;File Filter&lt;/em&gt;&lt;/a&gt; and to avoid remembering the next hotkey. Moreover, during working with notes, it’s more natural to write directly in the editor, so the snippet looks like the best option here.&lt;/p&gt;
&lt;p&gt;To activate the workflow, type following keys sequence: &lt;code&gt;\[[&lt;/code&gt;. It’s easy to memorize because it looks like the beginning of wiki-style link preceded by a slash, which usually means command in some other software.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty_hu_a7498f76f250e8bf.png 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty_hu_159df99be4c11509.png 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty_hu_c7086896da6c06ab.png 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty_hu_86d7608cc897bb2d.png 1200w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_empty_hu_1ba2637978b2cc3.png 1292w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1292&#34;
          height=&#34;322&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Alfred input window with prepared File Filter to look for notes.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The workflow looks for matched &lt;em&gt;markdown files&lt;/em&gt; in the pre-configured directory. When you find the note you want to refer to, press Enter to replace snippet with the &lt;em&gt;wiki-style&lt;/em&gt; reference. If you’re going to insert the full name of note instead, hold cmd and press Enter.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries_hu_ddfb2cd9feada106.png 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries_hu_4bc5cfdeb4e769f5.png 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries_hu_169b915a48ce76d1.png 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries_hu_b2697d30fe5bf410.png 1200w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_with_entries_hu_e14af85faff7021a.png 1292w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1292&#34;
          height=&#34;414&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Alfred input window with filtered results.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Before you start, make sure you set the path to your notes directory in environment variables.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_environment_variable.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_environment_variable_hu_da898bfff74a77f4.png 360w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_environment_variable_hu_dc17c182b76bdb8d.png 640w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_environment_variable_hu_2a8c5b57233ab09b.png 960w, https://szymonkrajewski.pl/find-reference-to-note-using-alfred/060_alfred_workflow_environment_variable_hu_9d64ac4fc9b5c1cf.png 1000w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1000&#34;
          height=&#34;222&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Workflow Configuration in Alfred where you can set environment variables.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Remember to set the notes_directory variable to limit the scope of searching.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I hope you’ll find value in this article. There’s no first time when I take advantage of Alfred to make my work easier. Properly configured and customized, it could be a good partner in your work.&lt;/p&gt;
&lt;p&gt;The important takeaway is: you don’t need to use an all-in-one, huge application with tons of features that lock you in its ecosystem. If you can do something manually, you can also automate it on your own! This article responds partially to the &lt;a href=&#34;https://zettelkasten.de/posts/fake-link-autocompletion-to-any-app/&#34;&gt;post written by Christian on Zettelkasten.de&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The workflow is available in my &lt;a href=&#34;https://github.com/skrajewski/alfred-workflows&#34;&gt;GitHub’s repository&lt;/a&gt;. You can download it &lt;a href=&#34;https://github.com/skrajewski/alfred-workflows/raw/master/Note%20reference%20search.alfredworkflow&#34;&gt;from here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@lunarts?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Volodymyr Hryshchenko&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Think big, start small</title>
      <link>https://szymonkrajewski.pl/think-big-start-small/</link>
      <pubDate>Wed, 22 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/think-big-start-small/</guid>
      <description>&lt;p&gt;When I think about a new idea or my next goal in life, I usually think big. The goal has to be ambitious and attractive to me, otherwise, it’s not worth putting effort into it. However, big goals also overwhelm me, because I don’t know where I should start. So I prepare. And I think. And I prepare. And I think. And this loop goes on.&lt;/p&gt;
&lt;p&gt;It changed. The endless loop of thinking about the best approach to achieve the goal or to do whatever you want means that you probably try to start with something big. To break it, just do the opposite – &lt;strong&gt;think big, but start small&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-big-problem&#34;&gt;The &lt;em&gt;big&lt;/em&gt; problem&lt;/h2&gt;
&lt;p&gt;The biggest problem with every idea is that we inaccurately think of the final effect as the result of a single action. We’re in the current position, so we need to do &lt;strong&gt;something&lt;/strong&gt; and in the result, we achieve our desired goal.&lt;/p&gt;
&lt;figure class=&#34;figure left invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_ffc23f1f0fbfc06a.png 360w, https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_ddd020cb432068fb.png 640w, https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_ddea892eef931168.png 960w, https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_ea6901ab6f12e5e6.png 1200w, https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_db4578698714246d.png 1600w, https://szymonkrajewski.pl/think-big-start-small/055_single_action_chart_hu_200eb8052592c6af.png 1640w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1640&#34;
          height=&#34;481&#34;
           alt=&#34;diagram shows the flow from currents state through single, but unknown action to desired result&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Most of the time we think that desired outcome is the result of one single action, but we have problem to define that one.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The goal is usually &lt;em&gt;big&lt;/em&gt;, so we change plans and refine things, looking for perfection when we don’t even know what it means and how it looks. Doing something sounds like a feasible activity, but it’s a delusion. In effect, instead of doing even small steps, we do nothing at all. We just think and prepare.&lt;/p&gt;
&lt;h2 id=&#34;starting-big-leads-to-procrastination&#34;&gt;Starting &lt;em&gt;big&lt;/em&gt; leads to procrastination&lt;/h2&gt;
&lt;p&gt;Thinking and preparing also refer to procrastination. Sometimes we excuse ourselves saying that we need to have a plan or schedule – to be on track. Or that we need to revalidate the idea a third time because something should look different. Or because it’s not a good time. Or because of reason. It’s all about avoiding failure.&lt;/p&gt;
&lt;h3 id=&#34;planning-as-an-excuse&#34;&gt;Planning as an excuse&lt;/h3&gt;
&lt;p&gt;By having a good plan, you can mitigate the risk of failure. By improving your plan, you can reduce the risk even more. By constantly working on your plan, you can eliminate the risk of failure – If you do nothing, you don’t fail.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A good plan today is better than a perfect plan tomorrow. Don’t wait for an inspired ending to come to mind. Work your way to the ending and see what comes up.&lt;/p&gt;
&lt;p&gt;Andy Weir&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I don’t mean you shouldn’t make plans. Having a plan it’s OK as long as it helps you move forward. But it doesn’t perform the move – it’s your responsibility.&lt;/p&gt;
&lt;h2 id=&#34;start-with-something-small&#34;&gt;Start with something small&lt;/h2&gt;
&lt;p&gt;Being a famous blogger or Instagram influencer, building profitable project, developing recognizable open-source library, or collecting 10000 dollars for local charity – there are examples of &lt;em&gt;thinking big&lt;/em&gt;. However, you can’t achieve any of these goals at night. Things last. What you should do is to start with a small action, then observe the results and ultimately improve it in time – continuously.&lt;/p&gt;
&lt;figure class=&#34;figure left invertible&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_a1e81d235aca3884.png 360w, https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_e942ef0cd7b19c76.png 640w, https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_2bd5e3da1f9f9613.png 960w, https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_ae9fc02d12eff149.png 1200w, https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_a919c1711b4cc0f2.png 1600w, https://szymonkrajewski.pl/think-big-start-small/055_improvement_chart_hu_750afc3679f28599.png 1740w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1740&#34;
          height=&#34;672&#34;
           alt=&#34;diagram shows the flow from current state through improvement to result but with additional loop where improvement constantly change result&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      It’s much easier to think of our actions as improvements which has influence on result. Small actions are easy to define, execute and validate.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The idea should be ambitious and developmental, however, our steps towards the goal should be small enough to fulfill all of our needs at this moment and give us more opportunities for further development. If something went wrong, consequences will be smaller, so we can go back and revalidate our actions or update our plans according to results. Evolution, instead of revolution.&lt;/p&gt;
&lt;h2 id=&#34;consequences-of-starting-small&#34;&gt;Consequences of starting small&lt;/h2&gt;
&lt;p&gt;Sooner or later: &lt;strong&gt;you’ll get your job done&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Things are not perfect and almost everywhere we can see the field for potential improvement. However, by starting small we create something that is usable. We can test the idea. We can create a habit. We can discard the idea if it’s no longer relevant for us. We can get feedback. We can change decision. Ultimately, by starting small you can reach your goal without even noticing that.&lt;/p&gt;
&lt;p&gt;Maybe it’s more appropriate to say that you should &lt;strong&gt;act small&lt;/strong&gt;?&lt;/p&gt;
&lt;h2 id=&#34;start-small-is-not-about-laziness&#34;&gt;Start small is not about laziness&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Think big&lt;/em&gt; means that our idea should be &lt;strong&gt;big&lt;/strong&gt;. This is the goal – something we would like to achieve in the future. &lt;em&gt;Start small&lt;/em&gt; means that we shouldn’t do everything we think we would need at this moment. Just the small action that brings us &lt;strong&gt;closer&lt;/strong&gt; to the goal. Because we don’t know what we should do next.&lt;/p&gt;
&lt;p&gt;The space between current state and the goal is unknown. Do first step and look if it’s in a right direction. Then do next one. And another one. This is actually a journey and, who knows, maybe you’ll discover something even more important?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Thanks&lt;/strong&gt; to Krzysztof Szulka and Dawid Kuśmierek for reading draft of this article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@tateisimikito?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Jukan Tateisi&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/start?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Quick access to any menu item in macOS apps</title>
      <link>https://szymonkrajewski.pl/macos-help-menu-shortcut/</link>
      <pubDate>Wed, 08 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/macos-help-menu-shortcut/</guid>
      <description>&lt;p&gt;MacOS has a special shortcut I wish I have known them at the beginning of my journey with this system. The functionality is hidden in the menu in almost every app when you click on &lt;em&gt;Help&lt;/em&gt;. I talk about this little search box.&lt;/p&gt;
&lt;p&gt;This small feature may immensely boost your work, especially if you&amp;rsquo;re the keyboard-centric type of person. In short, it allows you to find and run almost every action in the active app. Press &lt;kbd&gt;cmd&lt;/kbd&gt;+&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;/&lt;/kbd&gt; and start typing.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_d1260961fd633b3b.webp 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_43eddcc3ce7f0c2e.webp 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_86a36ebf68c85ae5.webp 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_6f6ca5e1fa9d75f6.webp 1200w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_72165e0246f18c9f.jpg 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_9bea704750bdf840.jpg 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_55bedda80a63fa11.jpg 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_window_keyboard_shortcuts_help_hu_2413e895a4506b5.jpg 1200w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1200&#34;
          height=&#34;1043&#34;
           alt=&#34;The Keyboard Settings Window on macOS with the enabled shortcut on the list.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Keyboard Settings Window. Make sure you have &amp;ldquo;Show Help menu&amp;rdquo; action enabled and assigned to the proper shortcut.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;meet-the-the-command-palette&#34;&gt;Meet the &lt;em&gt;The Command Palette&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;When I&amp;rsquo;ve seen the &lt;em&gt;Command Palette&lt;/em&gt; for the first time in &lt;em&gt;Sublime Text&lt;/em&gt; editor, I consider it as the most supportive feature every app should have. Right now, most of the code editors have this feature built-in. But other apps don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;In apps like &lt;em&gt;Gimp&lt;/em&gt;, &lt;em&gt;Word&lt;/em&gt;, &lt;em&gt;Excel&lt;/em&gt;, &lt;em&gt;Photoshop&lt;/em&gt; where you have plenty of various actions, it&amp;rsquo;s hard to find a specific menu item, unless you spent some time with the app. It often happens to me – I know that an app has a feature I want to use, but I don&amp;rsquo;t know where it exactly is and which shortcut it has. I used to went through each menu in the app looking for the option. It was a waste of time.&lt;/p&gt;
&lt;p&gt;Recently, I discovered that the &lt;em&gt;macOS help menu&lt;/em&gt; has an input field that allows me to filter menu items simply by typing. &lt;strong&gt;You can use options directly from here&lt;/strong&gt;. Moreover, it shows me &lt;strong&gt;where I can find the selected option&lt;/strong&gt; in the menu hierarchy. Some of them have keyboard shortcuts assigned – it&amp;rsquo;s also an opportunity to get to know them. So, if you don&amp;rsquo;t know the shortcut or if the option doesn&amp;rsquo;t have the shortcut assigned, searching in &lt;em&gt;Help&lt;/em&gt; is another fast way to invoke the desired option.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_e04044ab36c242e3.webp 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_5f747b3bc65e3d68.webp 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_34ff74a3a9f2b4a7.webp 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_15fab5e6f38531fc.webp 1200w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_319c91dceaae3035.jpg 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_aa6cee8a585ab29b.jpg 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_d639df2f4a003857.jpg 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_search_menu_help_excel_example_hu_67d6d65d3be8a8d8.jpg 1200w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1200&#34;
          height=&#34;762&#34;
           alt=&#34;Looking for &amp;#34;New Comment&amp;#34; menu item in Microsoft Excel application using macOS help menu.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      A few keystrokes are enough to find desired action. You can invoke it directly from the filtered list. Moreover, macOS shows where you can find selected menu item.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Remember, by using &lt;kbd&gt;cmd&lt;/kbd&gt;+&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;/&lt;/kbd&gt; you&amp;rsquo;ll get focus on Search Box in macOS help menu, where you can find any menu item available the app.&lt;/p&gt;
&lt;h2 id=&#34;you-may-also-use-alfred&#34;&gt;You may also use Alfred&lt;/h2&gt;
&lt;p&gt;If you use Alfred, there is a dedicated workflow called &lt;a href=&#34;https://www.alfredforum.com/topic/10231-faster-menu-search/&#34;&gt;Menu Search&lt;/a&gt; created by &lt;a href=&#34;https://github.com/BenziAhamed&#34;&gt;this guy&lt;/a&gt;. Thanks to this, you can search for menu items from the active app directly in the Alfred window. It&amp;rsquo;s useful, if the app overrides the native macOS shortcut.&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_626c3dda9556af1e.webp 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_c69c0e6a474024ad.webp 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_5af783717cd06f50.webp 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_66c66410bdbd5b0c.webp 1200w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_a81ed1c3485cb9a8.jpg 360w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_14571b42a5788c87.jpg 640w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_b10417454eb07a6.jpg 960w, https://szymonkrajewski.pl/macos-help-menu-shortcut/057_menu_search_alfred_gimp_example_hu_d48b8ff87bb07242.jpg 1200w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1200&#34;
          height=&#34;736&#34;
           alt=&#34;Alfred workflow with filtered menu options from GIMP application that works similarly to macOS help menu.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      This Alfred workflow works similarly to the native Help menu.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I use Alfred as the replacement for Apple&amp;rsquo;s Spotlight and I found it much useful because of custom workflows, snippets, clipboard history and more. I described one of my workflow &lt;a href=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/&#34;&gt;in the article about running notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. Thanks for reading. I hope you&amp;rsquo;ll find value in this article!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@cadop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Mathew Schwartz&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>My 2019 Annual Review</title>
      <link>https://szymonkrajewski.pl/2019-annual-review/</link>
      <pubDate>Tue, 31 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/2019-annual-review/</guid>
      <description>&lt;p&gt;The end of the year is presumably a good time for reflection and review. I don&amp;rsquo;t have any strict structure for annual reviews. I did them many times before, but I haven&amp;rsquo;t found any process that I stick to. I used to set goals and check if I accomplished them. However, this year I tried something different.&lt;/p&gt;
&lt;h2 id=&#34;past-year-review&#34;&gt;Past Year Review&lt;/h2&gt;
&lt;p&gt;Although I set goals for 2019, I don&amp;rsquo;t think they&amp;rsquo;re something that drove me throughout the year. Moreover, I didn&amp;rsquo;t think too much about them. For 2020, I decided to conduct a &lt;a href=&#34;https://tim.blog/2018/12/28/past-year-review/&#34;&gt;Past Year Review&lt;/a&gt; by Tim Ferris.&lt;/p&gt;
&lt;p&gt;The whole &lt;em&gt;Past Year Review&lt;/em&gt; process fits in these 5 steps (&lt;em&gt;from Tim&amp;rsquo;s blog&lt;/em&gt;):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grab a notepad and create two columns: POSITIVE and NEGATIVE.&lt;/li&gt;
&lt;li&gt;Go through your calendar from the last year, looking at every week.&lt;/li&gt;
&lt;li&gt;For each week, jot down on the pad any people or activities or commitments that triggered peak positive or negative emotions for that month. Put them in their respective columns.&lt;/li&gt;
&lt;li&gt;Once you’ve gone through the past year, look at your notepad list and ask, “What 20% of each column produced the most reliable or powerful peaks?”&lt;/li&gt;
&lt;li&gt;Based on the answers, take your “positive” leaders and schedule more of them in the new year. Get them on the calendar now! Book things with friends and prepay for activities/events/commitments that you know work. It’s not real until it’s in the calendar. That’s step one. Step two is to take your “negative” leaders, put “NOT-TO-DO LIST” at the top, and put them somewhere you can see them each morning for the first few weeks of 2019 (2020). These are the people and things you &lt;em&gt;know&lt;/em&gt; make you miserable, so don’t put them on your calendar out of obligation, guilt, FOMO, or other nonsense.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I changed this process making it more suitable for me. Instead of using notepad, &lt;a href=&#34;https://szymonkrajewski.pl/taking-notes-in-plaintext/&#34;&gt;I wrote down everything in a text file&lt;/a&gt;. Instead of working with the calendar, I went through my journal.&lt;/p&gt;
&lt;p&gt;At the end of this step, I create two lists. Let&amp;rsquo;s call them &lt;strong&gt;positive&lt;/strong&gt; and &lt;strong&gt;negative&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;analyse-positive-and-negative-lists&#34;&gt;Analyse &lt;em&gt;positive&lt;/em&gt; and &lt;em&gt;negative&lt;/em&gt; lists&lt;/h2&gt;
&lt;p&gt;I had a lot of points on my &lt;em&gt;positive&lt;/em&gt; list, but most of them repeated over time. They were related to other people, trips, sport, learning, creating, reading, or doing something for others.&lt;/p&gt;
&lt;p&gt;It was hard for me to fill the &lt;em&gt;negative&lt;/em&gt; list, so I ended up having a few specific points. In general, I pointed out things like implicit expectations, arguments, misunderstandings, excuses, as well as traffic jams, eating junk food, and checking blog stats or social media.&lt;/p&gt;
&lt;p&gt;What is interesting, all these points are not related to income, profits or any other material benefits. They are about positive and negative emotions and feelings.&lt;/p&gt;
&lt;p&gt;The next step I&amp;rsquo;ve done was transforming the lists to the form of &lt;strong&gt;do more&lt;/strong&gt; and &lt;strong&gt;try to eliminate&lt;/strong&gt;. Here they&amp;rsquo;re:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do more&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take care of important relationships in your life, spend more time with friends and put real value in their life.&lt;/li&gt;
&lt;li&gt;Write more articles and essays on the blog and be worried less about the form (like in this article).&lt;/li&gt;
&lt;li&gt;Do good things and support other people.&lt;/li&gt;
&lt;li&gt;Learn new things, read books and develop new skills.&lt;/li&gt;
&lt;li&gt;Take care of yourself – sleep more, exercise, and check your health regularly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Try to eliminate&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Excuses.&lt;/li&gt;
&lt;li&gt;Pretending that everything is OK instead of talking to solve problems.&lt;/li&gt;
&lt;li&gt;Checking social media and blog statistics after a new publication.&lt;/li&gt;
&lt;li&gt;Browsing the Internet aimlessly.&lt;/li&gt;
&lt;li&gt;Wasting time in traffic jams.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;I should schedule some activities or events, but I don&amp;rsquo;t think I have to do it right now. I can&amp;rsquo;t plan everything I want to do at this moment. Instead, I&amp;rsquo;m going to collect some ideas related to the &lt;em&gt;do more&lt;/em&gt; list that I can use in the next months. I want to also be more aware of my &lt;em&gt;try to eliminate&lt;/em&gt; list, so I put them in a visible place.&lt;/p&gt;
&lt;h2 id=&#34;what-about-the-goals&#34;&gt;What about the goals?&lt;/h2&gt;
&lt;p&gt;Before 2017, I was defining goals somehow.&lt;/p&gt;
&lt;p&gt;For 2017 and 2018, I defined goals according to the S.M.A.R.T. rule.&lt;/p&gt;
&lt;p&gt;In the meantime, In 2018, I defined my values.&lt;/p&gt;
&lt;p&gt;For 2019, I defined goals based on my values and my &amp;ldquo;whys&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In the meantime, In 2019, I defined my own &lt;em&gt;mission&lt;/em&gt; and my &lt;em&gt;vision&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For 2020, I try &amp;ldquo;&lt;em&gt;Past Year Review&lt;/em&gt;&amp;rdquo;. At this point, I define no annual goals. Maybe I would set them when I&amp;rsquo;ve needed them.&lt;/p&gt;
&lt;p&gt;Goals appear and disappear, become important and less important – depends on the situation. It doesn&amp;rsquo;t mean I don&amp;rsquo;t have personal goals. I have, but they are no results of annual review but rather my direction through life.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;In 2019, I accomplished most of my goals. On the whole, lots of good things happened to me this year. I had many opportunities to learn, challenge and to broaden my mind. I appreciate that.&lt;/p&gt;
&lt;p&gt;On the other hand, I know, that I&amp;rsquo;ve could do many things better. There&amp;rsquo;re always fields to improvement. The point is to see them and be aware of them.&lt;/p&gt;
&lt;p&gt;Thank you for reading. I wish you a Happy New Year!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@nate_dumlao?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Nathan Dumlao&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Running notes and information capturing on macOS using Alfred</title>
      <link>https://szymonkrajewski.pl/running-notes-and-information-capturing/</link>
      <pubDate>Wed, 18 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/running-notes-and-information-capturing/</guid>
      <description>&lt;p&gt;List of books. Inspiring food for thoughts. Things I&amp;rsquo;ve done today. Big ideas that can help the community I live with. That&amp;rsquo;s are examples of &lt;em&gt;running notes&lt;/em&gt; – useful notes, that I update all the time.&lt;/p&gt;
&lt;p&gt;This is one type of notes I use in my &lt;a href=&#34;https://szymonkrajewski.pl/taking-notes-in-plaintext/&#34;&gt;plaintext personal note-taking system&lt;/a&gt;. In this article, I&amp;rsquo;m going to show you, how to make use of them in an effective way – using tool called &lt;em&gt;Alfred&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I found the name &amp;ldquo;&lt;em&gt;running note&lt;/em&gt;&amp;rdquo; in the prof. Aleh Cherp&amp;rsquo;s article &lt;a href=&#34;https://macademic.org/2011/03/31/taking-notes/&#34;&gt;&amp;ldquo;Notes: what&amp;rsquo;s in the name?&amp;rdquo;&lt;/a&gt; which inspired me a lot. I&amp;rsquo;ll use that name since there is no reason to invent a different term for the same definition.&lt;/p&gt;
&lt;h2 id=&#34;why-you-may-need-running-notes&#34;&gt;Why you may need running notes?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s possible, that you already have some kind of &lt;em&gt;running note&lt;/em&gt;. If you have a list of books to read, ideas for gifts for your wife (or husband), bookmarks in your web browser, to-do list – all this information could be the note.&lt;/p&gt;
&lt;p&gt;Sometimes I want to store a piece of information that I found or read but it&amp;rsquo;s too small to be a single note. I often do this during my work or focus time, so it shouldn&amp;rsquo;t interrupt my flow.&lt;/p&gt;
&lt;p&gt;By keeping various lists of things that matter for you (you store them, so they matter), you may come back to the list and find something useful whenever you need it.&lt;/p&gt;
&lt;h2 id=&#34;what-is-running-note&#34;&gt;What is &lt;em&gt;running note&lt;/em&gt;?&lt;/h2&gt;
&lt;p&gt;Running notes are files in your note-taking system that you update quite often. You should be able to find and edit it without breaking your workflow.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. As you can see, the idea is super simple.&lt;/p&gt;
&lt;p&gt;Here are some examples of my &lt;em&gt;running notes&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;bkx&lt;/em&gt; – books I want to read.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;fftx&lt;/em&gt; – quotes from movies, books, articles.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;wtfx&lt;/em&gt; – things I wait for.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;idsx&lt;/em&gt; – ideas.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;urlx&lt;/em&gt; – list of my favorites sites, articles, blogs, etc.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;artx&lt;/em&gt; – ideas for blog posts.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;inbx&lt;/em&gt; – everything that is worth to remember but need further processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The names of these notes are &lt;strong&gt;short&lt;/strong&gt;. I use search a lot and I don&amp;rsquo;t want to type too much. For instance, I often double-check what is the right tire pressure for in my car while I&amp;rsquo;m standing next to the compressor. I have this information in one of my running notes.&lt;/p&gt;
&lt;p&gt;Also, the &lt;code&gt;x&lt;/code&gt; at the end of the name means, that there is a running note.&lt;/p&gt;
&lt;h3 id=&#34;structure-of-note&#34;&gt;Structure of note&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s good to define a small structure to make further automation easier. I store each entry as a list element. Almost every markdown editor displays a good-looking list.&lt;/p&gt;
&lt;p&gt;But sometimes it&amp;rsquo;s not enough. In some notes, I put a date before each entry to remember when I store the piece of information and how long it lives in the note. If you need more detailed information, (e.g. in the work-log file), you may use a timestamp, instead of a date.&lt;/p&gt;
&lt;p&gt;This is how my running notes look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;created: 2019-11-05 12:00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tags: running-note
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;# Ideas
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 2019-11-05 # Write about your approach to note-taking.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 2019-11-12 # Describe the idea of running notes.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 2019-11-12 # Describe the plaintext approach.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don&amp;rsquo;t worry about the header. I put it in all of my notes. I&amp;rsquo;ll touch this topic in another article.&lt;/p&gt;
&lt;h2 id=&#34;how-to-write-to-running-notes&#34;&gt;How to write to running notes?&lt;/h2&gt;
&lt;p&gt;When it comes to writing, the rule of thumb is: find the shortest path to write something in the note and avoid being disturbed. Automate as many things as possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you work in the terminal, make a script that echos your input to the proper file.&lt;/li&gt;
&lt;li&gt;Use the editor that allows you switching between files using the keyboard.&lt;/li&gt;
&lt;li&gt;Create a snippet or abbreviation to reduce the number of keystrokes.&lt;/li&gt;
&lt;li&gt;Use a tool like &lt;a href=&#34;https://www.alfredapp.com/&#34;&gt;Alfred&lt;/a&gt; or &lt;a href=&#34;https://www.hammerspoon.org/&#34;&gt;Hammerspoon&lt;/a&gt;, to create a workflow that streams your input into the proper file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although I use the terminal a lot, I don&amp;rsquo;t have special scripts for my running notes. Instead, I do everything I need using Alfred&amp;rsquo;s workflow and text editor.&lt;/p&gt;
&lt;h3 id=&#34;create-a-workflow-for-alfred&#34;&gt;Create a workflow for Alfred&lt;/h3&gt;
&lt;p&gt;If you don&amp;rsquo;t know Alfred, it&amp;rsquo;s something like &lt;em&gt;&lt;a href=&#34;https://support.apple.com/en-us/HT204014&#34;&gt;Spotlight&lt;/a&gt;&lt;/em&gt;, but with some extra features. One of my favorite Alfred&amp;rsquo;s features are &lt;em&gt;&lt;a href=&#34;https://www.alfredapp.com/workflows/&#34;&gt;Workflows&lt;/a&gt;&lt;/em&gt;. It allows you creating integrations between apps, files, scripts, and the operating system. Unfortunately, it requires Alfred&amp;rsquo;s Power-Pack which is paid.&lt;/p&gt;
&lt;p&gt;Hammerspoon looks like a free alternative for Alfred&amp;rsquo;s Workflows, but you should have a basic programming knowledge to write your automation.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a big fan of &lt;em&gt;&amp;quot;&lt;a href=&#34;https://szymonkrajewski.pl/think-big-start-small/&#34;&gt;think big, but start small&lt;/a&gt;&amp;quot;&lt;/em&gt; approach, so in my opinion, instead of figuring out how to create a highly-efficient, customizable, and flexible workflow, create one with some keywords and pass the argument as the input to the proper file. Simple enough.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example workflow:&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow_hu_746aea6e77e6464.png 360w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow_hu_1319f9f376f97c7.png 640w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow_hu_671528528d49f842.png 960w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow_hu_a159dbcf79db2081.png 1200w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_simple_alfred_workflow_hu_8f72c93c4d6f7b14.png 1344w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1344&#34;
          height=&#34;418&#34;
           alt=&#34;Simple alfred workflow to store text in file.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Simple Alfred workflow to store argument in the proper note.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The goal of the workflow is clear – store information as fast as possible without losing focus on what I&amp;rsquo;m doing. Take a look at the keyword &lt;code&gt;inbx&lt;/code&gt; – it corresponds with the &lt;em&gt;running note&lt;/em&gt; with the same name. It&amp;rsquo;s easy to remember how to store the information and where I can look for it later.&lt;/p&gt;
&lt;h3 id=&#34;handle-more-running-notes&#34;&gt;Handle more running notes&lt;/h3&gt;
&lt;p&gt;In previous example, I decided to use a variable to store the file name instead of using it directly in the &amp;ldquo;&lt;em&gt;Append to File&lt;/em&gt;&amp;rdquo; task. Here&amp;rsquo;s why:&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow_hu_40df48c826041c71.png 360w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow_hu_df71885833027565.png 640w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow_hu_cc55a09aecf2e5a3.png 960w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow_hu_5bf9d7a8d60c22ea.png 1200w, https://szymonkrajewski.pl/running-notes-and-information-capturing/054_running_note_many_files_alfred_workflow_hu_d92e006c83835eef.png 1420w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1420&#34;
          height=&#34;854&#34;
           alt=&#34;Alfred workflow with three keywords which steer the argument to the proper file.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      This workflow handles more note types by different keywords.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;By using the variable you can modify the destination path, so there is no need to copy the entire workflow to support more files. You can simply add new triggers and pass the execution to further processing.&lt;/p&gt;
&lt;p&gt;When you summon Alfred, you may type one of your keywords followed by your short note. In result, it stores information in the linked file. Alfred is accessible from any place in the system, so whether you browse the internet, chat, or write, you may call it and store whatever you want.&lt;/p&gt;
&lt;p&gt;If you urge doing something better because you won&amp;rsquo;t bear this kind of duplication or so, or you want to implement more features because you think you&amp;rsquo;ll need them – take a break. Do you want to invest your &lt;em&gt;precious time&lt;/em&gt; to develop something you &lt;em&gt;may&lt;/em&gt; use? &lt;em&gt;Think big, start small, improve in time&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can preview how this workflow works:&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;img
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        src=&#34;https://szymonkrajewski.pl/running-notes-and-information-capturing/054_add_note_using_alfred.gif&#34;
         alt=&#34;Animation presents how alfred workflow for running notes works&#34; 
        
        
      /&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Alfred Workflow for running notes in action. Text editor is visible only for the presentation purpose.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The process of capturing information to notes should be quick and easy. The slower the whole procedure is, the more reasons you&amp;rsquo;ll find why you shouldn&amp;rsquo;t do this. Lack of time or fear of losing focus is only a few examples.&lt;/p&gt;
&lt;p&gt;By making the capturing process easy, you&amp;rsquo;ll see that you store more and more information – food for thoughts, gift ideas, links, moments during the workday when you need to switch the context (to let your boss realize that this way of work is inefficient). I update some of my running notes almost every single day.&lt;/p&gt;
&lt;p&gt;Although the whole note-taking needs some of your time, it&amp;rsquo;s like an investment you do for yourself. By using appropriate tools and approaches, you may reduce the needed time and still hold benefits from taking notes. It&amp;rsquo;s worth to do it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@stilclassics?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;STIL&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/notes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Stop using specialized vocabulary</title>
      <link>https://szymonkrajewski.pl/specialized-vocabulary/</link>
      <pubDate>Wed, 04 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/specialized-vocabulary/</guid>
      <description>&lt;p&gt;Each professional group has own &lt;em&gt;specialized vocabulary&lt;/em&gt;. It consists of words often related to tools, technologies or processes. They make everyday communication smooth and effective. Yet, this way of communication is appropriate only for specific environments.&lt;/p&gt;
&lt;p&gt;Sometimes IT professionals forget about their &lt;em&gt;specialized vocabulary&lt;/em&gt; and use them in conversations with people from departments such as sales, marketing, HR, or management. Moreover, I often hear some technical words in talks with people who have nothing in common with IT. The most common view at this moment is the embarrassment drawing on people&amp;rsquo;s faces.&lt;/p&gt;
&lt;p&gt;Would you like to talk to someone who doesn&amp;rsquo;t understand what you&amp;rsquo;re talking about?&lt;/p&gt;
&lt;p&gt;Imagine the situation when you have a medical visit. The doctor gives you a diagnosis using complex medical vocabulary. He also adds some Latin words because he usually uses them with talks with other doctors. How would you feel? Is it your fault that you don&amp;rsquo;t understand him?&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re the programmer, you know – you cannot use a new word in the source code counting that the compiler will understand what you expect. You don&amp;rsquo;t do this with computers, so why do you try the same with people?&lt;/p&gt;
&lt;p&gt;Language is a &lt;strong&gt;tool&lt;/strong&gt;. If you use a tool dedicated to &lt;a href=&#34;https://szymonkrajewski.pl//tags/communication/&#34;&gt;communication&lt;/a&gt; and people don&amp;rsquo;t understand you, you use it &lt;em&gt;wrong&lt;/em&gt;. The key element of each conversation is the &lt;strong&gt;mutual understanding&lt;/strong&gt;. It&amp;rsquo;s a pleasure moment when &lt;em&gt;I feel, that you understand me and you feel, that I understand you&lt;/em&gt;. Let your conversation partner feel this way and the &lt;em&gt;magic&lt;/em&gt; will happen.&lt;/p&gt;
&lt;p&gt;I propose you an exercise. Try to explain &lt;em&gt;hard things&lt;/em&gt; to people, using simple words. It could be more difficult than you expect but the outcomes could be outstanding.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@mimithian?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Mimi Thian&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/understanding?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Why it&#39;s worth taking notes in plaintext</title>
      <link>https://szymonkrajewski.pl/taking-notes-in-plaintext/</link>
      <pubDate>Thu, 14 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/taking-notes-in-plaintext/</guid>
      <description>&lt;p&gt;For most people, keeping notes and other stuff in text files is ridiculous – tons of services and apps could do it better. But what the &lt;em&gt;better&lt;/em&gt; means?&lt;/p&gt;
&lt;p&gt;Despite the availability and diversity of note-taking solutions on the market, I haven&amp;rsquo;t found any that would meet my expectations. I tried to stick to the one app but I had felt the internal resistance to settling in one specific, often a &lt;em&gt;vendor-locked&lt;/em&gt; solution.&lt;/p&gt;
&lt;p&gt;The breakthrough was when I ask myself – &lt;em&gt;why I even want to note this information?&lt;/em&gt; I had started to look for my &amp;ldquo;&lt;em&gt;why&lt;/em&gt;&amp;rdquo; and it turned out, that I don&amp;rsquo;t need a fancy app or service to storing notes but a directory called &amp;ldquo;notes&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;why-do-i-take-notes&#34;&gt;Why do I take notes?&lt;/h2&gt;
&lt;p&gt;Most of the people take notes – on a piece of paper, post-it notes, journals, smartphones, apps and so one. But why do we do this? I assume, that the common reason is &amp;ldquo;&lt;em&gt;To remember X&lt;/em&gt;&amp;rdquo;. It&amp;rsquo;s a far-reaching simplification. In general, we take notes to &lt;strong&gt;make the information available for our future self&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you write note that you&amp;rsquo;ll need to buy milk tomorrow, it&amp;rsquo;s a reminder for &lt;em&gt;tomorrow&amp;rsquo;s self&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If you take notes from the lecture, you&amp;rsquo;ll want to learn from them &lt;em&gt;later&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If you write a journal, you expect that information you log might be helpful &lt;em&gt;someday&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If you find interesting information, you&amp;rsquo;ll store them hoping it&amp;rsquo;ll be useful &lt;em&gt;in the future&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you got the point. Each note is a piece of information addressed to &lt;em&gt;somebody&lt;/em&gt;, including your &lt;em&gt;future self&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-plaintext&#34;&gt;Why plaintext?&lt;/h2&gt;
&lt;p&gt;Geared with the knowledge why I take notes, I can define my needs and check if the specific approach, app or service works for me. It turned out that the best option for me is to keep notes in plaintext.&lt;/p&gt;
&lt;h3 id=&#34;plaintext-is-easy-to-search&#34;&gt;Plaintext is easy to search&lt;/h3&gt;
&lt;p&gt;Because my notes are addressed to myself from the future, I would like to be able to find them exactly when I need them. So the &lt;strong&gt;searchability&lt;/strong&gt; is the crucial factor of the whole note-taking system. Does it make sense to store notes you can&amp;rsquo;t find?&lt;/p&gt;
&lt;p&gt;Text files are search-friendly – you may look for files containing specific words or phrases. Pictures and diagrams, although still useful, need to be additionally described, if you want to look for something they show.&lt;/p&gt;
&lt;h3 id=&#34;files-are-easy-to-create&#34;&gt;Files are easy to create&lt;/h3&gt;
&lt;p&gt;New notes should be &lt;strong&gt;easy to create&lt;/strong&gt;. I don&amp;rsquo;t want to run a huge program to write a few words. I have an idea – bang! – a few keystrokes and it&amp;rsquo;s stored. Have you found anything interesting? Open app using the shortcut, insert what you&amp;rsquo;ve found, and add context if needed. Get back to work. That&amp;rsquo;s it.&lt;/p&gt;
&lt;h3 id=&#34;plaintext-is-long-lasting&#34;&gt;Plaintext is long-lasting&lt;/h3&gt;
&lt;p&gt;I usually store everything I find interesting to me. The problem is that I don&amp;rsquo;t know when I&amp;rsquo;ll need this piece of information. Will it be tomorrow? Next month? When I get a new job? When I get retired? Because of that, my notes have to be &lt;strong&gt;long-lasting&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Software evolves and many tools I used in the past are no longer available. The company, that provides services, may change strategy or business model e.g. by introducing payments or changing policies, making the service unattractive in the outcome. Also, sticking to the proprietary format makes you &lt;em&gt;dependent on the specific tool&lt;/em&gt;. However, plaintext is just &lt;em&gt;text&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;plaintext-doesnt-depend-on-specific-software&#34;&gt;Plaintext doesn&amp;rsquo;t depend on specific software&lt;/h3&gt;
&lt;p&gt;Data, although yours, is accessible only through the one specific piece of software provided by the company, whose vision may evolve in time. You can export them but it often means losing a specific set of features or formatting in the result. The plaintext is &lt;strong&gt;universal&lt;/strong&gt; and each device and operating system support it.&lt;/p&gt;
&lt;p&gt;Keeping data in plaintext is &lt;strong&gt;free&lt;/strong&gt;. There is no subscription fee for the service or app. You can use your favorite text editor or you can buy one if you want something extra, but it&amp;rsquo;s optional.&lt;/p&gt;
&lt;h3 id=&#34;plaintext-gives-you-more-control-and-more-possibilities&#34;&gt;Plaintext gives you more control and more possibilities&lt;/h3&gt;
&lt;p&gt;Because data is under your control, you are not obliged to store each piece of information on the vendor&amp;rsquo;s infrastructure. You may keep everything locally or use more &lt;strong&gt;privacy-focused&lt;/strong&gt; solutions that use &lt;strong&gt;end-to-end encryption&lt;/strong&gt;. You can still store your notes in the cloud (&lt;a href=&#34;https://szymonkrajewski.pl/the-hidden-cost-of-self-hosted-services/&#34;&gt;self-hosted solutions have lots of hidden costs&lt;/a&gt;), but it&amp;rsquo;s only your choice.&lt;/p&gt;
&lt;p&gt;By storing data in files, you&amp;rsquo;re not limited by the one app. You can use any &lt;strong&gt;other tools&lt;/strong&gt; to develop the functionality you need. For instance, if you would like you to track the changes in your notes, &lt;a href=&#34;https://szymonkrajewski.pl/automatic-file-versioning-after-change-using-fswatch-and-git/&#34;&gt;you can use git with &lt;em&gt;launchd&lt;/em&gt;&lt;/a&gt; (or &lt;em&gt;cron&lt;/em&gt;), to &lt;em&gt;automate&lt;/em&gt; this process.&lt;/p&gt;
&lt;p&gt;I talked only about &lt;em&gt;encryption&lt;/em&gt; and &lt;em&gt;versioning&lt;/em&gt;, but there are more possibilities. I mentioned what I use because for now, they&amp;rsquo;re only things &lt;em&gt;I need&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;should-it-be-just-plaintext&#34;&gt;Should it be just plaintext?&lt;/h2&gt;
&lt;p&gt;The plaintext is more like &lt;em&gt;philosophy&lt;/em&gt;. It&amp;rsquo;s a way for organizing information and knowledge without worrying about privacy, price and compatibility.&lt;/p&gt;
&lt;p&gt;To make this system useful, you need to dive into it. Notes will be eventually a dump of your brain that you can trust. But to make this happen, you have to make your notes a part of your life. It&amp;rsquo;s much easier if you have a &lt;em&gt;private space&lt;/em&gt; for that, without distracting, collaborative features.&lt;/p&gt;
&lt;p&gt;But sometimes plaintext is &lt;em&gt;not enough&lt;/em&gt;. And that&amp;rsquo;s fine! Of course, it&amp;rsquo;s much easier to store some kind of information as images or spreadsheets instead of trying to fit each piece of information into the text form. But it&amp;rsquo;s important to remember that you have that piece of information &lt;em&gt;somewhere&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;A lot of things still need further explication. How to start? How to organize files? One directory or many? How to name the note? How to organize them efficiently? I&amp;rsquo;ll touch these topics in the next articles, but for now, I&amp;rsquo;m going to leave two useful resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://plaintextproject.online/&#34;&gt;Plaintextproject.online&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://plaintext-productivity.net/&#34;&gt;Plaintext-productivity.net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&amp;rsquo;t take notes but you want to start, I recommend you to &lt;a href=&#34;https://szymonkrajewski.pl/think-big-start-small/&#34;&gt;&lt;strong&gt;think big, but start small&lt;/strong&gt;&lt;/a&gt;. Start with creating a &lt;em&gt;daily journal&lt;/em&gt; or &lt;em&gt;work log&lt;/em&gt;. Make yourself comfortable with taking notes. Don&amp;rsquo;t try to develop your perfect note-taking system, because, on this step, you don&amp;rsquo;t even know what you need. Make yourself comfortable with taking notes first, and go to the next level if you&amp;rsquo;re ready. And don&amp;rsquo;t forget about your &lt;em&gt;why&lt;/em&gt; – your future self may thank you someday.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@glenncarstenspeters?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Glenn Carstens-Peters&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/digital-text?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The selfie stick story</title>
      <link>https://szymonkrajewski.pl/selfie-stick-story/</link>
      <pubDate>Wed, 30 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/selfie-stick-story/</guid>
      <description>&lt;p&gt;One of the problems during traveling in pair is to take photos of us together. Since we have enough photos &amp;ldquo;&lt;em&gt;from hand&lt;/em&gt;&amp;rdquo; in our albums, I decided to buy a selfie stick.&lt;/p&gt;
&lt;p&gt;Instead of a standard stick with a jack wire, I bought a Bluetooth version of it. I was trying to be modern, but I forgot that I have to charge it from time to time and connect it to my phone each time I want to use it.&lt;/p&gt;
&lt;p&gt;I regret that I had bought this selfie stick.&lt;/p&gt;
&lt;p&gt;In the meantime, I changed my phone to one which has no headphone jack. Last holidays I noticed that I&amp;rsquo;m still able to use my stick because it has a Bluetooth connection, not a headphone jack wire.&lt;/p&gt;
&lt;p&gt;I appreciated that I had bought this selfie stick.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@abrkett?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Adam Birkett&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/selfie-stick?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Automate your macOS backup to Backblaze B2 using Restic and launchd</title>
      <link>https://szymonkrajewski.pl/macos-backup-restic/</link>
      <pubDate>Thu, 17 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/macos-backup-restic/</guid>
      <description>&lt;p&gt;In this article, I would like to show you how to prepare the script that will back up your important files to the cloud storage using a tool called &lt;em&gt;restic&lt;/em&gt;. Even though the article is specific for macOS and Backblaze B2, you can use some techniques for creating a similar script for Linux systems.&lt;/p&gt;
&lt;h2 id=&#34;disclaimer&#34;&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m not going to give you a ready-to-use script or working solution. I know nothing about your needs, your files, limitations of your network, budget, and other things that could have an impact on the backup workflow. You should always adjust the tools and the workflow to your needs.&lt;/p&gt;
&lt;p&gt;Instead, I&amp;rsquo;ll show you my process of building the backup script that works for me. I use a word &lt;em&gt;process&lt;/em&gt;, because it&amp;rsquo;s not a &lt;em&gt;single task&lt;/em&gt;. Trying to build something perfect first off will lead to a product with unnecessary features. Start with the minimal script and change when you notice that you &lt;strong&gt;need&lt;/strong&gt; something more.&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements&lt;/h2&gt;
&lt;p&gt;In this article, I&amp;rsquo;m going to use some tools and services and I assume, that you have basic knowledge of them. If not, don&amp;rsquo;t worry. I&amp;rsquo;ll describe how to use them in the context of creating the backup script. If you want to know more, please check the documentation of these tools.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://restic.net/&#34;&gt;Restic&lt;/a&gt; – a command-line tool for creating backups and restoring files.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.backblaze.com/cloud-storage&#34;&gt;Backblaze B2&lt;/a&gt; – Cloud storage (you can use a different provider).&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html&#34;&gt;launchd&lt;/a&gt; – macOS&amp;rsquo;s system service management (you can use &lt;em&gt;cron&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;macOS keychain – secure way to store secrets.&lt;/li&gt;
&lt;li&gt;Bash – I&amp;rsquo;m going to write the script in bash.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;preparing-environment&#34;&gt;Preparing environment&lt;/h2&gt;
&lt;h3 id=&#34;make-your-file-structure-backup-friendly&#34;&gt;Make your file structure backup-friendly&lt;/h3&gt;
&lt;p&gt;Before you start, I recommend you to clean up the disk a little bit by separating stuff you care from the clutter you don&amp;rsquo;t need. Are you need to backup your entire disk? Do you want to pay for storing all these downloaded stuff?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Keep files you want to backup and non-important files separate.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Maintaining a good and solid file structure may help you also during restoring files from snapshots. If you have a dedicated place on the disk for stuff you care, you&amp;rsquo;ll never have a problem with finding the file you lost or need.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;em&gt;restic&lt;/em&gt; and your perseverance to organize files, you&amp;rsquo;ll add &lt;strong&gt;extra dimension&lt;/strong&gt; of your disk – time. It means you are not limited to browse files only within directories, but also in different moments in time.&lt;/p&gt;
&lt;h3 id=&#34;install-restic&#34;&gt;Install restic&lt;/h3&gt;
&lt;p&gt;First, you need to install &lt;strong&gt;restic&lt;/strong&gt;. In macOS, the easiest way to do is to using &lt;em&gt;homebrew&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; brew install restic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After installation, you can run the program to see available commands.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; restic
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;restic is a backup program which allows saving multiple revisions of files and
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;directories in an encrypted repository stored on different backends.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  restic &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;command&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Available Commands:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  backup        Create a new backup of files and/or directories
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  cache         Operate on &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; cache directories
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  cat           Print internal objects to stdout
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;initialize-restic-repository-in-backblaze-b2&#34;&gt;Initialize restic repository in Backblaze B2&lt;/h3&gt;
&lt;p&gt;This step depends on where you want to store your repository. I use the Backblaze B2 service, but you may use different storage. Here is a &lt;a href=&#34;https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html&#34;&gt;full list of supported storage providers&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;create-backblazes-b2-bucket&#34;&gt;Create Backblaze&amp;rsquo;s B2 Bucket&lt;/h4&gt;
&lt;p&gt;Create a new &lt;strong&gt;private bucket&lt;/strong&gt; called e.g. &lt;code&gt;restic-backup-2019&lt;/code&gt;. Then, In the &lt;strong&gt;App Keys&lt;/strong&gt; section, add a new application key called e.g. &lt;code&gt;macos-restic-backup&lt;/code&gt; with &lt;em&gt;read and write&lt;/em&gt; access to this newly created bucket.&lt;/p&gt;
&lt;h4 id=&#34;use-keys-to-initialize-the-repository&#34;&gt;Use keys to initialize the repository&lt;/h4&gt;
&lt;p&gt;Restic reads Backblaze credentials from environment variables. Let&amp;rsquo;s export them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; &lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;keyID visible on app keys page&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; &lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&amp;lt;applicationKey displayed after app key generation&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, you can initialize the new repository using &lt;code&gt;restic init&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; restic init -r b2:restic-backup-2019:backup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enter password &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; new backend:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enter password again:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; flag stands from &lt;em&gt;repository&lt;/em&gt;. The prefix (b2) points to the provider, whereas suffix is a path to the repository within the bucket (/&lt;em&gt;backup&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Restic will ask you to enter a password for the new repository. Set the &lt;strong&gt;strong password&lt;/strong&gt; and store it in a safe place, e.g. in a password manager such as &lt;em&gt;KeePass&lt;/em&gt; or &lt;em&gt;Keychain&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;create-your-first-backup&#34;&gt;Create your first backup&lt;/h3&gt;
&lt;p&gt;Once the repository is initialized, you may create your first backup. To see how restic works, let&amp;rsquo;s do it by hand. This command creates a new snapshot of &lt;code&gt;~/Documents&lt;/code&gt; directory in the repository.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; /usr/local/bin/restic backup -r b2:restic-backup-2019:backup --verbose ~/Documents
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, restic will ask you for the password to the repository. If you want to automate this process, you have to provide the password differently.&lt;/p&gt;
&lt;p&gt;Restic has two options for retrieving the password to the repository.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using file – you can store the password in the file.&lt;/li&gt;
&lt;li&gt;Using command – you can specify a &lt;em&gt;shell command&lt;/em&gt; to get the password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Retrieving a password using command sounds better for me because I can store the password securely using macOS Keychain. Then, I can obtain it using &lt;strong&gt;security&lt;/strong&gt;, which is a command-line interface for the Keychain. Let&amp;rsquo;s do this way.&lt;/p&gt;
&lt;h3 id=&#34;store-secrets-in-macos-keychain&#34;&gt;Store secrets in macOS keychain&lt;/h3&gt;
&lt;p&gt;You can add new entries to the keychain using the graphical interface or by the security tool. In the terminal, add a new password using the &lt;code&gt;security add-generic-password&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; security add-generic-password -s backup-restic-repository -a restic_backup -w
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;password data &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; new item:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;retype password &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; new item:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To retrieve the password from the keychain, use the &lt;code&gt;security find-generic-password&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; security find-generic-password -s backup-restic-password-repository -w
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;the_secret_password_to_secure_my_backups
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s reasonable to store other secrets in Keychain as well, so let&amp;rsquo;s add them.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; security add-generic-password -s backup-restic-repository -a restic_backup -w
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; security add-generic-password -s backup-restic-b2-accound-id -a restic_backup -w
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; security add-generic-password -s backup-restic-b2-account-key -a restic_backup -w
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;prepare-script-to-automate-backup&#34;&gt;Prepare script to automate backup&lt;/h2&gt;
&lt;h3 id=&#34;use-restic-in-script&#34;&gt;Use restic in script&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s use secrets from Keychain to run the first backup using the dedicated script.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-b2-accound-id -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-b2-account-key -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;RESTIC_REPOSITORY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-repository -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;RESTIC_PASSWORD_COMMAND&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;security find-generic-password -s backup-restic-password-repository -w&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/restic backup --verbose -o b2.connections&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt; ~/Documents
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, I populate environment variables that restic uses by secrets obtained from Keychain. Only the &lt;code&gt;RESTIC_PASSWORD_COMMAND&lt;/code&gt; variable is a &lt;em&gt;string&lt;/em&gt; with the command because restic will run it when the password is needed.&lt;/p&gt;
&lt;p&gt;The extra &lt;code&gt;-o b2.connections=20&lt;/code&gt; flag is a specific option for the Backblaze B2 backend. You can omit this if you use a different storage provider.&lt;/p&gt;
&lt;p&gt;Save it as a &lt;code&gt;backup.sh&lt;/code&gt; using your favorite text editor and make it executable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; chmod +x backup.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; ./backup.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you used a GUI to add secrets to the keychain, you should allow &lt;strong&gt;security&lt;/strong&gt; obtaining passwords. Use &lt;em&gt;Always Allow&lt;/em&gt; to avoid this question in the future.&lt;/p&gt;
&lt;h3 id=&#34;specify-the-list-of-directories-to-backing-up&#34;&gt;Specify the list of directories to backing up&lt;/h3&gt;
&lt;p&gt;You can type directories or even single files directly in the &lt;code&gt;restic backup&lt;/code&gt; command, however, if you have lots of them, the command will be hard to read and manage. Unless you don&amp;rsquo;t back up the whole home directory, it&amp;rsquo;s useful to prepare a file containing directories to backing up.&lt;/p&gt;
&lt;p&gt;Create a new file called &lt;code&gt;backup.txt&lt;/code&gt; with a list of directories and files you want to include in backups. Here&amp;rsquo;s an example file. &lt;strong&gt;All the paths are relative to the user&amp;rsquo;s home directory&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat ~/backup.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Desktop/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Documents/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Movies/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Music/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Photos/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.config/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.ssh/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, change the &lt;code&gt;backup&lt;/code&gt; command to use this file. I assume, that this file is located in the home directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/restic backup --verbose -o b2.connections&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt; --files-from ~/backup.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;avoid-running-backup-twice-at-the-same-time&#34;&gt;Avoid running backup twice at the same time&lt;/h3&gt;
&lt;p&gt;Because you&amp;rsquo;re preparing the automatic script, it&amp;rsquo;s worth to add some checks to prevent malfunctions, e.g. invoking script when another backup is in progress.&lt;/p&gt;
&lt;p&gt;You can use a simple approach with the PID file in the home directory. The script could check if there is any backup process running. If so, there is no need to perform a backup, so you can finish the execution. Otherwise, the script creates a PID file and removes it once the backup is done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PID_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.restic_backup.pid
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;File &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exist. Probably backup is already in progress.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# restic execution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, it was not enough. Sometimes, because of logouts, computer restarts, or other errors, the script won&amp;rsquo;t reach the &lt;code&gt;rm&lt;/code&gt; command and the next scheduled backup had no chance to start. You can change the approach a bit and add an extra condition to check if this specific process exists. Let&amp;rsquo;s add a date and time to the output to provide more context.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PID_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.restic_backup.pid
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; ps -p &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;File &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exist. Probably backup is already in progress.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;File &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exist but process &amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; not found. Removing PID file.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    rm &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# restic execution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;create-a-backup-from-trusted-networks&#34;&gt;Create a backup from trusted networks&lt;/h3&gt;
&lt;p&gt;For some reason, you may don&amp;rsquo;t want to perform backups from public or untrusted networks or these created as personal mobile hot-spot, because of the limited bandwidth. In that case, it&amp;rsquo;s worth to check the active connection and allow executing script only if a computer is connected to the &lt;strong&gt;allowed network&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For backup purposes, you may use your home and work Wi-Fi network. To check the current Wi-Fi on macOS, you can use the &lt;code&gt;networksetup -getairportnetwork en0&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;networksetup -getairportnetwork en0 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -E &lt;span class=&#34;s2&#34;&gt;&amp;#34;Home-Network|Work-Network&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Unsupported network.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By using grep with regex option you can check if the output contains one of the allowed Wi-Fi names. If not, the script exits with status code &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I use different status codes to distinguish why the script ends – it may be useful in future automation.&lt;/p&gt;
&lt;h3 id=&#34;create-backup-only-if-the-computer-is-plugged-in-to-power-source&#34;&gt;Create backup only if the computer is plugged in to power source&lt;/h3&gt;
&lt;p&gt;Backup may be a power-demanding operation, especially if restic needs to perform a full scan of files and send lots of them to the cloud. To avoid battery draining, the script should do backups only if the computer is connected to the &lt;strong&gt;stable power source&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can check if your mac uses power from the battery using &lt;code&gt;pmset -g ps&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;pmset -g ps &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~ &lt;span class=&#34;s2&#34;&gt;&amp;#34;Battery&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Computer is not connected to the power source.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;schedule-automatic-backup&#34;&gt;Schedule automatic backup&lt;/h3&gt;
&lt;p&gt;The script is almost done so you can schedule the backup by defining a &lt;strong&gt;launchd job&lt;/strong&gt;. You can also use &lt;em&gt;cron&lt;/em&gt;, but &lt;em&gt;launchd&lt;/em&gt; is preferred way to schedule actions in macOS.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how we can approach this.&lt;/p&gt;
&lt;h4 id=&#34;the-problem-with-launchd&#34;&gt;The problem with &lt;em&gt;launchd&lt;/em&gt;&lt;/h4&gt;
&lt;p&gt;Launchd has an interesting property called &lt;code&gt;StartInteval&lt;/code&gt; that lets you create an agent that runs a task every &lt;code&gt;n&lt;/code&gt; seconds. Moreover, it counts time also when the computer is asleep.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartInterval&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;300&lt;span class=&#34;nt&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nevertheless, the script has some extra conditions that may break the execution earlier – this is a variation of the &lt;a href=&#34;https://szymonkrajewski.pl/why-should-you-return-early/&#34;&gt;return early approach&lt;/a&gt;. Despite that, &lt;em&gt;launchd&lt;/em&gt; get the information that script was executed and it will schedule the next execution and you end up with no backup.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve experienced also another issue related to the &lt;code&gt;StartInterval&lt;/code&gt; property, especially if the value is greater than a few hours. The job will be executed after an unpredictable time, sometimes much later than the interval value. For short intervals, like 5 minutes, everything works like a charm.&lt;/p&gt;
&lt;p&gt;Backing up files every 5 minutes is too aggressive and surely not needed, because it leads to many snapshots of data with small differences.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to show you how to add this kind of logic into the backup script.&lt;/p&gt;
&lt;h4 id=&#34;add-time-condition-to-your-script&#34;&gt;Add time condition to your script&lt;/h4&gt;
&lt;p&gt;First, you need to have an additional place to store the &lt;em&gt;timestamp&lt;/em&gt;. Let&amp;rsquo;s keep it simple and use the file as we do with the PID file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.restic_backup_timestamp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file will contain the timestamp of date in the future, but for now, it doesn&amp;rsquo;t even exist. Don&amp;rsquo;t care about it. In the script, you can add a condition to check if the timestamp file exists and if so, you may check if the &lt;em&gt;current timestamp&lt;/em&gt; is less than the value from the file. If it&amp;rsquo;s true, then the minimum threshold hasn&amp;rsquo;t been reached and there is no need to back up – it&amp;rsquo;s too early. Otherwise, we can go further.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;time_run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;current_time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$current_time&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -lt &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$time_run&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You have to also set this threshold after the backup command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date -v +6H +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Above statement adds 6 hours to the current time and stores the timestamp to the &lt;code&gt;$TIMESTAMP_FILE&lt;/code&gt;. If you want to change the frequency of backup, you can replace &lt;code&gt;6H&lt;/code&gt; by a different value in the &lt;code&gt;date&lt;/code&gt; command.&lt;/p&gt;
&lt;h4 id=&#34;create-a-launchd-job&#34;&gt;Create a &lt;em&gt;launchd&lt;/em&gt; job&lt;/h4&gt;
&lt;p&gt;Now, you have to create a new &lt;em&gt;property list&lt;/em&gt; file. In my case, the name is &lt;code&gt;pl.skrajewski.restic_backup.plist&lt;/code&gt;, with is the same name as the &lt;em&gt;label&lt;/em&gt; for the job. This name follows the &lt;a href=&#34;https://en.wikipedia.org/wiki/Reverse_domain_name_notation&#34;&gt;reverse domain name notation&lt;/a&gt;, which is a simple way to categorize and sort not only jobs, but also packages, components and other stuff.&lt;/p&gt;
&lt;p&gt;My &lt;em&gt;plist&lt;/em&gt; file looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;pl.skrajewski.restic_backup&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/scripts/backup.sh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartInterval&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;300&lt;span class=&#34;nt&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WorkingDirectory&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/Library/Logs/pl.skrajewski.restic_backup.out.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/Library/Logs/pl.skrajewski.restic_backup.error.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because I want to run this job as a user, I&amp;rsquo;m going to register it as an &lt;em&gt;agent&lt;/em&gt;. All you have to do is to copy this file to the &lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt; directory and load the job using &lt;code&gt;launchctl&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cp pl.skrajewski.restic_backup.plist ~/Library/LaunchAgents
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; launchctl load ~/Library/LaunchAgents/pl.skrajewski.restic_backup.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the job is loaded, the first backup will be started. Launchd also loads jobs when a user logs in. If you want to know how it works, you can read the &lt;em&gt;&lt;a href=&#34;https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html&#34;&gt;The launchd Startup Process&lt;/a&gt;&lt;/em&gt; section in the documentation.&lt;/p&gt;
&lt;h2 id=&#34;full-backup-script&#34;&gt;Full backup script&lt;/h2&gt;
&lt;p&gt;There is a full &lt;code&gt;backup.sh&lt;/code&gt; script. It writes the output will to &lt;code&gt;StandardOutPath&lt;/code&gt; and &lt;code&gt;StandardErrorPath&lt;/code&gt; files. You can preview them using your terminal or using the &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_macOS_components#Console&#34;&gt;Console application&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This file is also available as &lt;a href=&#34;https://gist.github.com/skrajewski/a8b43ce3e5f0fe0971887bac7c45fe18&#34;&gt;GitHub&amp;rsquo;s Gist&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PID_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.restic_backup.pid
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/.restic_backup_timestamp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; ps -p &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;File &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exist. Probably backup is already in progress.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;File &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; exist but process &amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; not found. Removing PID file.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    rm &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;time_run&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;cat &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nv&#34;&gt;current_time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$current_time&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -lt &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$time_run&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;networksetup -getairportnetwork en0 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -E &lt;span class=&#34;s2&#34;&gt;&amp;#34;Home-Network|Work-Network&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Unsupported network.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;pmset -g ps &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~ &lt;span class=&#34;s2&#34;&gt;&amp;#34;Battery&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Computer is not connected to the power source.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$$&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Backup start&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-b2-accound-id -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;B2_ACCOUNT_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-b2-account-key -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;RESTIC_REPOSITORY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;security find-generic-password -s backup-restic-repository -w&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;RESTIC_PASSWORD_COMMAND&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;security find-generic-password -s backup-restic-password-repository -w&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/restic backup --verbose -o b2.connections&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt; --files-from ~/backup.txt 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%Y-%m-%d %T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Backup finished&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date -v +6H +&lt;span class=&#34;s2&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; &amp;gt; &lt;span class=&#34;nv&#34;&gt;$TIMESTAMP_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm &lt;span class=&#34;nv&#34;&gt;$PID_FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;macos-catalina-dealing-with-system-integrity-protection-sip-and-new-permissions&#34;&gt;MacOS Catalina: Dealing with System Integrity Protection (SIP) and new permissions&lt;/h2&gt;
&lt;p class=&#34;box.info.has-title&#34;&gt;&lt;strong&gt;Before you start&lt;/strong&gt;&lt;br&gt;
If you don&amp;rsquo;t want to use the extra binary to handle specific permissions, you can add your shell (e.g. &lt;em&gt;bash&lt;/em&gt; or &lt;em&gt;zsh&lt;/em&gt;) to &lt;strong&gt;Full Disk Access&lt;/strong&gt; list in &lt;em&gt;Security and Privacy&lt;/em&gt; panel.&lt;/p&gt;
&lt;p&gt;After upgrading the system to macOS Catalina 10.15, my backup script stopped working. The error log had error &lt;em&gt;operation not permitted&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scan: Open: open /Users/szymon/Desktop: operation not permitted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scan: Open: open /Users/szymon/Documents: operation not permitted
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scan: Open: open /Users/szymon/Pictures/Photos.photoslibrary: operation not permitted
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;According to Catalina&amp;rsquo;s landing page, Apple improved the security of the system by using read-only volume for system data and adding more permissions that applications have to ask for.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data protections.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Apps must now get your permission before directly accessing files in your Documents and Desktop folders, iCloud Drive, and external volumes, so you’re always in control of your data. And you’ll be prompted before any app can capture keyboard activity or a photo or video of your screen.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://www.apple.com/macos/catalina/&#34;&gt;https://www.apple.com/macos/catalina/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Unfortunately, the script didn&amp;rsquo;t ask me for permission for any directory or functionality. I tried to add the &lt;em&gt;Full Disk Access&lt;/em&gt; to the backup script, restic, bash, and terminal, but I still got the same outcome.&lt;/p&gt;
&lt;p&gt;After &lt;a href=&#34;https://github.com/restic/restic/issues/2051&#34;&gt;reading some discussion on GitHub&lt;/a&gt;, I found out that wrapping script in the binary may help solve this issue. I don&amp;rsquo;t know the underlying mechanism yet, but when you run the binary, it asks the user for permission to requested resources.&lt;/p&gt;
&lt;p&gt;I wrote a small &lt;strong&gt;console application&lt;/strong&gt; in Swift that runs my backup script.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Foundation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;task&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;launchPath&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/bin/bash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arguments&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/Users/szymon/scripts/backup.sh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;os_log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;waitUntilExit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;There is a security problem: the intruder may replace the content of &lt;code&gt;backup.sh&lt;/code&gt; and run the custom script to gain extra privileges.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After compilation, I got the binary &lt;strong&gt;backup_wrapper&lt;/strong&gt;. I put it in the same place as my original backup script and I changed the path to this executable in &lt;code&gt;pl.skrajewski.restic_backup.plist&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/scripts/backup_wrapper&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When I run the binary, it asked me for permission to resources. &lt;strong&gt;I didn&amp;rsquo;t have to add &lt;em&gt;Full Disk Access&lt;/em&gt; to the binary, restic nor bash.&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/macos-backup-restic/045_security_files_folders.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/macos-backup-restic/045_security_files_folders_hu_ec0e7bcec23761c9.png 360w, https://szymonkrajewski.pl/macos-backup-restic/045_security_files_folders_hu_c9390a6d046d874b.png 640w, https://szymonkrajewski.pl/macos-backup-restic/045_security_files_folders_hu_dfd13ba194135d17.png 780w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;780&#34;
          height=&#34;685&#34;
           alt=&#34;macOS privacy panel showing that binary backup_wrapper has access to the Files and Folders.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      backup_wrapper binary with the permission to &lt;em&gt;Files and Folder&lt;/em&gt;.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/macos-backup-restic/045_security_photos.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/macos-backup-restic/045_security_photos_hu_5860f638dc74b724.png 360w, https://szymonkrajewski.pl/macos-backup-restic/045_security_photos_hu_1f1fc6a2e06200e1.png 640w, https://szymonkrajewski.pl/macos-backup-restic/045_security_photos_hu_2f44de0112e19d31.png 780w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;780&#34;
          height=&#34;685&#34;
           alt=&#34;macOS privacy panel showing that binary backup_wrapper has access to the Photos.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      backup_wrapper binary with the permission to &lt;em&gt;Photos&lt;/em&gt;.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;It&amp;rsquo;s still a lot of hassle to set up the whole backup process. If you work with bash scripts, there is a big chance that you&amp;rsquo;ll get in trouble with permissions. I would like to hear other opinions about this mechanism in Catalina and how to deal with it. If you already solve this issue in another way, please let me know.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As you can see, by crafting things for your needs you don&amp;rsquo;t have to start with &lt;em&gt;something big&lt;/em&gt;. Start small, and change it in time. Add only things that you need to have, not everything that comes to your mind. Otherwise, you will end up with a bloated script with tons of unnecessary features.&lt;/p&gt;
&lt;p&gt;I would implement this as a separate piece of software, written e.g. in JavaScript, using ready-to-use modules. But I didn&amp;rsquo;t. Things have to be &lt;strong&gt;simple&lt;/strong&gt;, because simple things are &lt;em&gt;cheap, easy to use and maintain.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are some extra features I have implemented in my backup workflow but I didn&amp;rsquo;t include in the article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/tonsky/AnyBar&#34;&gt;Anybar&lt;/a&gt; integration to check if the backup is already in progress.&lt;/li&gt;
&lt;li&gt;macOS notifications – I consider to get rid of them because the workflow is stable enough and I don&amp;rsquo;t need to have feedback if the backup has started.&lt;/li&gt;
&lt;li&gt;Integration with &lt;a href=&#34;https://healthchecks.io/&#34;&gt;healthchecks.io&lt;/a&gt; to get feedback if something is wrong with the workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recommend you to invest some time to set up an automatic backup workflow. It&amp;rsquo;s like the insurance – you may even don&amp;rsquo;t need it. But if something happens, it&amp;rsquo;s better to have it. And remember to check from time to time if your backup is consistent and restorable. Or maybe should we automate it as well?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;changelog&#34;&gt;Changelog&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 2019-11-28:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I added information, &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/#macos-catalina-dealing-with-system-integrity-protection-sip-and-new-permissions&#34;&gt;how I deal with the new permission system aka &amp;ldquo;entitlement&amp;rdquo;&lt;/a&gt;. It complicated this set up process a bit because it requires to compile custom binary, that runs the backup script.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 2021-03-07:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;As readers confirm, there is no need to run the backup script script using the extra binary wrapper if you grant &lt;strong&gt;Full Disk Access&lt;/strong&gt; to the shell you use with the script (bash or zsh). I added a proper info to the article in the &lt;a href=&#34;https://szymonkrajewski.pl/macos-backup-restic/#macos-catalina-dealing-with-system-integrity-protection-sip-and-new-permissions&#34;&gt;section regarding custom binary wrapper&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://pixabay.com/users/kropekk_pl-114936/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=283251&#34;&gt;kropekkpl&lt;/a&gt; from &lt;a href=&#34;https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=283251&#34;&gt;Pixabay&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Write code for people</title>
      <link>https://szymonkrajewski.pl/write-code-for-people/</link>
      <pubDate>Wed, 02 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/write-code-for-people/</guid>
      <description>&lt;p&gt;The first draft of this article was about the code-style. I wanted to show you why it is important and why we should care about it. But the longer I thought, the stronger conviction I gained, that the code-style – whatever it means – is only a way to achieve a much more important goal.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a big advocate of well-quality code. Besides good architecture, I pay attention to style – consistent spacing and indentation, coherent naming convention and other rules that make the code visually better. But how to convince someone, who never took care of style before, that is it a good thing?&lt;/p&gt;
&lt;h2 id=&#34;why-should-you-take-care-of-code&#34;&gt;Why should you take care of code?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s not obvious but the code you produce is not for the computer. Machines don&amp;rsquo;t need a beautiful code. They don&amp;rsquo;t even need your fancy file structure or well-designed architecture. A compiler translates your work to the machine code that doesn&amp;rsquo;t resemble your handsome code at all.&lt;/p&gt;
&lt;p&gt;So why should you care? &lt;strong&gt;Because you write for people, not machines&lt;/strong&gt;. Machines can run the program like a flash, whereas people need to read the code line by line to imagine what it does and what would the outcome be.&lt;/p&gt;
&lt;p&gt;The good code makes this process easy.&lt;/p&gt;
&lt;p&gt;By focus on the people, you switch the mindset from &amp;ldquo;&lt;em&gt;Does it work?&lt;/em&gt;&amp;rdquo; to &amp;ldquo;&lt;em&gt;Is it easy to understand?&lt;/em&gt;&amp;rdquo;. The computer understands the crappiest code in the world as long as it&amp;rsquo;s valid. People need more time to connect the dots and see dependencies in the code. You may help them by reducing the cognitive load by using proper abstractions, expressive naming, and consistent style.&lt;/p&gt;
&lt;p&gt;Even if you think that it doesn&amp;rsquo;t apply to you, you should care. If you conduct a project alone in a company, someone else may join you because of higher demands. If you leave your job, another developer takes over your work. How smooth this process will be?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Instead of asking &amp;ldquo;&lt;em&gt;Does it work?&lt;/em&gt;&amp;rdquo;, ask &amp;ldquo;&lt;em&gt;Is it easy to understand?&lt;/em&gt;&amp;rdquo; and change, until the answer is &amp;ldquo;yes&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Still in doubt? &lt;a href=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/&#34;&gt;A personal project you&amp;rsquo;re working on&lt;/a&gt; would be something bigger one day or would be stuck in your hard drive. It&amp;rsquo;s your decision. Even so, it&amp;rsquo;s something that you write for your &lt;strong&gt;future self&lt;/strong&gt;. You may come back to this code in the future, so it should be easy to understand and make a pleasure to work with.&lt;/p&gt;
&lt;p&gt;Code style is subjective, but it doesn&amp;rsquo;t matter. It&amp;rsquo;s everything about our customs and habits. Each team, company, and community has its perspective. Because you write for people, you will know how to do it right.&lt;/p&gt;
&lt;p&gt;Take care of the people, who will work with your code. Help them understand what the code does. &lt;a href=&#34;https://szymonkrajewski.pl/the-code-is-a-common-good/&#34;&gt;Treat it like the common good&lt;/a&gt;. You will see, how its quality improves in time. People will thank you someday.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@mimithian?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Mimi Thian&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/developer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The hidden cost of self-hosted services</title>
      <link>https://szymonkrajewski.pl/the-hidden-cost-of-self-hosted-services/</link>
      <pubDate>Wed, 18 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/the-hidden-cost-of-self-hosted-services/</guid>
      <description>&lt;p&gt;The Internet is teeming with services you can take advantage of. You can store your files in the cloud, share movies, organize photo collections, talk with friends, manage your projects and so on. It also means that your data is stored outside your computer and you don&amp;rsquo;t have full control over it.&lt;/p&gt;
&lt;p&gt;You may use self-hosted counterparts of services. Instead of accepting vague privacy policies, you may set the rules. However, you are fully responsible for sustaining your data.&lt;/p&gt;
&lt;p&gt;I used to consider self-hosted services as a privacy-respective and cheap alternatives of popular services. The former is true, however, the latter factor is often far-fetched.&lt;/p&gt;
&lt;h2 id=&#34;the-definition-of-cheap&#34;&gt;The definition of &lt;em&gt;cheap&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;If you limit yourself to thinking about costs only in the category of money, you may fall into the trap of &lt;em&gt;hidden costs&lt;/em&gt;. Nowadays, servers are affordable, so money it&amp;rsquo;s not a real problem. It is even &lt;em&gt;cheaper&lt;/em&gt; if you decide to host your services on-site, e.g. at home or in the office.&lt;/p&gt;
&lt;p&gt;Money is the most visible cost, however, &lt;strong&gt;it&amp;rsquo;s not the only one&lt;/strong&gt;. The second one is &lt;strong&gt;time&lt;/strong&gt;. To sustain the self-hosted services you need to devote extra time to installation, solving appearing problems, maintenance and so on.&lt;/p&gt;
&lt;h2 id=&#34;its-fun-as-long-as-its-just-for-fun&#34;&gt;It&amp;rsquo;s fun as long as it&amp;rsquo;s just for fun&lt;/h2&gt;
&lt;p&gt;Hosting your photo gallery or file server may be both fun and useful, however, you should ask yourself what do you want to achieve.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s only for learning purposes, you should reckon with potential &lt;em&gt;data loss&lt;/em&gt;. Your services may be unreliable and insecure, due to the unstable internet connection, lack of updates and improper configuration. If you plan to incorporate the service you host into your workflow, you have to &lt;em&gt;adapt&lt;/em&gt; it to production usage.&lt;/p&gt;
&lt;p&gt;What do you do if service that you use is unavailable and you&amp;rsquo;re the only one who can fix it? Do you want to be user or maintainer?&lt;/p&gt;
&lt;h2 id=&#34;the-hidden-cost-of-maintenance&#34;&gt;The hidden cost of maintenance&lt;/h2&gt;
&lt;p&gt;People often forget that everything around us requires our attention. Self-hosted services are not different. Attention should be considered a valuable asset. If you devote time and attention to maintaining your self-hosted services, you neglect the other, more important things you have to do.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth to &lt;strong&gt;calculate the value of your time&lt;/strong&gt;. If you know how much your hour costs, you make more accurate decisions. Isn&amp;rsquo;t better to pay a few bucks once a month and spend the recovered time on something more productive?&lt;/p&gt;
&lt;h2 id=&#34;do-you-need-self-hosted-solutions&#34;&gt;Do you need self-hosted solutions?&lt;/h2&gt;
&lt;p&gt;Always have a goal in mind. If you want to learn or test something, don&amp;rsquo;t use the service you host as the production one. Sooner or later, it will get you in trouble.&lt;/p&gt;
&lt;p&gt;If you want to have a privacy-respective alternative of another service, calculate the actual costs and check if it&amp;rsquo;s worth to hassle. Don&amp;rsquo;t underestimate the needs of the proper hardware, regular backups, and updates.&lt;/p&gt;
&lt;p&gt;Sometimes you enforce yourself on a specific self-hosted solution while there is a simpler alternative. Try to take a step back and look at the problem more broadly.&lt;/p&gt;
&lt;p&gt;Do you need a file synchronization server but you don&amp;rsquo;t want to send your files somewhere? You may &lt;a href=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/&#34;&gt;encrypt files before sending them to the cloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Do you need an online photo gallery to showcase your photos at family events? How often you need to do that? Isn&amp;rsquo;t a local gallery on your computer enough? How much time do you save hosted your gallery?&lt;/p&gt;
&lt;p&gt;You may find more and more examples. &lt;strong&gt;A self-hosted solution should solve the problem you have, instead of adding other ones.&lt;/strong&gt; Treat it as a rule of thumb. If something requires more resources than it gives you value, eliminate it.&lt;/p&gt;
&lt;p&gt;A goal is rarely a &amp;ldquo;&lt;em&gt;having self-hosted service&lt;/em&gt;&amp;rdquo;. The self-hosted solution is often one of possibilities to solve the actual problem. Choose the simpler one.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@gmalhotra?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Gayatri Malhotra&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Collecting tiny thoughts</title>
      <link>https://szymonkrajewski.pl/collecting-tiny-thoughts/</link>
      <pubDate>Wed, 21 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/collecting-tiny-thoughts/</guid>
      <description>&lt;p&gt;I collect lots of thoughts in my private notes. Some of them could be quite useful if I show them in a decent form. Unfortunately, they are too small to be a full article, so I decided to introduce another form of posts – &lt;em&gt;tiny thoughts&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tiny thoughts are thoughts that are too small to be an article but are too important to forget it.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I select only thoughts that are useful for you as well. They are my thoughts and my conclusions, so they are subjective. But I hope you will also find value in them.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to predict what I&amp;rsquo;ll be writing about. I spend a lot of time &lt;a href=&#34;https://szymonkrajewski.pl/tags/learning/&#34;&gt;learning&lt;/a&gt;, exploring and trying new things, &lt;a href=&#34;https://szymonkrajewski.pl/categories/growth&#34;&gt;improving myself and my life&lt;/a&gt;, so it would be an interesting topic. Nevertheless, I feel an urge to write a few words about things I do – either successful or failed ones.&lt;/p&gt;
&lt;p&gt;Thank you for reading it. 😊&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@danielcgold?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Dan Gold&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Use make as task runner</title>
      <link>https://szymonkrajewski.pl/use-make-as-task-runner/</link>
      <pubDate>Wed, 07 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/use-make-as-task-runner/</guid>
      <description>&lt;p&gt;During development, you probably take advantage of some extra command-line tools. In PHP world it could be a mess detector or program to check the code style. The framework you use also exposes some functionalities to clear cache, migrate database or generate documentation. All these commands are helpful but you need to look for them until you memorize the most useful ones.&lt;/p&gt;
&lt;p&gt;Sometimes you need to perform a &lt;strong&gt;task&lt;/strong&gt;, like project initialization or restoring a stable snapshot of the database. It&amp;rsquo;s rarely an atomic operation, so you need to execute a few commands in a specific order.&lt;/p&gt;
&lt;p&gt;This is a place where a &lt;strong&gt;task runner&lt;/strong&gt; comes to play. Take a look at how a program called &lt;strong&gt;make&lt;/strong&gt; can help you organize common tasks in your project.&lt;/p&gt;
&lt;h2 id=&#34;why-make&#34;&gt;Why make?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.gnu.org/software/make/&#34;&gt;&lt;strong&gt;Make&lt;/strong&gt;&lt;/a&gt; is a tool to generate files from other files, e.g. executables from the source code. But it can also perform other operations like running other tools and commands.&lt;/p&gt;
&lt;p&gt;Unlike &lt;strong&gt;gulp&lt;/strong&gt; or &lt;strong&gt;grunt&lt;/strong&gt;, &lt;strong&gt;make&lt;/strong&gt; doesn&amp;rsquo;t need any special JavaScript&amp;rsquo;s runtime or plugins. If you can describe your task using a set of shell commands, you may use them as your task in &lt;strong&gt;make&lt;/strong&gt; as well.&lt;/p&gt;
&lt;h2 id=&#34;install-make&#34;&gt;Install make&lt;/h2&gt;
&lt;h3 id=&#34;macos&#34;&gt;macOS&lt;/h3&gt;
&lt;p&gt;If you use a macOS, you may install &lt;strong&gt;make&lt;/strong&gt; using &lt;a href=&#34;https://brew.sh&#34;&gt;homebrew&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ brew install make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;debian-ubuntu&#34;&gt;Debian, Ubuntu&lt;/h3&gt;
&lt;p&gt;If you use a Debian-like system, it&amp;rsquo;s likely that you already have &lt;strong&gt;make&lt;/strong&gt;. If not, you can install it from the repository:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo apt install build-essential
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;windows&#34;&gt;Windows&lt;/h3&gt;
&lt;p&gt;I have no experience with &lt;strong&gt;make&lt;/strong&gt; on a Windows machine, but according to &lt;a href=&#34;https://stackoverflow.com/a/54086635&#34;&gt;this answer from StackOverflow&lt;/a&gt;, you can install &lt;strong&gt;make&lt;/strong&gt; using &lt;a href=&#34;https://chocolatey.org/&#34;&gt;chocolatey package manager&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; choco install make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;define-your-first-task&#34;&gt;Define your first task&lt;/h2&gt;
&lt;p&gt;To define your first task, create a file called &lt;em&gt;Makefile&lt;/em&gt; with the following content. Please be aware, &lt;em&gt;Makefile&lt;/em&gt; uses &lt;em&gt;tabs&lt;/em&gt; as indentation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-makefile&#34; data-lang=&#34;makefile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s2&#34;&gt;&amp;#34;Initialize project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s2&#34;&gt;&amp;#34;End of initialization&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, run the task by executing &lt;code&gt;make init&lt;/code&gt;. The output should be like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ make init
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Initialize project
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;End of initialization
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;em&gt;Makefile&lt;/em&gt; is a bag for your tasks. Each task (called &lt;em&gt;target&lt;/em&gt;) consists of commands that can be executed through the shell.&lt;/p&gt;
&lt;p&gt;Because it&amp;rsquo;s a file, you can store and version it together with your project files in the VCS. Any team member may define other tasks to build up a set of tools and procedures for the whole team.&lt;/p&gt;
&lt;p&gt;Thanks to the &lt;code&gt;@&lt;/code&gt; symbol at the beginning command, make displays output without printing the executed command. Whether it is useful or not depends on your use-case.&lt;/p&gt;
&lt;p&gt;The extra line containing &lt;code&gt;.PHONY: init&lt;/code&gt; means, that task doesn&amp;rsquo;t produce any file. It would be problematic if you have a file with the same name as the &lt;em&gt;make target&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;make-make-more-useful&#34;&gt;Make &lt;em&gt;Make&lt;/em&gt; more useful&lt;/h2&gt;
&lt;p&gt;What is in your &lt;em&gt;Makefile&lt;/em&gt; depends on your needs. Think about the goal of each task. Don&amp;rsquo;t put everything you suppose you will need. You may consider adding a new task if it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;adds any value for you and your team&lt;/li&gt;
&lt;li&gt;speeds up your work&lt;/li&gt;
&lt;li&gt;simplifies any complex operation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are a few examples of tasks from my &lt;em&gt;Makefile&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-makefile&#34; data-lang=&#34;makefile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Initialize the application
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    docker-compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; app bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;composer install -n -o --dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    make init-db
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Go to the shell inside app container
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;jump&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @docker-compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; app bash
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;jump&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Fix code style issues using phpcbf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;cbf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ./vendor/bin/phpcbf --extensions&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;php --standard&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;phpcs.xml -p src/ --warning-severity&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;9&lt;/span&gt; --cache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cbf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Show code style issues using phpcs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;cs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ./vendor/bin/phpcs --extensions&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;php --standard&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;phpcs.xml -p src/ --warning-severity&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;9&lt;/span&gt; --cache
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Migrate database
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;migrate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @docker-compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; app bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;php ./bin/console doctrine:migrations:migrate&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;migrate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Initialize database    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init-db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @docker-compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; db mysql -u root -proot -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;DROP DATABASE IF EXISTS app_db&amp;#34;&lt;/span&gt; &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @docker-compose &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; db mysql -u root -proot -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;CREATE DATABASE app_db CHARACTER SET utf8 COLLATE utf8_unicode_ci&amp;#34;&lt;/span&gt; &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @make migrate
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;.PHONY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;-&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Creating a good &lt;em&gt;Makefile&lt;/em&gt; is more like a process rather than a single action. &lt;strong&gt;Trying to define everything in advance is unproductive and may lead to creating many tasks you will rarely use&lt;/strong&gt;. Start with something small and improve in time.&lt;/p&gt;
&lt;h2 id=&#34;let-make-document-itself&#34;&gt;Let &lt;em&gt;Make&lt;/em&gt; document itself&lt;/h2&gt;
&lt;p&gt;Since it&amp;rsquo;s worth to have a distinct name of the task, the goal of task runner is to save your time and memory. Instead of creating tasks with long names, you can define another task that displays the description of the task alongside its name.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-makefile&#34; data-lang=&#34;makefile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GREEN&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell tput -Txterm setaf 2&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;YELLOW&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell tput -Txterm setaf 3&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;WHITE&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell tput -Txterm setaf 7&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;RESET&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;shell tput -Txterm sgr0&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TARGET_MAX_CHAR_NUM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;.DEFAULT_GOAL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;## Show this help message
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;Usage:&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;  ${YELLOW}make${RESET} ${GREEN}&amp;lt;target&amp;gt;${RESET}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @echo &lt;span class=&#34;s1&#34;&gt;&amp;#39;Targets:&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;/^[a-zA-Z\-\_0-9]+:/ { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;        helpMessage = match(lastLine, /^## (.*)/); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;        if (helpMessage) { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;            helpCommand = substr($$1, 0, index($$1, &amp;#34;:&amp;#34;)); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;            sub(/:/, &amp;#34;&amp;#34;, helpCommand); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;            helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;            printf &amp;#34;  ${YELLOW}%-$(TARGET_MAX_CHAR_NUM)s${RESET} ${GREEN}%s${RESET}\n&amp;#34;, helpCommand, helpMessage; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;        } \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    } \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;    { lastLine = $$0 }&amp;#39;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;MAKEFILE_LIST&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://gist.github.com/rcmachado/af3db315e31383502660&#34;&gt;Here&amp;rsquo;s a link to the source code&lt;/a&gt;. You may find other implementations on GitHub or StackOverflow.&lt;/p&gt;
&lt;p&gt;Using my &lt;em&gt;Makefile&lt;/em&gt;, it produces output like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ make
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  make &amp;lt;target&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Targets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt;            Show this &lt;span class=&#34;nb&#34;&gt;help&lt;/span&gt; message
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  init            Initialize the application
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  jump            Go to the shell inside app container
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  cbf             Fix code style issues using phpcbf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  cs              Show code style issues using phpcs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  migrate         Migrate database
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  init-db         Initialize database
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which looks like&lt;/p&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/use-make-as-task-runner/040_iterm_makefile_help.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/use-make-as-task-runner/040_iterm_makefile_help_hu_434a13aef49cb05a.png 360w, https://szymonkrajewski.pl/use-make-as-task-runner/040_iterm_makefile_help_hu_baebebcc9d807849.png 640w, https://szymonkrajewski.pl/use-make-as-task-runner/040_iterm_makefile_help_hu_3a526db87ab4a5b9.png 710w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;710&#34;
          height=&#34;488&#34;
           alt=&#34;iterm2 window that displays the help target&amp;#39;s output&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Self-documented Makefile – each task has the description
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, the help message is displaying by default if you provide no arguments. It&amp;rsquo;s configured by setting &lt;code&gt;.DEFAULT_GOAL := help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If tasks&amp;rsquo; names are longer than 15 characters, you can adjust the &lt;code&gt;TARGET_MAX_CHAR_NUM&lt;/code&gt; variable.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Make&lt;/strong&gt; is not the only tool in this area. Aforementioned &lt;strong&gt;gulp&lt;/strong&gt; and &lt;strong&gt;grunt&lt;/strong&gt; are also good but they need some extra resources and plugins. If you already work on JavaScript project, you can consider using either. Even so, it&amp;rsquo;s more likely that you already use a tool like &lt;strong&gt;webpack&lt;/strong&gt; to bundle your application.&lt;/p&gt;
&lt;p&gt;Using a task runner is great to &lt;em&gt;hide&lt;/em&gt; complex operations behind simple commands. However, it&amp;rsquo;s also worth to know, what specific task does under the hood. The goal is to save time, not to reduce the necessary knowledge. In case of any error, who will be able to fix a bug, if the last modification to &lt;em&gt;Makefile&lt;/em&gt; has done one year ago by a person who isn&amp;rsquo;t in the project anymore?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/@blackout095?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;sergey Svechnikov&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>5 stages of development as the programmer</title>
      <link>https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/</link>
      <pubDate>Mon, 27 May 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/</guid>
      <description>&lt;p&gt;5 years have passed since I had started working as a professional software developer. Although the programming was my hobby since the early years, my skill started to grow when I had taken a real job.&lt;/p&gt;
&lt;p&gt;I was surprised when I had recalled my very beginning because I noticed how much I had changed and how big progress I had taken. So, I decided to write it down.&lt;/p&gt;
&lt;p&gt;In this article, I&amp;rsquo;ll take you on a short journey where I&amp;rsquo;ll tell you about my development process. You&amp;rsquo;ll find out what I&amp;rsquo;ve been thinking about, what I&amp;rsquo;ve been working on and ultimately what changed from my perspective. Ready?&lt;/p&gt;
&lt;h2 id=&#34;junior-developer-writes-working-code&#34;&gt;Junior developer writes working code&lt;/h2&gt;
&lt;p&gt;Everybody, who started programming knows, that the first of most enjoyable moments is when a program starts to work. It doesn&amp;rsquo;t matter it writes a static text on the screen, shows a message after button pressing or does a less ambitious job. It works.&lt;/p&gt;
&lt;p&gt;At this stage, nobody thinks about architecture. At least I didn&amp;rsquo;t think. I was fascinated by what I can program yet. Bots to various games, simple website or even more advanced CMS systems. The more experimental projects, the better.&lt;/p&gt;
&lt;p&gt;Over time, more advanced projects appear, and with them the first major problems. Problems with the code structure, duplication, readability, and other mess, that lead to increasing frustration. It takes a lot of time to introduce changes and the outcome is rarely satisfactory.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;img
      loading=&#34;lazy&#34;
      decoding=&#34;async&#34;
      src=&#34;https://media.giphy.com/media/xZqycRHIQkKNa/giphy.gif&#34;
      
      
      
    /&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Source: &lt;a href=&#34;https://giphy.com/gifs/13XW2MJE0XCoM0&#34;&gt;giphy.com&lt;/a&gt;
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The lack of knowledge about basic patterns can stop even the most ambitious project. The code becomes unmanageable. In the outcome, it&amp;rsquo;s easier to discard everything and start writing from scratch. Unfortunately, with the same effect.&lt;/p&gt;
&lt;p&gt;Some smart people and interesting books have suggested what can I do with it. Making use of rules such as &lt;em&gt;SOLID&lt;/em&gt;, &lt;em&gt;DRY&lt;/em&gt;, and &lt;em&gt;KISS&lt;/em&gt; was the first suggestion I read – I don&amp;rsquo;t even know where. It sounds great in theory. However, these rules are problematic even for more advanced programmers, let alone beginners.&lt;/p&gt;
&lt;p&gt;The solution seems to be the use of design patterns. They will be my basic tool at work soon.&lt;/p&gt;
&lt;h2 id=&#34;the-use-of-design-patterns&#34;&gt;The use of design patterns&lt;/h2&gt;
&lt;p&gt;During discovering the programming world, I figured out, that most of the problems I had struggled with had been already solved. Some of them were generalized enough to fit into known design patterns, e.g. those proposed in &lt;a href=&#34;https://en.wikipedia.org/wiki/Design_Patterns&#34;&gt;the book of a gang of four&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The whole idea behind design patterns was encouraging. They provide battle-tested solutions for specific problems. For me, as a beginner programmer, it was a way to put a piece of code in a good place in a good way.&lt;/p&gt;
&lt;h3 id=&#34;overuse-of-design-patterns&#34;&gt;Overuse of design patterns&lt;/h3&gt;
&lt;p&gt;Design patterns were such a great tool, that it seemed almost a mistake to write the code without using them. In the outcome, after some time I saw the possibility of using a design pattern everywhere. Even where it was not needed.&lt;/p&gt;
&lt;p&gt;At first, having a generalized solution to every problem seemed like a good idea. Most often, however, the problem became a cluster of abstractions. What he did was deeply hidden in the code.&lt;/p&gt;
&lt;p&gt;Now I know that the design pattern is some kind of framework to solve the problem, but it is not a ready solution in itself.&lt;/p&gt;
&lt;p&gt;In this time, I&amp;rsquo;ve been writing most of the solutions by myself. I didn&amp;rsquo;t want to use ready-made solutions such as frameworks or libraries. On the one hand, it was good because I learned and understood a lot of things when I had to implement them. On the other hand, it required much more time, but the results were rarely satisfactory.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The design pattern is a generalization of the solution, rather than the solution itself. It provides some kind of framework to solve a specific problem.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When I got bored with writing the same things over and over again, my attention switched towards ready-made solutions.&lt;/p&gt;
&lt;h2 id=&#34;if-there-is-a-problem-there-is-also-a-library-that-solves-it&#34;&gt;If there is a problem, there is also a library that solves it&lt;/h2&gt;
&lt;p&gt;I joined in the framework-world quickly. They have added extra restrictions, especially when it comes to the code writing. In the result, they made the work more structured. Frameworks also have had a lot of components that increase the speed of development.&lt;/p&gt;
&lt;p&gt;If there is a problem, it&amp;rsquo;s very likely that there is also a library that solves it. I got used to this approach very fast.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers_hu_c5e1f8984e0c92c.png 360w, https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers_hu_34d56ed9e75dba0e.png 640w, https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers_hu_a0ffd876ae6d1386.png 960w, https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers_hu_e2414677a15f777e.png 1200w, https://szymonkrajewski.pl/5-stages-of-development-as-the-programmer/032_packagist_package_numbers_hu_3d569590aa8766ea.png 1526w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1526&#34;
          height=&#34;704&#34;
           alt=&#34;The chart which presents number of packages and their versions in time&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      This chart shows growth of quantity of libraries and their versions in the packagist.org registry. There is above 225 thousands of libraries in May 2019.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&#34;focus-on-ready-made-libraries-instead-of-solutions&#34;&gt;Focus on ready-made libraries instead of solutions&lt;/h3&gt;
&lt;p&gt;The confidence that there is a library for every problem got me into troubles. Instead of focusing on solving problems, I spent a lot of time looking for a library which would solve it for me. I usually found what I had been looking for and finally, I solved the problem. It doesn&amp;rsquo;t mean that everything was seamless. Most of the time, third-party code put extra restrictions, which turned into other problems, especially in some more specific business cases.&lt;/p&gt;
&lt;p&gt;After some time I started looking very carefully to each library I would incorporate into my project. If I need to spend extra time to adjust functionality to requirements, maybe it&amp;rsquo;s worth to write it from scratch? In time, more question emerged, e.g. if I use another library, will I have fewer or more problems? Does it solve my problem?&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;img
      loading=&#34;lazy&#34;
      decoding=&#34;async&#34;
      src=&#34;https://media.giphy.com/media/kwEmwFUWO5Ety/giphy.gif&#34;
      
      
      
    /&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Adjusting ready-made solutions according to client&amp;rsquo;s requirements. Source: &lt;a href=&#34;https://giphy.com/gifs/modern-software-development-kwEmwFUWO5Ety&#34;&gt;giphy.com&lt;/a&gt;
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;problem-solving&#34;&gt;Problem solving&lt;/h2&gt;
&lt;p&gt;At some point in my career, I understood, that solving problems is not about writing software at all costs. Some classes of problems could be solved by existing software. The key is to know what could be used.&lt;/p&gt;
&lt;h3 id=&#34;listening-to-the-clients-needs&#34;&gt;Listening to the client&amp;rsquo;s needs&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ll give you an example. Let&amp;rsquo;s assume that you receive a request about making an application to generate summaries and reports from the company. The client, however, has a very limited budget. You&amp;rsquo;re not so interested in this cooperation but for some reason, the client reached you or your company.&lt;/p&gt;
&lt;p&gt;This should lead you to a few questions, that you should answer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why did the client get to me? Do I aim right customer group?&lt;/li&gt;
&lt;li&gt;Since this client has already asked me, how can I help him?
&lt;ul&gt;
&lt;li&gt;Does the client need a dedicated software?&lt;/li&gt;
&lt;li&gt;If he doesn&amp;rsquo;t need dedicated software, is any other tool that I could offer him instead? E.g. Microsoft Excel, Calc from Libre Office or any other spreadsheet?&lt;/li&gt;
&lt;li&gt;Could I recommend the client to another company specialized in problems that the client needs to resolve?&lt;/li&gt;
&lt;li&gt;Could I do anything for the client in his budget?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, not everything has to come down to writing code. If it does, it should take care of solving an actual client&amp;rsquo;s problem to meet his needs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Architecture, paradigm, language, framework are only tools, which have the one important task. They are to help in solving a domain problem.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You&amp;rsquo;ve noticed, that I write lots about problems. I don&amp;rsquo;t mean the challenges we set for ourselves as programmers by choosing architecture, paradigm or framework. They are our tools, that are designed to help solve the higher level problem, usually a business problem or more generally – a domain problem.&lt;/p&gt;
&lt;h3 id=&#34;solving-domain-problems&#34;&gt;Solving domain problems&lt;/h3&gt;
&lt;p&gt;Domain problems are the most interesting because they need more than programming knowledge. You need also a specialized domain knowledge. They&amp;rsquo;re real challenges, that require from the programmer constant improvement and willingness to learn new things, which are not necessarily related to the IT world.&lt;/p&gt;
&lt;p&gt;There is where the famous DDD acronym – Domain-Driven Design – appears. It is a holy Graal to every programmer aspiring to be an architect or a domain designer. It&amp;rsquo;s a place, where developers know what the business expects and, at the same time, the business feels that developers understand its needs.&lt;/p&gt;
&lt;p&gt;To make it possible, you should discard all things that introduce unnecessary noise. You may use them only where it&amp;rsquo;s necessary to solve the problem. You should simplify complicated things, and you should make simple things even simpler.&lt;/p&gt;
&lt;p&gt;The favoring of simplicity drives to the situation, where a developer starts to write a code that just works. And it&amp;rsquo;s simple.&lt;/p&gt;
&lt;h2 id=&#34;senior-developer-writes-working-code&#34;&gt;Senior developer writes working code&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m not looking for projects which I could do in my favorite framework or with the usage of currently top-fashioned architecture. I don&amp;rsquo;t try to replace each loop by an iterator and I don&amp;rsquo;t try to complicate things that can be simple.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t reinvent the wheel and I use tools which are designed to solve specific problems. Each written line of code is something that needs maintaining in the future.&lt;/p&gt;
&lt;p&gt;Instead, I help others in solving their problems. I try to understand what they need and I suggest the best solution to a specific problem and situation.&lt;/p&gt;
&lt;p&gt;Moreover, I also take advantage of the generosity of other programmers who share their knowledge and work. I try to do the same by showing how to write good code and by sharing my knowledge and experience as well.&lt;/p&gt;
&lt;p&gt;When I write code, I know, that I&amp;rsquo;m not the only one who will be reading it. Many eyes will be reading and trying to understand how given functionality was implemented and what it does. My job is to help others understand this.&lt;/p&gt;
&lt;p&gt;All these things combined with my experience are my workshop. Thanks to which I can effectively solve further problems, constantly improving myself. Every day I get to know new things and I decide, which of them I can include in my workshop.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I share my thoughts because I want to show you how at different stages of my life and career I have changed my perspective and my way of thinking.&lt;/p&gt;
&lt;p&gt;Once I tried to write as much as I could by myself. Another time I was looking for ready-made solutions. Then I tried hard to use what I had learned. It took me years to learn how to do it well.&lt;/p&gt;
&lt;p&gt;As you can see, a lot of things changed – in my opinion for the better. Changes are visible consequences and proof of making progress. However, they are not the result only of the passing time.&lt;/p&gt;
&lt;p&gt;Changing a job, other people, maintaining healthy relationships, environment, &lt;a href=&#34;https://szymonkrajewski.pl/ive-finished-studies-here-are-some-of-my-thoughts/&#34;&gt;study&lt;/a&gt;, and books. They are some of the most important elements that have influenced my way of thinking. That&amp;rsquo;s why is so important to open yourself to new experiences and confront your thoughts with new ideas.&lt;/p&gt;
&lt;p&gt;What I have described here is my journey, which doesn&amp;rsquo;t necessarily coincide with yours and probably will not. However, if you somehow identify with what I have presented, please tell me about it by leaving a comment or by e-mail.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t the end of the journey.&lt;/p&gt;
&lt;p&gt;Before we say goodbye, I would like to leave you with one thought – What is the most important change you see in yourself, in comparison to yourself from a year ago?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/xrVDYZRGdw4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Émile Perron&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/developer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Automatic file versioning after change using fswatch and git</title>
      <link>https://szymonkrajewski.pl/automatic-file-versioning-after-change-using-fswatch-and-git/</link>
      <pubDate>Mon, 01 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/automatic-file-versioning-after-change-using-fswatch-and-git/</guid>
      <description>&lt;p&gt;I create a lot of notes. Seriously, I write tons of notes. Inspired by &lt;em&gt;Getting Things Done&lt;/em&gt; method, I treat my mind as a thoughts generator rather than the storage. I capture thoughts, ideas, inspiring quotes, links, and pictures. Mostly using files.&lt;/p&gt;
&lt;p&gt;Although I&amp;rsquo;m on the early stage of creating my custom note-taking solution, I&amp;rsquo;ve done some work to synchronize notes between devices and versioning them. In this article, I&amp;rsquo;d like to focus on the latter and I&amp;rsquo;m going to show you how to set up automatic files versioning.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a software developer so, unsurprisingly, I use &lt;strong&gt;git&lt;/strong&gt; to versioning my notes as well. Instead of manually committing changes, I take advantage of tools like &lt;strong&gt;fswatch&lt;/strong&gt; and &lt;strong&gt;launchd&lt;/strong&gt; to automate this process.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;My notebook (&lt;em&gt;if it&amp;rsquo;s a proper name for a directory with files&lt;/em&gt;) consists of simple text and Markdown files as well as attachments to them like pictures, archives, records, etc. The internal structure doesn&amp;rsquo;t matter.&lt;/p&gt;
&lt;p&gt;Keeping notes as regular files instead of dedicated Notebooks (like &lt;em&gt;Evernote&lt;/em&gt; or &lt;em&gt;OneNote&lt;/em&gt;) let me use any tool I need at the moment or I really like. It has also some drawbacks – most of the features provided by dedicated software are missing, including the synchronization and versioning of my notes.&lt;/p&gt;
&lt;p&gt;I decide to use &lt;strong&gt;git&lt;/strong&gt; to keep track of changes in files. Moreover, I want to automate this process. I don&amp;rsquo;t want to remember to commit each change in the note, so I delegate this operation to my OS.&lt;/p&gt;
&lt;h2 id=&#34;initial-steps&#34;&gt;Initial steps&lt;/h2&gt;
&lt;h3 id=&#34;prepare-repository&#34;&gt;Prepare repository&lt;/h3&gt;
&lt;p&gt;Make sure that you have &lt;strong&gt;git&lt;/strong&gt; installed on your OS. I use macOS, the fastest way to install any software is using &lt;em&gt;&lt;a href=&#34;https://brew.sh/&#34;&gt;homebrew&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a dedicated directory for your notes/files and put them here. I store my versioned notebook under the path &lt;code&gt;/Users/szymon/Documents/Notes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s time to initialize a new repository. Using terminal, go to the newly created directory and run &lt;code&gt;git init&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /Users/szymon/Documents/Notes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The repository is ready. It&amp;rsquo;s time to configure watcher to receive information about changes in files.&lt;/p&gt;
&lt;h3 id=&#34;install-fswatch&#34;&gt;Install &lt;em&gt;fswatch&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Actually, we don&amp;rsquo;t need to know what exactly has changed (it will be covered by git). We need to know only that change occurred in our repository.&lt;/p&gt;
&lt;p&gt;I choose &lt;strong&gt;fswatch&lt;/strong&gt; as a tool to observe changes in the directory. It&amp;rsquo;s cross-platform, works well on macOS and is able to &lt;strong&gt;check directories recursively&lt;/strong&gt;. &lt;em&gt;Launchd&lt;/em&gt;, however, is also able to watch for changes in the directory but it doesn&amp;rsquo;t track nested directories.&lt;/p&gt;
&lt;p&gt;You can install &lt;em&gt;fswatch&lt;/em&gt; using &lt;em&gt;brew&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install fswatch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it&amp;rsquo;s done, we can go to connecting these tools.&lt;/p&gt;
&lt;h2 id=&#34;prepare-scripts-to-operate&#34;&gt;Prepare scripts to operate&lt;/h2&gt;
&lt;p&gt;One of the UNIX philosophy sounds &amp;ldquo;&lt;em&gt;Make each program do one thing well&lt;/em&gt;&amp;rdquo;. Actually, UNIX-like systems are full of programs which do one thing: &lt;em&gt;cat&lt;/em&gt;, &lt;em&gt;grep&lt;/em&gt;, &lt;em&gt;tail&lt;/em&gt;, &lt;em&gt;head&lt;/em&gt;, &lt;em&gt;xargs&lt;/em&gt;. The true power lies in the possibilities of connecting all these little programs together.&lt;/p&gt;
&lt;p&gt;Let me describe what we need to achieve. For each change in the &lt;em&gt;directory&lt;/em&gt; we need to add changed files to stage and commit them. So, we have basically 2 different operations which are connected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) Watch the &lt;em&gt;directory&lt;/em&gt; and run &lt;em&gt;command&lt;/em&gt; for each change&lt;/li&gt;
&lt;li&gt;(2) Stage and commit all files in the repository with &lt;em&gt;message&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating these 2 scripts separately gives us more possibilities. We can test them easily, we can replace one of the scripts with another one, we can reuse them in a completely different scenario, etc.&lt;/p&gt;
&lt;h3 id=&#34;add-all-to-the-stage-and-commit&#34;&gt;Add all to the stage and commit&lt;/h3&gt;
&lt;p&gt;First, let&amp;rsquo;s create the script to stage and commit all changes in the repository (2). I usually put scripts like this in &lt;code&gt;~/.scripts&lt;/code&gt; directory. You can create this file using the command &lt;code&gt;touch ~/.scripts/commit-all.sh&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git commit -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;Changed: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also add &lt;code&gt;git push&lt;/code&gt; command to push changes to the remote repository.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re more advanced &lt;em&gt;git&lt;/em&gt; user, you probably know, that &lt;em&gt;git&lt;/em&gt; allows to pass option -a to commit command to stage modified files. Unfortunately, it ignores new files which are not tracked yet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-a, -all Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;$1&lt;/code&gt; is an argument to the script and it&amp;rsquo;s used in the commit message. Make this script executable by command &lt;code&gt;chmod +x ~/.scripts/commit-all.sh&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;run-command-to-observe-changes&#34;&gt;Run command to observe changes&lt;/h3&gt;
&lt;p&gt;The second script runs &lt;em&gt;fswatch&lt;/em&gt; with a set of options and passes the name of the changed file to the further execution. Start with creating script &lt;code&gt;touch ~/.scripts/run-on-change.sh&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/local/bin/fswatch -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;.git&amp;#34;&lt;/span&gt; -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*\.swp&amp;#34;&lt;/span&gt; -e &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*~&amp;#34;&lt;/span&gt; -0 . &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; xargs -0 -n &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; -I &lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt; basename &lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Similarly, we need to make this script executable by command &lt;code&gt;chmod +x ~/.scripts/run-on-change.sh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To prevent watching specific pattern I exclude them using the &lt;code&gt;-e&lt;/code&gt; option. It&amp;rsquo;s important to &lt;strong&gt;exclude the .git directory&lt;/strong&gt; – out intention is to automatically commit all detected changes. We don&amp;rsquo;t want to commit anything after changing something in the repository.&lt;/p&gt;
&lt;h3 id=&#34;combine-scripts&#34;&gt;Combine scripts&lt;/h3&gt;
&lt;p&gt;We have a simple script which detects changes in the current working directory and another one which adds and commits all files to the repository. Let&amp;rsquo;s combine them together.&lt;/p&gt;
&lt;p&gt;In your repository, run following command&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~/.scripts/watch-and-commit.sh &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; xargs -I&lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt;  ~/.scripts/commit-all.sh &lt;span class=&#34;o&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, if we change or add a file to the directory, it would be immediately committed. Moreover, the commit message would contain the name of the changed file.&lt;/p&gt;
&lt;h2 id=&#34;schedule-execution-using-launchd&#34;&gt;Schedule execution using &lt;em&gt;launchd&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s not the first time when I use &lt;strong&gt;launchd&lt;/strong&gt; as a job scheduler instead of a &lt;em&gt;cron&lt;/em&gt; service. I briefly wrote about it in my article about &lt;a href=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/#schedule-execution&#34;&gt;synchronizing local files with encrypted vault&lt;/a&gt;. Basically, &lt;em&gt;launchd&lt;/em&gt; is a recommended solution for macOS for job scheduling.&lt;/p&gt;
&lt;p&gt;Because I use macOS on a daily basis, I focus mostly on this platform, however, we can reach the same goal using a tool called &lt;strong&gt;&lt;a href=&#34;http://supervisord.org/&#34;&gt;supervisord&lt;/a&gt;&lt;/strong&gt;. It&amp;rsquo;s my first choice when I will configure this flow server-side or on Linux-powered machines.&lt;/p&gt;
&lt;p&gt;I created a &lt;em&gt;pl.skrajewski.notes.autocommit.plist&lt;/em&gt; file in directory &lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt; which is one of the locations for &lt;em&gt;agents&lt;/em&gt; which work on behalf of the user.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;pl.skrajewski.notes.autocommit&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;bash&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-c&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;~/.scripts/watch-and-commit.sh | xargs -I{}  ~/.scripts/commit-all.sh {}&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WorkingDirectory&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/szymon/Documents/Notes&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/pl.skrajewski.notes.autocommit.out.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/pl.skrajewski.notes.autocommit.error.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In short: &lt;code&gt;Label&lt;/code&gt; is an identifier of the agent. System runs the command specified in &lt;code&gt;ProgramArguments&lt;/code&gt; in &lt;code&gt;WorkingDirectory&lt;/code&gt; and redirects &lt;em&gt;stdout&lt;/em&gt; and &lt;em&gt;stderr&lt;/em&gt; to files defined as &lt;code&gt;StandardOutPath&lt;/code&gt; and &lt;code&gt;StandardErrorPath&lt;/code&gt;. The option &lt;code&gt;KeepAlive&lt;/code&gt; means, that &lt;em&gt;launchd&lt;/em&gt; will restart the job each time it goes down.&lt;/p&gt;
&lt;p&gt;The job will start once is loaded. You can do this using &lt;strong&gt;launchctl&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;launchctl load ~/Library/LaunchAgents/pl.skrajewski.notes.autocommit.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If everything is set up properly, each change in directory produces new commit. If not, we can use log and error file to investigate what goes wrong.&lt;/p&gt;
&lt;h3 id=&#34;weak-points-of-this-solution&#34;&gt;Weak points of this solution&lt;/h3&gt;
&lt;p&gt;This solution is far from ideal and has some weak points.&lt;/p&gt;
&lt;p&gt;A proposed watcher, &lt;em&gt;fswatch&lt;/em&gt; doesn&amp;rsquo;t observe changes in &lt;em&gt;.git&lt;/em&gt; directory due to exclusion policy. However, if we checkout past commit, it will be treated as a change, so the script will be trying to commit changes. If you want to restore the previous version of files, make sure that the agent is not running. You can &lt;em&gt;unload&lt;/em&gt; it using the following command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;launchctl unload ~/Library/LaunchAgents/pl.skrajewski.notes.autocommit.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nevertheless, it&amp;rsquo;s better to change the script a bit to avoid committing if e.g. HEAD is from &lt;em&gt;master branch&lt;/em&gt;. It&amp;rsquo;s a field to improve.&lt;/p&gt;
&lt;p&gt;The problems may come also from the editor you use. Some of the editors store temporary files next to original files and they may trigger auto-commit each time when the content of file change. You&amp;rsquo;ll have to exclude these files in the &lt;em&gt;fswatch&lt;/em&gt; invocation.&lt;/p&gt;
&lt;p&gt;Additionally, a lot of editors have auto-save functionality, creating in outcome lots of commits with relatively small differences. Instead of committing changes immediately, we can delay this operation a bit. If something change before the commit, the script will cancel the previous invocation and it&amp;rsquo;ll schedule the commit with new changes.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Although the whole process sounds like reinventing the wheel, I hope that you found out something interesting for you. You can treat this article like a proof-of-concept or a presentation of the idea rather than the complete recipe.&lt;/p&gt;
&lt;p&gt;I used to write software which &lt;em&gt;does something&lt;/em&gt; I needed. Sometimes, I spent days or weeks before I got something useful. Recently, I&amp;rsquo;ve discovered the real power of combining small and simple tools, mostly provided by my operating system. It &lt;em&gt;just&lt;/em&gt; works.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t mean that writing software is &lt;em&gt;bad&lt;/em&gt;. In my opinion, writing is the best way to learn. But the main goal of software should be to &lt;strong&gt;solve a problem&lt;/strong&gt;. If the problem can be solved using tools we already have, why not use them? At least until we want to &lt;em&gt;generalize&lt;/em&gt; the use case to be useful for others.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://pixabay.com/users/myrfa-3126475/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1614223&#34;&gt;Ag Ku&lt;/a&gt; from &lt;a href=&#34;https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1614223&#34;&gt;Pixabay&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>How to use final in PHP?</title>
      <link>https://szymonkrajewski.pl/how-to-use-final-in-php/</link>
      <pubDate>Mon, 18 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/how-to-use-final-in-php/</guid>
      <description>&lt;p&gt;If someone would ask me about my favorite keyword in PHP, I would certainly answer: &lt;em&gt;final&lt;/em&gt;. It doesn&amp;rsquo;t mean I write this modifier in each class or method. It not only shows the intention but also provides a mechanism to protect the code. At least from the assumption.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;final&lt;/em&gt; keyword can be used both on the class level and on the method level. It prevents future extension of functionalities in a non-effective way. If a class is marked as &lt;em&gt;final&lt;/em&gt;, then we can&amp;rsquo;t inherit from it. If a method is marked as &lt;em&gt;final&lt;/em&gt;, we can&amp;rsquo;t override it.&lt;/p&gt;
&lt;p&gt;The theory sounds good. Let&amp;rsquo;s go to details.&lt;/p&gt;
&lt;h2 id=&#34;the-intention-behind-final&#34;&gt;The intention behind &lt;em&gt;final&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;In object-oriented programming, you can extend the behavior of the class by &lt;strong&gt;inheritance&lt;/strong&gt;. It&amp;rsquo;s simple and convenient.&lt;/p&gt;
&lt;p&gt;Do you remember the example classes &lt;em&gt;Figure&lt;/em&gt; and its children: &lt;em&gt;Square, Rectangle, Polygon&lt;/em&gt;, etc.? It&amp;rsquo;s a classic example of inheritance and polymorphism in work. In this case, you probably don&amp;rsquo;t want to finalize &lt;em&gt;Figure&lt;/em&gt; class.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;final&lt;/em&gt; keyword added to the &lt;em&gt;Figure&lt;/em&gt; class puts the extra restriction on the subject, so inheritance is &lt;strong&gt;no longer possible&lt;/strong&gt;. So why does this mechanism even exist? Who would like to limit the usability of own solutions?&lt;/p&gt;
&lt;p&gt;But finalizing &lt;strong&gt;is not the same&lt;/strong&gt; as &lt;em&gt;limiting&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Imagine the hypothetical class &lt;em&gt;ArticleProvider&lt;/em&gt; whose only one public method &lt;code&gt;getAll()&lt;/code&gt; is responsible for returning collection of articles, e.g. from my blog. Moreover, let&amp;rsquo;s say, that this class fulfills the interface called &lt;em&gt;ArticleProviderInterface&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you want to operate over my articles, you have two possible ways. You can extend the class &lt;em&gt;ArticleProvider&lt;/em&gt; using inheritance or you can use the class, e.g. using composition. Both ways lead to a proper solution, but because I&amp;rsquo;m the author of the class, I would like to encourage you to choose the safer option – &lt;em&gt;composition&lt;/em&gt;. It prevents creating a &lt;strong&gt;chain of inheritance&lt;/strong&gt; and preserves the &lt;strong&gt;encapsulation&lt;/strong&gt; of behavior and state in class.&lt;/p&gt;
&lt;p&gt;Since there is no special reason why someone would need to extend &lt;em&gt;ArticleProvider&lt;/em&gt;, I decided to mark them as &lt;strong&gt;&lt;em&gt;final&lt;/em&gt;&lt;/strong&gt;. It means the class is ready to use as one of the implementations of the interface &lt;em&gt;ArticleProviderInterface&lt;/em&gt;. Other developers may utilize only the public API of this class.&lt;/p&gt;
&lt;h3 id=&#34;final-as-the-synonym-of-ready&#34;&gt;Final as the synonym of &amp;ldquo;ready&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;An interface is a contract between developers and people who will be using the class so they can be sure that the behavior of methods specified by interface will not change.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Final&lt;/em&gt;, however, is a &lt;strong&gt;contract between developers&lt;/strong&gt; to make sure, that the implementation of the specific class &lt;strong&gt;will not change in the future&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The class marked as &lt;em&gt;final&lt;/em&gt; means, that the &lt;strong&gt;specific implementation of the interface is final&lt;/strong&gt;. In other words, it&amp;rsquo;s complete in the sense, that there is no need to add any other functionalities there.&lt;/p&gt;
&lt;p&gt;It looks like a good moment for an example. I&amp;rsquo;m going to create a simple &lt;em&gt;mailer&lt;/em&gt;, so let&amp;rsquo;s define an interface for it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MailerInterface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Message&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I need at least two implementations of this interface: the first one should actually send an email. For this example, we can use a PHP&amp;rsquo;s builtin &lt;code&gt;mail&lt;/code&gt; function. The second one should write messages to file – I&amp;rsquo;ll use it in tests.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;BasicMailer&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MailerInterface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Message&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I think, that above class is functionally complete and fulfill the interface, so I decided to mark it as &lt;em&gt;final&lt;/em&gt;. The second implementation is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;FileMailer&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MailerInterface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Message&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$payload&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;json_encode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;subject&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;s1&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;file_put_contents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As previous, I marked this implementation as &lt;em&gt;final&lt;/em&gt;. Nobody should expect more from this simple class – it does exactly what it should.&lt;/p&gt;
&lt;p&gt;This example is suited to be used with the final keyword, but most of the classes we write aren&amp;rsquo;t. It&amp;rsquo;s important to know when we &lt;strong&gt;want to&lt;/strong&gt; use this feature. I write &lt;strong&gt;want&lt;/strong&gt; because there are no &amp;ldquo;&lt;em&gt;should&lt;/em&gt;&amp;rdquo; or &amp;ldquo;&lt;em&gt;should not&lt;/em&gt;&amp;rdquo;. You, as the developer and author, decide how your code is flexible. I&amp;rsquo;m going to write about this later.&lt;/p&gt;
&lt;h2 id=&#34;how-to-introduce-change-if-a-class-is-final&#34;&gt;How to introduce change if a class is &lt;em&gt;final&lt;/em&gt;?&lt;/h2&gt;
&lt;p&gt;First, answer this question: &amp;ldquo;Does the change you would like to introduce isn&amp;rsquo;t the other implementation of specific interface?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Consider this scenario: Our platform serves users&amp;rsquo; avatars from our own FTP server. Due to cost-reduction, business would like to migrate to the Amazon S3. Avatars are accessible by &lt;code&gt;AvatarService&lt;/code&gt; which fulfill the interface &lt;code&gt;AvatarProviderInterface&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AvatarProviderInterface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getForUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Although it&amp;rsquo;s not explicitly marked, &lt;em&gt;AvatarService&lt;/em&gt; is final in that sense, that it provides access to avatars from FTP server. Instead of changing its implementation to S3, we should create another implementation of &lt;em&gt;AvatarProviderInterface&lt;/em&gt;, e.g. &lt;code&gt;S3AvatarService&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We finished with 2 completely different implementations of the same interface. Both are in some sense final – we don&amp;rsquo;t expect anyone to extend them. Moreover, in any moment, we can switch to the previous implementation. We may also &lt;a href=&#34;https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/&#34;&gt;deprecate it, if is no longer needed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I recommend asking yourself if the change isn&amp;rsquo;t the other implementation each time when we need to change something in the class, even for a non-final class.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s a bug that needs to be fixed in existing implementation, don&amp;rsquo;t worry. Go to your tests and spend a few minutes writing a missing scenario that covers this issue. Then, make tests green again.&lt;/p&gt;
&lt;h2 id=&#34;tests-with-final-class&#34;&gt;Tests with &lt;em&gt;final&lt;/em&gt; class&lt;/h2&gt;
&lt;p&gt;Tests are always connected to specific implementations. There is nothing wrong to mock a filesystem, an external API or a queue to check if specific implementation works properly.&lt;/p&gt;
&lt;p&gt;I often hear an opinion that trying to cover each line of methods is sometimes like concreting the implementation. Whatever you change in the code, the covering test has started to fail.&lt;/p&gt;
&lt;p&gt;Instead of identifying another implementation, developers are trying to change existing and completed (&lt;em&gt;final&lt;/em&gt;) classes. Of course, there are exceptions. Nevertheless, it&amp;rsquo;s normal, that if you change the implementation, you have to also change the way how you test it.&lt;/p&gt;
&lt;h3 id=&#34;how-to-mock-final-class&#34;&gt;How to mock final class?&lt;/h3&gt;
&lt;p&gt;One of the arguments against using the &lt;em&gt;final&lt;/em&gt; keyword in classes is that we&amp;rsquo;re unable to create a mock of this class in order to test the dependent service.&lt;/p&gt;
&lt;p&gt;It might sound strange, but this behavior is completely O.K.&lt;/p&gt;
&lt;p&gt;Actually, you should mock an interface (generally speaking, &lt;em&gt;abstraction&lt;/em&gt;) rather than the implementation of it. If you follow this rule, the problem with mocking &lt;em&gt;final&lt;/em&gt; class will disappear. Unless you use methods or properties from outside the interface. &lt;a href=&#34;https://szymonkrajewski.pl/dont-use-properties-and-methods-from-outside-the-interface/&#34;&gt;I wrote why we shouldn&amp;rsquo;t do that in other article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It shows also another concern: &lt;strong&gt;not everything should be marked as &lt;em&gt;final&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;when-to-use-a-final-modifier&#34;&gt;When to use a &lt;em&gt;final&lt;/em&gt; modifier?&lt;/h2&gt;
&lt;p&gt;There is no better than &lt;a href=&#34;https://ocramius.github.io/blog/when-to-declare-classes-final/&#34;&gt;Marco Pivetta&amp;rsquo;s answer&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make your classes always final, if they implement an interface, and no other public methods are defined&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For any other situation, I would consider rethinking an approach a bit and check if a class doesn&amp;rsquo;t break any of SOLID principles. It may turn out, that making the class as &lt;em&gt;final&lt;/em&gt; is ineffective, e.g. for classes like &lt;em&gt;DTOs&lt;/em&gt;, &lt;em&gt;controllers&lt;/em&gt; – a class without interface – or &lt;em&gt;entities&lt;/em&gt;, (e.g. Doctrine&amp;rsquo;s entities) where &lt;em&gt;final&lt;/em&gt; breaks &lt;em&gt;proxy&lt;/em&gt; mechanism.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Marking class as &lt;em&gt;final&lt;/em&gt; is a contract between developers, that the implementation of the class will not change in the future. Interface, on the other side, is a contract which ensures that class has specific behavior.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;final&lt;/em&gt; modifier may apply to both classes or methods. It prevents further modifications by inheritance, encouraging developers to use composition instead.&lt;/p&gt;
&lt;p&gt;I recommend creating classes with the intention to &amp;ldquo;&lt;em&gt;finalize&lt;/em&gt;&amp;rdquo; them. It&amp;rsquo;s not an obligation and you may remove this modifier anytime. It&amp;rsquo;s easier to transit from &lt;em&gt;final&lt;/em&gt; to &lt;em&gt;non-final&lt;/em&gt; class rather than the opposite direction.&lt;/p&gt;
&lt;p&gt;Give yourself time to check if you&amp;rsquo;re comfortable with this approach. You&amp;rsquo;re going towards a better, cleaner code.&lt;/p&gt;
&lt;p&gt;So, at the end I have one thought: If &lt;em&gt;abstract&lt;/em&gt; means, that implementation is not ready, shouldn&amp;rsquo;t the &lt;em&gt;final&lt;/em&gt; be a mark of its completeness? I&amp;rsquo;ll be happy to hear your opinions.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/K3uOmmlQmOo?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Angelina Litvin&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/ready?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The practical difference between &#34;findBy&#34; and &#34;getBy&#34; in repositories</title>
      <link>https://szymonkrajewski.pl/the-practical-difference-between-findby-and-getby-in-repositories/</link>
      <pubDate>Mon, 25 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/the-practical-difference-between-findby-and-getby-in-repositories/</guid>
      <description>&lt;p&gt;Repositories are a special example of a class. They usually have a lot of methods designed to retrieve data from the database or the other storage. To mark this operation in the name of the method, we can use one of the common words: &lt;em&gt;find, get, search&lt;/em&gt;. Are all them mean the same? In this article, I would like to show you a practical difference between &lt;code&gt;getById&lt;/code&gt; and &lt;code&gt;findById&lt;/code&gt; methods.&lt;/p&gt;
&lt;h2 id=&#34;find-vs-get&#34;&gt;Find vs Get&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s nothing wrong to use &lt;code&gt;find&lt;/code&gt; and &lt;code&gt;get&lt;/code&gt; words in methods&amp;rsquo; name interchangeably. Both of them have similar meanings and mark the intention properly. However, if we decided to differentiate them, we&amp;rsquo;ll get an extra benefit as the more meaningful and consistent code. Let me give you an example. Please see at the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ArticleRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Article&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;findById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Article&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Did you pay attention to the extra question mark in the second method&amp;rsquo;s header? If you already know what&amp;rsquo;s going on, you can stop reading here. You can also leave a comment under this article describing why you use this convention. Otherwise, you can continue reading.&lt;/p&gt;
&lt;p&gt;As you can see, the only difference (besides the name of the methods) is the return type. This is related to the &lt;strong&gt;error handling&lt;/strong&gt; mechanism — what should be done if the entity doesn&amp;rsquo;t exist?&lt;/p&gt;
&lt;h2 id=&#34;return-null-or-throw-an-exception&#34;&gt;Return null or throw an exception&lt;/h2&gt;
&lt;p&gt;First method, &lt;code&gt;getById&lt;/code&gt; always returns an &lt;code&gt;Article&lt;/code&gt; object. You may use this method in a workflow if you&amp;rsquo;re sure that the specific entity exists. E.g. when you have checked condition related to its existence. If for some reason the entity doesn&amp;rsquo;t exist, there is an actual unexpected situation and the method throws the exception.&lt;/p&gt;
&lt;p&gt;If we could embody the &lt;code&gt;getById&lt;/code&gt; method, it would be a kid who always gets what he wants. Otherwise, he will scream like a crazy. You should know beforehand how to deal with him.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;findById&lt;/code&gt; either returns &lt;code&gt;Article&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;. It depends on the existence of the requested entity. You may use this method if you&amp;rsquo;re not sure if this entity actually exists, e.g. when someone requests a specific article through an API.&lt;/p&gt;
&lt;p&gt;In this case, the &lt;code&gt;findById&lt;/code&gt; method is more like a customer in a grocery store. He may ask for something and get it. Another time, he may ask for a &lt;em&gt;blue tomato&lt;/em&gt; and get nothing. It happens.&lt;/p&gt;
&lt;h2 id=&#34;small-improvements-big-results&#34;&gt;Small improvements, big results&lt;/h2&gt;
&lt;p&gt;Although this proposal isn&amp;rsquo;t revealing and ground-breaking it makes the code more predictable and consistent. The more predictable and consistent code, the easier to read and understand it is.&lt;/p&gt;
&lt;p&gt;Remember, that nothing works separately. If you want to improve the codebase of your project, you have to introduce a set of easy and useful standards and techniques. If you know polish, I encourage you to read my article &lt;a href=&#34;https://blog.fingo.pl/pl/brak-konsekwencji-czy-chwilowy-chaos-jak-aktualizowac-standardy-w-zastanym-projekcie/&#34;&gt;&amp;ldquo;How to update standards in the aged project&amp;rdquo; 🇵🇱&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In our team, we successfully use this convention, sometimes in conjunction with &lt;a href=&#34;https://szymonkrajewski.pl/why-should-you-return-early/&#34;&gt;return early approach&lt;/a&gt;. Our controller&amp;rsquo;s actions often have to return details regarding the requested entity. If the entity doesn&amp;rsquo;t exist, we handle it immediately. For instance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;actionShowArticle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$article&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;findById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$article&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;errorResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StatusCode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or the other example, if you prefer exceptions:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;actionShowArticle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$article&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EntityNotFoundException&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;errorResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StatusCode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;NOT_FOUND&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The most important is to keep consistency in the behavior of &lt;code&gt;findBy…&lt;/code&gt; and &lt;code&gt;getBy…&lt;/code&gt; methods. The inconsistent code is &lt;strong&gt;untrustworthy&lt;/strong&gt;, so each time, developers have to make sure that the method is implemented properly, instead of taking it from granted.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s all about the convention and standards. These things are useful as long as there are people who want to use them. If you feel, that this tip might be useful in your project, don&amp;rsquo;t hesitate to discuss this idea with your team.&lt;/p&gt;
&lt;p&gt;Even in an old project, it&amp;rsquo;s still worth to propose and introduce improvements, to reduce a cognitive load of the code. The work with this code will be much easier and more enjoyable, for you and your teammates.&lt;/p&gt;
&lt;p&gt;If you like this article, I&amp;rsquo;ll be grateful if you share it further. Thank you!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/FGbLYvTgxx0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Aaron Burden&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/dictionary?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash.&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Quick start with TDD in JavaScript using Jest</title>
      <link>https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/</link>
      <pubDate>Mon, 22 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/</guid>
      <description>&lt;p&gt;JavaScript is one of the most versatile programming languages I know, however, I had a constant problem with the testing. Since I run into TDD, I was looking for something that allows me to build my solutions using this approach. When I had been creating a &lt;a href=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/&#34;&gt;small project in React&lt;/a&gt;, I accidentally discovered &lt;em&gt;Jest&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It surprised me because it hadn&amp;rsquo;t required any extra configuration. I thought that the configuration was built-in into the &lt;em&gt;create-react-app&lt;/em&gt; tool. It turns out that &lt;em&gt;zero-configuration&lt;/em&gt; is one of the philosophies behind this tool. I decided to try using it outside the React application. Since this moment, this is a standard tool for my unit tests for the JavaScript.&lt;/p&gt;
&lt;p&gt;Let me show how easy work with &lt;em&gt;Jest&lt;/em&gt; is.&lt;/p&gt;
&lt;h2 id=&#34;how-to-start-testing-with-jest&#34;&gt;How to start testing with Jest&lt;/h2&gt;
&lt;h3 id=&#34;add-jest-to-the-project&#34;&gt;Add Jest to the project&lt;/h3&gt;
&lt;p&gt;First, we need to install &lt;em&gt;Jest&lt;/em&gt; as a dependency. We can also install it globally but I prefer to treat it like a normal dependency within the project. The easiest way to do it is to use &lt;em&gt;yarn&lt;/em&gt; or &lt;em&gt;npm&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yarn add --dev jest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The next step is adding a starting script to the &lt;code&gt;package.json&lt;/code&gt; file to run the test using locally installed instance.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;jest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;run-tests-in-the-watch-mode&#34;&gt;Run tests in the &lt;em&gt;watch mode&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;If we&amp;rsquo;re working in the project using &lt;em&gt;git&lt;/em&gt;, &lt;em&gt;Jest&lt;/em&gt; uses the data from the repository to identify and runs tests for modified files. It&amp;rsquo;s necessary if we want to run it in the &lt;em&gt;watch mode&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yarn &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt; --watch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Otherwise, if we&amp;rsquo;re not using any version control system, &lt;em&gt;Jest&lt;/em&gt; isn&amp;rsquo;t able to identify changed files. We can still watch for changes in every file using the &lt;code&gt;—watchAll&lt;/code&gt; flag. It&amp;rsquo;s OK for the small number of files.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yarn &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt; --watchAll
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, we can still narrow the watching scope to a specific name or path to file, but let&amp;rsquo;s keep things simple for the beginning. For more information, take a look at the &lt;a href=&#34;https://facebook.github.io/jest/docs/en/cli.html&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;configure-jest-to-work-with-babel&#34;&gt;Configure &lt;em&gt;Jest&lt;/em&gt; to work with &lt;em&gt;Babel&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;I cannot imagine work without the ES6 syntax in the JavaScript. It introduced lots of useful features which make JS easier to use. To support &lt;em&gt;Babel&lt;/em&gt;, we need to add a few more dependencies.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yarn add --dev babel-jest babel-core babel-preset-env regenerator-runtime
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;According to the documentation, &lt;code&gt;regenerator-runtime&lt;/code&gt; isn&amp;rsquo;t needed if you use &lt;em&gt;yarn&lt;/em&gt; or &lt;em&gt;npm (v3 or v4)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can also have to adjust the &lt;code&gt;.babelrc&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;presets&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;write-test&#34;&gt;Write test&lt;/h3&gt;
&lt;p&gt;Now, we can switch to the writing the test. I prefer simple solutions, so I started with creating a test file in the same directory as the tested code. Let&amp;rsquo;s consider the function which normalizes an array of numbers.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Normalizer.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lodash/max&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;min&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lodash/min&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_min&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_max&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;_min&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_min&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;_max&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_max&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dataset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;_min&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dataset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;normalize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;normalize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;max&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To start, we have to import the function in the scope of the test and check outputs for given inputs. Of course, we consider only unit tests for now.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Normalizer.test.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;./Normalizer&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should return an empty result for empty dataset&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([])).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toHaveLength&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should return the same dataset for one identity element&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMatchObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should return the same dataset for already normalized elements&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMatchObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should normalize single element in dataset using it as a max&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMatchObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should normalize dataset using min and max from the dataset&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaled&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMatchObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;should normalize dataset using defined min and max&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaled&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;scaler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scaled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toMatchObject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Thanks to the watching mode, we can receive the feedback after each change in both the test and the code. That&amp;rsquo;s great when you work using a TDD approach. Moreover, if you using an editor like &lt;strong&gt;Visual Studio Code&lt;/strong&gt; or &lt;strong&gt;Atom&lt;/strong&gt;, there are great extensions providing useful visual feedback directly in the editor (&lt;em&gt;see resources&lt;/em&gt;).&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_2f6ccb09638eff4.png 360w, https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_c961fe88e2bef1d4.png 640w, https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_c57f8959ae7c210a.png 960w, https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_2e7db69b74d62809.png 1200w, https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_c8fde400c8110648.png 1600w, https://szymonkrajewski.pl/quick-start-with-tdd-in-javascript-using-jest/019_vscode_test_live_hu_18172dd393f36362.png 2154w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;2154&#34;
          height=&#34;1518&#34;
           alt=&#34;Visual Studio Code window with Jest runner plugin&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Visual Studio Code with runner plugin.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This presented use-case is small, however, I often need a develop some small functionalities to confirm my initial idea. That&amp;rsquo;s why I need a tool, that supports me with my work instead of overwhelming me by its own configuration. By using &lt;em&gt;Jest&lt;/em&gt;, I can focus on things I want to do instead of doing things I have to do.&lt;/p&gt;
&lt;p&gt;I think It&amp;rsquo;s always worth to try new development tools. Sometimes a little change may have a significant difference in future work. But don&amp;rsquo;t take things for granted and test by our own. Maybe in your case, a different tool is much better? Let me know in comments.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://jestjs.io/en/&#34;&gt;https://jestjs.io/en/&lt;/a&gt; – Jest documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://babeljs.io/docs/en/&#34;&gt;https://babeljs.io/docs/en/&lt;/a&gt; – Babel documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://jestjs.io/docs/en/getting-started#using-babel&#34;&gt;https://jestjs.io/docs/en/getting-started#using-babel&lt;/a&gt; – Using Jest with Babel&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest&#34;&gt;https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest&lt;/a&gt; – Jest runner for VSCode&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/tester-jest&#34;&gt;https://atom.io/packages/tester-jest&lt;/a&gt; – Jest runner for Atom&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/ViEBSoZH6M4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Chris Liverani&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/test?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Set up multi-container environment using Docker Compose</title>
      <link>https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/</link>
      <pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/</guid>
      <description>&lt;p&gt;I remember this time when I discovered &lt;strong&gt;Vagrant&lt;/strong&gt;. The magic behind this tool and the general idea of scaffolding the whole environment using single command was pretty genius. Moreover, I had programmed on Windows and thankfully I could get rid of the XAMP and any other Windows-oriented web server packages.&lt;/p&gt;
&lt;p&gt;In my previous job, my boss showed me a tool called &lt;strong&gt;Docker&lt;/strong&gt;. I instantaneously got the point of the concept behind it and I started exploring the big universe of the possibilities of use. In this article, I&amp;rsquo;m going to show the most common use-case for docker – a proposed local environment suited for PHP application development.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is devoted to the configuration rather than explanation what the Docker really is. I would like to show the actual use-case rather than showing my point of view – I&amp;rsquo;ll follow it in one of the next articles.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements&lt;/h2&gt;
&lt;p&gt;If you want to follow this instruction, you have to install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://store.docker.com/search?type=edition&amp;amp;offering=community&#34;&gt;Docker CE&lt;/a&gt; – docker engine&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.docker.com/compose/install/&#34;&gt;Docker Compose&lt;/a&gt; – tool for orchestrating the multi-container environment&lt;/li&gt;
&lt;li&gt;PHP and Composer&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;If you don&amp;rsquo;t want to follow this article, don&amp;rsquo;t worry. Everything is prepared and ready to use – just see my GitHub  repository &lt;a href=&#34;https://github.com/skrajewski/php-app-on-docker/tree/basic-php&#34;&gt;skrajewski/php-app-on-docker&lt;/a&gt; on a tag &lt;strong&gt;basic-php&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;test-application&#34;&gt;Test application&lt;/h2&gt;
&lt;p&gt;There is no much sense to create an advanced environment to develop a single-file script. To approach ourselves to the actual situation we&amp;rsquo;re going to run a Symfony 4 application.&lt;/p&gt;
&lt;p&gt;Our application should use &lt;strong&gt;PHP 7.2&lt;/strong&gt; and should be served by the &lt;strong&gt;nginx&lt;/strong&gt; web server. Moreover, we need also a database to store our data, e.g. &lt;strong&gt;MariaDB&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;think-about-the-infrastructural-services&#34;&gt;Think about the infrastructural services&lt;/h3&gt;
&lt;p&gt;All these three services may be considered as infrastructural services. Each of them may live in a dedicated container – we don&amp;rsquo;t have to mix up the PHP with the web server.&lt;/p&gt;
&lt;p&gt;This is our goal. Create a multi-container environment where services will be connected together if needed.&lt;/p&gt;
&lt;h3 id=&#34;install-symfonys-website-skeleton&#34;&gt;Install Symfony&amp;rsquo;s website skeleton&lt;/h3&gt;
&lt;p&gt;In this step, I assume that we have installed and configured PHP and Composer. Of course, we may start from the environment and subsequently run the whole set up within it. In this article, we&amp;rsquo;ll focus on putting the application into the new environment.&lt;/p&gt;
&lt;p&gt;To install Symfony application, run below command&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;composer create-project symfony/website-skeleton application
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Change the working directory to &lt;em&gt;application&lt;/em&gt; and let&amp;rsquo;s go to the environment definition.&lt;/p&gt;
&lt;h2 id=&#34;development-environment&#34;&gt;Development environment&lt;/h2&gt;
&lt;p&gt;We should prepare three different containers for each of our services. We don&amp;rsquo;t want to do it by hand. It&amp;rsquo;s the place where docker-compose come to the play. By preparing &lt;code&gt;docker-compose.yml&lt;/code&gt; file we can describe how our environment should look like.&lt;/p&gt;
&lt;p&gt;Create a &lt;code&gt;docker-compose.yml&lt;/code&gt; with the below content&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However this config will create a three containers environment, there is a lot of missing configuration yet.&lt;/p&gt;
&lt;h3 id=&#34;make-services-accessible-from-the-host&#34;&gt;Make services accessible from the host&lt;/h3&gt;
&lt;p&gt;To reach each of this service we have to know their internal IPs. Unfortunately, they are dynamically assigned so it&amp;rsquo;s hard to simply prepare a host file. To mitigate this problem we can bind exposed ports to our host machine. Let&amp;rsquo;s do this!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;33060:3306&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax is following: &lt;code&gt;&amp;quot;${HOST_PORT}:{SERVICE_PORT}&amp;quot;&lt;/code&gt;. Now, the web server should be accessible under &lt;code&gt;localhost:8080&lt;/code&gt; and the database under &lt;code&gt;localhost:33060&lt;/code&gt;. We don&amp;rsquo;t need a direct access to &lt;code&gt;php&lt;/code&gt; container.&lt;/p&gt;
&lt;h3 id=&#34;provide-credentials-to-our-database&#34;&gt;Provide credentials to our database&lt;/h3&gt;
&lt;p&gt;In order to access our database, we need to set up a password at least for the &lt;em&gt;root&lt;/em&gt; user. The documentation of &lt;a href=&#34;https://hub.docker.com/_/mariadb/&#34;&gt;MariaDB image on Docker Hub&lt;/a&gt; is pretty good and it shows how to do it using environment variables. Let&amp;rsquo;s change a &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration a little bit.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;version:&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;33060:3306&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_DATABASE=app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our database server will create a default database called &lt;strong&gt;app&lt;/strong&gt; and set the root password&amp;rsquo;s to &lt;code&gt;root&lt;/code&gt;. Not so secure, but it&amp;rsquo;s enough for the local development. Keep it simple.&lt;/p&gt;
&lt;h3 id=&#34;add-a-virtual-host-to-nginx-container&#34;&gt;Add a virtual host to nginx container&lt;/h3&gt;
&lt;p&gt;However &lt;strong&gt;nginx&lt;/strong&gt; is working correctly, it shows nothing interesting than the standard &lt;em&gt;welcome&lt;/em&gt; page. We have to add the configuration to let web server knows how to manage request to our application.&lt;/p&gt;
&lt;p&gt;Personally, I like to keep all docker related stuff in the directory called &lt;code&gt;.docker&lt;/code&gt; which I create in the root of the project. For a better order, I&amp;rsquo;m going to add subdirectory related to the service to keep all files together.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p .docker/nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch .docker/nginx/default.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Put the virtual host configuration to the newly created &lt;code&gt;default.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# This is the root of our application, we need to mount files in /app
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/app/public&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.php&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;try_files&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/index.php&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$is_args$args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/favicon.ico&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;log_not_found&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;access_log&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/robots.txt&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;allow&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;log_not_found&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;access_log&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;^/index\.php(/|$)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Instead of using socket, we pass execution of PHP files 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;# to the process on different container using host name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;kn&#34;&gt;fastcgi_pass&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;9000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;fastcgi_split_path_info&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;^(.+\.php)(/.*)&lt;/span&gt;$&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;fastcgi_params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$realpath_root$fastcgi_script_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;DOCUMENT_ROOT&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$realpath_root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;sr&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;\.(js|css|png|jpg|jpeg|gif|ico)&lt;/span&gt;$ &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;expires&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;log_not_found&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;~&lt;/span&gt; &lt;span class=&#34;sr&#34;&gt;\.php$&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;error_log&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/var/log/nginx/project_error.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;access_log&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/var/log/nginx/project_access.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we can see in the configuration file, we use a php-fpm located in another container. This approach allows us to e.g. change the PHP version without changes in the web-server.&lt;/p&gt;
&lt;p&gt;To make it works, we need to mount both configuration and application files into the &lt;code&gt;nginx&lt;/code&gt; container.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./:/app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./.docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;33060:3306&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_DATABASE=app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;mount-the-application-to-the-php-container&#34;&gt;Mount the application to the PHP container&lt;/h3&gt;
&lt;p&gt;In spite of the &lt;code&gt;nginx&lt;/code&gt; has access to the application files, the &lt;code&gt;php-fpm&lt;/code&gt; container doesn&amp;rsquo;t know anything about the files. The php-fpm should also have access to application files in order to process them on the request of web-server. Let&amp;rsquo;s mount the application directory also to this container.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./:/app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./:/app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./.docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;33060:3306&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_DATABASE=app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the old good days, docker-compose in version ‘2&amp;rsquo; has option &lt;code&gt;volumes_from&lt;/code&gt; which allows mounting volumes from the other container. It let us avoiding repetition but unfortunately, for various reasons, this option is no longer supported.&lt;/p&gt;
&lt;h3 id=&#34;use-our-own-php-configuration-and-extensions&#34;&gt;Use our own PHP configuration and extensions&lt;/h3&gt;
&lt;p&gt;Using the raw PHP image is very limited. The better option is to create own image using the PHP image as a foundation. We can do this by creating a new &lt;strong&gt;Dockerfile&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Similar to nginx configuration, let&amp;rsquo;s put the new file in &lt;code&gt;.docker/php/Dockerfile&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p .docker/php
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch .docker/php/Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, edit newly created Dockerfile and put below content.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-docker&#34; data-lang=&#34;docker&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; php:7.2-fpm&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; apt-get update &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get install -y &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    libmcrypt-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    zlib1g-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    libicu-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    g++&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; docker-php-ext-install -j&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; iconv mysqli pdo_mysql mbstring intl&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; yes &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; pecl install xdebug-2.6.1 mcrypt-1.0.1 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker-php-ext-enable xdebug mcrypt&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Based on &lt;code&gt;php:7.2-fpm&lt;/code&gt; we create a new image with some extra extensions, which are needed for our application. If you need more information about the php image, please check &lt;a href=&#34;https://hub.docker.com/_/php/&#34;&gt;the documentation on the Docker Hub&lt;/a&gt;. The same thing we may do with the &lt;code&gt;php.ini&lt;/code&gt; file, but we skip it for the sake of brevity.&lt;/p&gt;
&lt;p&gt;The only thing to do is to use this image in our &lt;code&gt;docker-compose.yml&lt;/code&gt; file. Let&amp;rsquo;s do it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;php-fpm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;dockerfile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;.docker/php/Dockerfile&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./:/app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;nginx:1.11&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./:/app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;./.docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;mariadb:10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;33060:3306&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MYSQL_DATABASE=app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configure-the-application&#34;&gt;Configure the application&lt;/h2&gt;
&lt;p&gt;The application will be running from the container so it may use internal hostnames. For example, to configure a database connection, open your &lt;code&gt;.env&lt;/code&gt; file and change the &lt;code&gt;DATABASE_URL&lt;/code&gt; to following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DATABASE_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mysql://root:root@db:3306/app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because we don&amp;rsquo;t run any extra services right now, this is the only change in the application.&lt;/p&gt;
&lt;h3 id=&#34;initialize-the-environment&#34;&gt;Initialize the environment&lt;/h3&gt;
&lt;p&gt;To initialize the whole environment, use a docker-compose tool.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It may take a while since the docker has to build a custom php image. After a few minutes, everything should be done and the application should be accessible under the &lt;code&gt;localhost:8080&lt;/code&gt; url.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;up&lt;/code&gt; command is for scaffolding the whole environment. For the normal usage, you should use &lt;code&gt;start/stop&lt;/code&gt; commands.&lt;/p&gt;
&lt;h2 id=&#34;final-result&#34;&gt;Final result&lt;/h2&gt;
&lt;p&gt;If you try to reach the &lt;code&gt;localhost:8080&lt;/code&gt;, you should see the page similar to the following.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_22d5a3697b8afead.png 360w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_6474f76cf71f8257.png 640w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_d30ec52b9bd29c72.png 960w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_2e79650c8c5bf05c.png 1200w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_d8f397efbc301b59.png 1600w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_application_page_hu_afc8724e5a096219.png 2480w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;2480&#34;
          height=&#34;1594&#34;
           alt=&#34;Symfony 4 default page&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Symfony 4 default page running on the new environment.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;To connect easily to the database server outside the environment, you may use the &lt;code&gt;localhost&lt;/code&gt; host and the port &lt;code&gt;33060&lt;/code&gt;. For instance, the below screen presents connection details in a &lt;em&gt;Sequel Pro&lt;/em&gt; application.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_4ae44a646beedca.png 360w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_7e7930f521eff60c.png 640w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_c1c3a8a03588819e.png 960w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_cecbe40eb597f3f8.png 1200w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_f68e40042000f883.png 1600w, https://szymonkrajewski.pl/set-up-multi-container-environment-using-docker-compose/022_database_connection_hu_5883f013dca98753.png 2054w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;2054&#34;
          height=&#34;1402&#34;
           alt=&#34;Sequel Pro application - database connection details&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Database connection details in Sequel Pro application.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;We successfully went through the first part of the environment preparation and configuration. This is the very beginning part of the whole journey. Each environment should be suited for the application it hosts. Maybe you&amp;rsquo;ll use a different database engine, a different cache storage or a queue solution. The configuration should depend on the needs.&lt;/p&gt;
&lt;p&gt;Is it worth to create a universal environment? It depends on how universal it should be. If you want to develop applications in only one framework or one technology, why now. If you would like to create a versatile environment for a bigger spectrum of technologies, it may not make much sense.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the reason why I didn&amp;rsquo;t write the final configuration with a paragraph of text describing how to use it. Instead, I tried to show you how to set up an environment you really need. I have taken each action as the response for a need.&lt;/p&gt;
&lt;p&gt;Preparing the environment is different than the programming, but also makes fun. It&amp;rsquo;s worth to know what you use it, how, and even why. Stay tuned.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/9cCeS9Sg6nU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;chuttersnap&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>I&#39;ve finished studies. Here are some of my thoughts</title>
      <link>https://szymonkrajewski.pl/ive-finished-studies-here-are-some-of-my-thoughts/</link>
      <pubDate>Mon, 24 Sep 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/ive-finished-studies-here-are-some-of-my-thoughts/</guid>
      <description>&lt;p&gt;A long time since my last article. During the last few months, I was fully involved in my last big project at my university. Dozens of articles and books, hundreds of visited pages and thousands of words later I had finished it and a few weeks later I defended my master&amp;rsquo;s thesis.&lt;/p&gt;
&lt;p&gt;Since I took a short break from almost every activity, I had a lot of time for thinking. When I look at 2012, I see the young guy who knows everything he needs and he wants to only the paper to confirm it. Now, in 2018, I&amp;rsquo;m equipped with knowledge, a completely new point of view and goals in life. But it wasn&amp;rsquo;t so obvious even during my education.&lt;/p&gt;
&lt;p&gt;Some people asked me what studies gives me at all. Was it worth to spend almost every weekend for the last 6 years at the university? Do I feel the difference? There are no simple answers to these questions because they depend on the approach. And it changed over time. But let me sum up.&lt;/p&gt;
&lt;h2 id=&#34;knowledge&#34;&gt;Knowledge&lt;/h2&gt;
&lt;p&gt;The main thing that studies gave me was a wider look at the life. What does it mean? Thanks to the studies, I&amp;rsquo;m more aware what I don&amp;rsquo;t know yet so I reduced the number of things I don&amp;rsquo;t know that I don&amp;rsquo;t know. Moreover, I&amp;rsquo;m a kind of person who likes to knows how things work and why. Discovering and learning something new gives me a lot of fun.&lt;/p&gt;
&lt;p&gt;I have respect for all people who continue Ph.D. and decided to learn and teach others. I thought about this path but without full devotion, it doesn&amp;rsquo;t make much sense to me. At least not now.&lt;/p&gt;
&lt;p&gt;My expectation when it comes to studies were related to the extension of my skills as a professional software developer. What is interesting, the best things I learned weren&amp;rsquo;t related directly to programming itself. Moreover, knowledge in those others fields gave me a fresh look at some problems in my disciplines. I still like to connect dots.&lt;/p&gt;
&lt;p&gt;I decided to study at weekends because I had wanted to start a regular job. I couldn&amp;rsquo;t sit at home. In the office, I had an opportunity to acquire the knowledge from other people. Personally, I don&amp;rsquo;t have an impression, that thanks to my studies, I have a good job. The education was important, but my own work, self-education, and researching approach played the bigger role. It doesn&amp;rsquo;t mean that I disregard the influence of studies on my life. It&amp;rsquo;s hard to be objective in this field.&lt;/p&gt;
&lt;h2 id=&#34;lectures&#34;&gt;Lectures&lt;/h2&gt;
&lt;p&gt;In spite of the attractiveness of some lectures and exercises, the lecturer itself has a big impact on the quality of the lesson. A lot of things matter: the way of speaking, the presentation, contact with the audience, and the general attitude to own job and the knowledge. Unfortunately, it was rare to find a single person with all of these features but when somebody had most of them, it also conducted the most interesting lecture. I still don&amp;rsquo;t know if it&amp;rsquo;s a rule or case.&lt;/p&gt;
&lt;h2 id=&#34;people&#34;&gt;People&lt;/h2&gt;
&lt;p&gt;I had pleasure met a lot of fantastic people – both lecturers and other students. They were so supportive and they shared knowledge willingly. Otherwise, I had to deal also with cruel and unfriend ones. All these contacts gave me value and learned how to deal with them.&lt;/p&gt;
&lt;p&gt;Too often people pushed an only minimal effort to pass through everything that is mandatory. In the beginning, it was too easy to work this way, but later the requirements were high enough to say about them as challenging.&lt;/p&gt;
&lt;p&gt;One of the favorite ways of conducting projects is to assemble people to so-called teams to work on some project together. It often meant that everything was done by one or two people on the team while the rest of the team were doing nothing but the documentation. Universities should push more effort to encourage and teach people how to work as a part of the team.&lt;/p&gt;
&lt;h2 id=&#34;improvement-of-soft-skills&#34;&gt;Improvement of soft skills&lt;/h2&gt;
&lt;p&gt;However some lessons were dedicated to practice soft skills like public speaking, presenting etc, in the vast majority, it was a waste of the time. A few hours during whole studies are not enough to learn students how to prepare a good talk. If someone didn&amp;rsquo;t learn somewhere else how to do it, he ended up speaking to the sleepy audience, sometimes including the lecturer.&lt;/p&gt;
&lt;p&gt;The reason is, 90% of the time, people spend listening to someone who is also learning as others. There is no way to get knowledge how to do it well except a few comments from the lecturer. The rest of the time you may spend conducting your own talk. But talking to the group of people that don&amp;rsquo;t want to listen is unpleasant.&lt;/p&gt;
&lt;p&gt;People need a friendly environment and the coach. Someone who see the posture and movement. Someone who listens to each word. Not to count the number of mistakes but to show how to improve them. I wish met someone who teaches me how to speak to people well.&lt;/p&gt;
&lt;h2 id=&#34;thesis-defense&#34;&gt;Thesis defense&lt;/h2&gt;
&lt;p&gt;I had a tendency to repeat the same mistakes again, especially when it comes to writing a thesis. I wrote two: Bachelor&amp;rsquo;s thesis and Master&amp;rsquo;s thesis. I hadn&amp;rsquo;t wanted to explore something I already knew. I treated this part of the study as an opportunity to learn something completely fresh for me. Regardless of the topic, I had a constant problem with starting doing something. A procrastination is a good word here. I procrastinated (and I&amp;rsquo;m still doing it) a lot. The &lt;a href=&#34;https://en.wikipedia.org/wiki/Parkinson%27s_law&#34;&gt;Parkinson&amp;rsquo;s law&lt;/a&gt; is very brutal.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This post is based on my personal experiences. You may have different opinions and feelings and this is completely fine – if you want, you can share them &lt;del&gt;in comments&lt;/del&gt; on twitter.&lt;/p&gt;
&lt;p&gt;Every time I hear that somebody talks to other about the sense of computer science studies, I recall why I started them and what I got at the end. And I have no regrets.&lt;/p&gt;
&lt;p&gt;Diploma degree or the title don&amp;rsquo;t really matter. The only thing you&amp;rsquo;ll use after studies is everything you learned during them. Keep learning. The more you know, the more opportunities you see.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/ItFTJoh1A8c?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Suad Kamardeen&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/graduation?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The code is a common good, so be responsible for him</title>
      <link>https://szymonkrajewski.pl/the-code-is-a-common-good/</link>
      <pubDate>Sun, 15 Apr 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/the-code-is-a-common-good/</guid>
      <description>&lt;p&gt;I joined to the project that was developed by one guy. He was an amazing developer with plenty of ideas and skills. He had also the best knowledge about the system – its domain, architecture, used solutions, hidden tricks, and workarounds. In every single task, I needed his support because I didn&amp;rsquo;t understand how things worked. My main goal was to retrieve as much knowledge as possible from his head. The reason was simple – his contract is ending in a month. I wish I didn&amp;rsquo;t know about it before.&lt;/p&gt;
&lt;p&gt;The project had the long to-do list of features. Some of them were partially implemented. Some other functionalities had specified time–frame because of the seasonal nature of the project. Everything was important from the business point of view.&lt;/p&gt;
&lt;p&gt;There were no tests, no code&amp;rsquo;s style guide, no documentation (besides a few out of date &lt;em&gt;README.md&lt;/em&gt; files across repository). I didn&amp;rsquo;t want to touch anything because the code was unstable and cause a lot of side–effects. After one month of torment, I thought – &amp;ldquo;&lt;em&gt;f*ck it, let&amp;rsquo;s make it works&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I share this story because it was one of the most important moments in my career as a software developer. The moment when you realize that if you want to go further you can&amp;rsquo;t hide behind lack of knowledge and misunderstanding of someone&amp;rsquo;s else solutions. I decided to explore it and treat it like my own code. I provided fixes, improvements, and changes. Finally, &lt;strong&gt;I took pleasure in making things better&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That led me to another thought – &lt;strong&gt;the code is &lt;em&gt;common good&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;working-as-the-only-one-developer&#34;&gt;Working as the only one developer&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t know how popular is working as the only developer in a project. On the beginning of my career, I worked in this way for a very short time. Most of the projects were like single–shots – do it and forget it. It was very convenient because it allowed me to test my ideas. Otherwise, It was hard to care of the code if you had limited amount of time and the pressure from the top.&lt;/p&gt;
&lt;p&gt;It was a lie. Almost always these projects came back both for simple improvements and further development. Even if they didn&amp;rsquo;t come back to us, they were transferred to other companies. The legacy code I produced wasn&amp;rsquo;t a problem for me – it was a problem for other developers. I realized it when I received the legacy project the first time.&lt;/p&gt;
&lt;p&gt;Although working as a one-man band has a lot of advantages, it has also a lot of pitfalls. The one is very destructive – you cannot challenge yourself with other developers. You have limited chance to speak about the project, about the potential solutions, you cannot validate your ideas and nobody does the code review for you. You are behind the wall so you are fully responsible for your work and the education. Your commit messages are the only single letter, you have plenty of jokes in comments in code with the hope that someone will see it and laugh. You don&amp;rsquo;t write tests because you know that everything works as expected.&lt;/p&gt;
&lt;p&gt;If this description fits you, be sure to read on.&lt;/p&gt;
&lt;h2 id=&#34;consequences-of-the-bad-code&#34;&gt;Consequences of the bad code&lt;/h2&gt;
&lt;p&gt;If you work as a professional developer, &lt;strong&gt;the code which you produce belongs to the company&lt;/strong&gt;. Even if you are the only one dev in the project, you work for (or on behalf of) your company. If the project is successful, the team may grow.&lt;/p&gt;
&lt;p&gt;Working in the team is a completely different story. But let&amp;rsquo;s focus on the code.&lt;/p&gt;
&lt;p&gt;Every project has an &lt;strong&gt;entry barrier&lt;/strong&gt; – it depends on the complexity of the project and domain but also on the code style, documentation, technical debt, tests, and all used hacks and workarounds. If you don&amp;rsquo;t take care of these things, the entry level will be higher and higher. The introduction of a new person to the project will take a long time causing extra costs for your company.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_44e5e7ed8c5ac415.webp 360w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_7ce4ecf0949d6674.webp 640w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_ffb71db37175a537.webp 960w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_1d6e5895bc885efe.webp 1200w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_c6e8213f6f9df71c.webp 1590w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_d4872cae0a25db8f.jpg 360w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_669bebbcdbac02d8.jpg 640w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_bc33fae20e95f297.jpg 960w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_a1bca2c8e0cab2f8.jpg 1200w, https://szymonkrajewski.pl/the-code-is-a-common-good/018_new_developer_in_project_hu_b962e358a1afa068.jpg 1590w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1590&#34;
          height=&#34;1061&#34;
           alt=&#34;A climbing man as a metaphor of a new developer in a project&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      New developer in a project. Photo by Lionello DelPiccolo on Unsplash
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Also, when a developer is leaving a company, somebody else has to come to his place. Leaving the project without any documentation, tests, with the code in a state resembling a rubbish dump is very selfish and irresponsible. It isn&amp;rsquo;t only a harm to the company but also for &lt;strong&gt;coworkers&lt;/strong&gt; and &lt;strong&gt;the rest of developers&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our goal as professional software developers is to create software that supports business, resolves problems and makes money. Software which produces other problems isn&amp;rsquo;t a good investment.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;We should take care of the code all the time during our daily work. The most difficult situation is when the &lt;strong&gt;team is split&lt;/strong&gt; to people who don&amp;rsquo;t take care of the code and the rest, who take the responsibility for what they produce. Whom to blame if something goes &lt;em&gt;wrong&lt;/em&gt;? Is it really a team?&lt;/p&gt;
&lt;p&gt;Another phenomenon is the &lt;strong&gt;disconnection from the code&lt;/strong&gt; written by someone&amp;rsquo;s else. Some developers avoid changing this code because they don&amp;rsquo;t feel responsible for it. Since the code is in the code base, it&amp;rsquo;s a &lt;em&gt;common good&lt;/em&gt; of the whole team. We all can comment and blame but only a few people are able to give a constructive feedback or even help.&lt;/p&gt;
&lt;p&gt;Is it all the consequences? I think the topic isn&amp;rsquo;t exhausted yet. But they are problems that I already met. I&amp;rsquo;m sure that you can write a few more comments.&lt;/p&gt;
&lt;h2 id=&#34;how-to-be-a-responsible-developer&#34;&gt;How to be a responsible developer?&lt;/h2&gt;
&lt;p&gt;I wanted to ask &amp;ldquo;&lt;em&gt;How to write a good code?&lt;/em&gt;&amp;rdquo;, but this is very subjective and depends a lot on technical aspects. Instead, I decided to ask about responsibility, because a responsible developer writes a &lt;em&gt;good code&lt;/em&gt;. It&amp;rsquo;s a natural implication.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know the easy-to-follow guidelines that make every developer accountable. That&amp;rsquo;s something that everyone has to discover. Instead, I have prepared a simple list which bases on my experience.&lt;/p&gt;
&lt;h4 id=&#34;1-treat-code-as-a&#34;&gt;&lt;strong&gt;1. Treat code as a &lt;em&gt;common good&lt;/em&gt;&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;No matter if you are only one developer or you are working in the team. The responsible developer always keeps the code in the good condition. Try to reduce the technical debt and make the code more understandable. Don&amp;rsquo;t disconnect yourself from someone&amp;rsquo;s else code – treat is as your own and makes it better.&lt;/p&gt;
&lt;h4 id=&#34;2-don&#34;&gt;&lt;strong&gt;2. Don&amp;rsquo;t blame your teammates&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Because the code is common, in case of error, the whole team fails. You can always check what someone else introduces to the code base. Don&amp;rsquo;t you have a code review process? It&amp;rsquo;s a nice time to make it as a part of the development process.&lt;/p&gt;
&lt;h4 id=&#34;3-write-code-easy-to-read&#34;&gt;&lt;strong&gt;3. Write code easy to read&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Developers spend most of the time reading the code. Make this process easier by writing code which is easy to read and understand. How to do this? For instance, I use the &lt;a href=&#34;https://szymonkrajewski.pl/why-should-you-return-early/&#34;&gt;&lt;em&gt;return early&lt;/em&gt;&lt;/a&gt; method to make my code more readable.&lt;/p&gt;
&lt;h4 id=&#34;4-write-code-easy-to-remove&#34;&gt;&lt;strong&gt;4. Write code easy to remove&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Isn&amp;rsquo;t something necessary? Remove it. Code which is easy to remove is also easy to change. Less code in the code base means less code to read and maintain. The good &lt;a href=&#34;https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/&#34;&gt;code&amp;rsquo; deprecation strategy&lt;/a&gt; is very helpful.&lt;/p&gt;
&lt;h4 id=&#34;5-write-tests&#34;&gt;&lt;strong&gt;5. Write tests&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;This is a proof that the code works as expected. It&amp;rsquo;s also a good documentation. Don&amp;rsquo;t treat tests like separate tasks, it doesn&amp;rsquo;t make much sense.&lt;/p&gt;
&lt;h4 id=&#34;6-deliver-solutions-not-problems&#34;&gt;&lt;strong&gt;6. Deliver solutions, not problems&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Even if you don&amp;rsquo;t have a good information to your manager, product owner or somebody who is in charge, try to show other potential solutions, not only problems.&lt;/p&gt;
&lt;h4 id=&#34;7-don&#34;&gt;&lt;strong&gt;7. Don&amp;rsquo;t be a blocker&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Don&amp;rsquo;t you want to learn something to improve your skills? Do you block opportunities for others? Instead, take as much as possible from them. Don&amp;rsquo;t you take care of the code style? Take advantage of auto-formatters – it isn&amp;rsquo;t a big deal.&lt;/p&gt;
&lt;h4 id=&#34;8-don&#34;&gt;&lt;strong&gt;8. Don&amp;rsquo;t be an asshole&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Be kind and helpful. Simply. Even if your contract is ending and you don&amp;rsquo;t have good relation with your managers, try to do everything to make the work easier for your teammates and future developers. You can meet them in one of the next companies.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I started from a short story about the situation that had a big impact on my career. The key is to understand that companies don&amp;rsquo;t hire people to help them, &lt;strong&gt;they hire because they need help&lt;/strong&gt;. They are looking for someone who is able to solve their problems and who introduces something fresh to the organization.&lt;/p&gt;
&lt;p&gt;I could wait and complain that the code is ugly and unmanageable. The company didn&amp;rsquo;t take care of the good knowledge transfer. But I didn&amp;rsquo;t. Instead, I did my best to make the work better for me and other developers. Ultimately I changed job but I left leaving things in the best form.&lt;/p&gt;
&lt;p&gt;Purposely I don&amp;rsquo;t write about the tools and methods how to write a &lt;em&gt;good code&lt;/em&gt;. It isn&amp;rsquo;t a key in this article. The point is to realize that we all more than just developers – people who write code. We solve complex problems, we give our customers the value, we make someone&amp;rsquo;s else life easier, we work together – whether we like it or not. Let us respect our work and take responsibility for what we do.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/UCZF1sXcejo&#34;&gt;Štefan Štefančík&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Did your team define own code deprecation strategy?</title>
      <link>https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/</link>
      <pubDate>Mon, 12 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/</guid>
      <description>&lt;p&gt;I didn&amp;rsquo;t always work in a larger team. At the beginning of my journey as a computer programmer, I was the only one person in a project. It meant that I had had a free-hand (or semi-free-hand) to choose how I could write a code and which solutions I could use. From day to day I could perform a little revolution in the codebase. No consequences and no problems because the only user of the code was me.&lt;/p&gt;
&lt;p&gt;Although this situation might look as the best case for a programmer, it doesn&amp;rsquo;t. Especially for the junior programmer. You have no opportunity to learn from someone else. You can&amp;rsquo;t validate your ideas and thoughts with others. Ultimately your only friends and co-workers are &lt;em&gt;Google&lt;/em&gt; and Mr. &lt;em&gt;StackOverflow&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;After some time, I got a new job and I started to work as a part of the team. As a new person in the company, I had tons of ideas and tools that we could use in a project to make our work better, more pleasant and easier. It was relatively easy to introduce new features into the codebase. On the other hand, it was almost impossible to remove the old ones. Why?&lt;/p&gt;
&lt;p&gt;Because we didn&amp;rsquo;t have the code deprecation strategy.&lt;/p&gt;
&lt;p&gt;If you add a revolutionary feature to the codebase and your co-workers like it, you can be 100% sure that further changes will not be that easy. &lt;em&gt;That works quite good, why we want to change it?&lt;/em&gt; More people in the project, the more difficult is to introduce ground-breaking changes.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the difference between working alone and with the team. Even if you introduce something that should replace the current feature, people should also accustom to the new one and change the old code as well. If not, your project will contain a &lt;strong&gt;deprecated&lt;/strong&gt; or even &lt;strong&gt;obsolete&lt;/strong&gt; code.&lt;/p&gt;
&lt;h2 id=&#34;why-code-become-obsolete&#34;&gt;Why code become obsolete?&lt;/h2&gt;
&lt;p&gt;By saying &lt;em&gt;obsolete&lt;/em&gt;, I mean that a code which is not in use anymore. Often it has left in codebase because it was used somewhere, but things changed and people forget about it. The code can become obsolete when we introduce a new way to solve a particular problem but we don&amp;rsquo;t do anything to get rid of the old feature.&lt;/p&gt;
&lt;p&gt;So every time when we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;have completely new idea to solve the problem,&lt;/li&gt;
&lt;li&gt;discover that that current implementation would be better,&lt;/li&gt;
&lt;li&gt;change the infrastructure behind,&lt;/li&gt;
&lt;li&gt;need to change something to adjust the system to business needs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a chance to left old code, even if it&amp;rsquo;s not needed anymore. Before code becomes obsolete, it enters the &lt;em&gt;transition phase&lt;/em&gt; – the stage when we change the old implementation to the new one. Problems might appear when we allow the old code work or exist in parallel with the new code without control. Finally, we can &lt;strong&gt;forget&lt;/strong&gt; about the old code. Sometimes we consciously leave old code in the project just in case.&lt;/p&gt;
&lt;p&gt;So what we should do to avoid situations like above? We should write code that is easy to understand, easy to use, and easy to replace. We should encourage people to use the new one all the time.&lt;/p&gt;
&lt;h2 id=&#34;how-to-change-the-code&#34;&gt;How to change the code?&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take the following statement as true:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&amp;rsquo;s very difficult to replace every usage of function/method/class in the codebase but it&amp;rsquo;s relatively easy to implement the new function/method/class beside old one and switch a usage to the new implementation step by step.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;According to above, we shouldn&amp;rsquo;t change the code directly within old methods or classes. Instead, we should create new methods and classes which will replace older counterparts &lt;em&gt;in the future&lt;/em&gt;. If you follow &lt;em&gt;SOLID&lt;/em&gt; principles, it&amp;rsquo;s convenient, thanks to &lt;em&gt;Single Responsibility Principle&lt;/em&gt; and &lt;em&gt;Open-Close Principle&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The cause is simple. If we don&amp;rsquo;t replace all usage of the old function or class and we change something in the old code (&lt;em&gt;implementation, method&amp;rsquo;s header, etc.&lt;/em&gt;), then the execution may fail.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to show a simple scenario when we perceive an opportunity to improve the usability of the code. Let&amp;rsquo;s say that we have a class called &lt;code&gt;ArticleRepository&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ArticleRepository&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BaseRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @param User $user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @return Article[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getArticlesForUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;SELECT * FROM articles WHERE user_id = :id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bindParam&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;:id&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;mapToEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Article&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Method &lt;code&gt;getArticlesForUser&lt;/code&gt; is very easy to use – its only need a &lt;code&gt;User&lt;/code&gt; object. In our system, we use this object in many places, more often in a magic way (&lt;em&gt;it make refactoring by IDE much harder&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;After some time we realized that we have to create whole &lt;code&gt;User&lt;/code&gt; object just to pass it to this method only to retrieve its &lt;em&gt;id&lt;/em&gt;. However, chaning the implementation or function header may lead to errors if we miss some occurrence. Instead, we can implement the new method and change the old one to work as a &lt;em&gt;proxy&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ArticleRepository&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BaseRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @param User $user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @return Article[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getArticlesForUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getArticlesForUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @param int $userId
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     * @return Article[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getArticlesForUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;SELECT * FROM articles WHERE user_id = :id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;bindParam&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;:id&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;mapToEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Article&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Great! We can use the new method without break anything in our code! If we had cases where we would like to use the old method, we could leave it. But let&amp;rsquo;s assume, that the &lt;code&gt;getArticlesForUser&lt;/code&gt; method isn&amp;rsquo;t vital anymore and it should be removed. Shall we do it now?&lt;/p&gt;
&lt;h3 id=&#34;deprecate-the-old-method&#34;&gt;Deprecate the old method&lt;/h3&gt;
&lt;p&gt;As we previously mentioned, we couldn&amp;rsquo;t find each place where the old method is called. In spite of all, leaving the method (even commented – who is care about comments?) in the code is like the invitation to use.&lt;/p&gt;
&lt;p&gt;Happily, thanks to the &lt;em&gt;PHPDoc&lt;/em&gt; standards, we have a &lt;code&gt;@deprecated&lt;/code&gt; annotation. Moreover, the majority of modern IDEs recognize this annotation and mark the corresponding calls to inform and discourage developers to use it.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method_hu_3759dea7b69f9375.png 360w, https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method_hu_4bd5bddb5dd0eb23.png 640w, https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method_hu_24aa7fe3414679a7.png 960w, https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method_hu_fb5c572a7e9b5168.png 1200w, https://szymonkrajewski.pl/did-your-team-defined-own-code-deprecation-strategy/016_phpstorm_showing_deprecated_method_hu_d3d9576e6c9b8365.png 1262w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1262&#34;
          height=&#34;278&#34;
           alt=&#34;PHPStorm notification about use of deprecated method.&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      PHPStorm notifies when you use deprecated methods or class.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The only thing we need is to add a &lt;code&gt;@deprecated&lt;/code&gt; annotation to the DocBlock comment above the method. It&amp;rsquo;s also recommended to add extra information about the future of this method and potential replacements.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ArticleRepository&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BaseRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;sd&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @param User $user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @return Article[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    * @deprecated in favour of {@see getArticlesForUserId}. It&amp;#39;ll be remove in the next major release.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;    */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getArticlesForUser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;User&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;trigger_error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Method getArticlesForUser is deprecated and it&amp;#39;ll be removed in the next major release. Use getArticlesForUserId instead.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;E_USER_DEPRECATED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getArticlesForUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bracket syntax within the PHPDoc means that the tag within is embedded as inline to refer to other parts of the documentation.&lt;/p&gt;
&lt;p&gt;We also put the &lt;code&gt;trigger_error&lt;/code&gt; function with preceding &lt;em&gt;silence operator&lt;/em&gt; &lt;em&gt;(@)&lt;/em&gt; to simply notify users whenever a method is executed. Thanks to this, we can set up a custom error handler dedicated to collect deprecation errors. It&amp;rsquo;ll give us a lot of useful information.&lt;/p&gt;
&lt;p&gt;A custom handler may look like below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$deprecationHandler&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$no&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$trace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;debug_backtrace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Y-m-d H:i:s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; in &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$line&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Context: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;json_encode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Trace: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;json_encode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$trace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fopen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;deprecation.log&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fputs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fclose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;set_error_handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$deprecationHandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;E_USER_DEPRECATED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, it&amp;rsquo;s only an example to show which data we can retrieve. You can use whatever you want – whatever you choose, &lt;a href=&#34;https://github.com/Seldaek/monolog&#34;&gt;Monolog&lt;/a&gt; should cover all your needs. The most interesting part is a &lt;code&gt;debug_backtrace()&lt;/code&gt;function which lets us see where the method call comes from – it&amp;rsquo;s usually done by third-party handlers like &lt;a href=&#34;https://sentry.io/for/php/&#34;&gt;Sentry&lt;/a&gt; or &lt;a href=&#34;https://www.bugsnag.com/platforms/php/&#34;&gt;Bugsnag&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;when-removing-deprecated-code&#34;&gt;When removing deprecated code?&lt;/h3&gt;
&lt;p&gt;By deprecating code we set a contract:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* @deprecated in favour of {@see getArticlesForUserId}. It&amp;#39;ll be remove in the next major release. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, please keep your word. If you don&amp;rsquo;t make it because of problems, deadlines, whatever, please update the obsolete information! There&amp;rsquo;s nothing more misleading than deprecated comments and deprecated promises about deprecated features.&lt;/p&gt;
&lt;h2 id=&#34;does-my-team-need-a-deprecation-strategy&#34;&gt;Does my team need a deprecation strategy?&lt;/h2&gt;
&lt;p&gt;You may ask: &lt;strong&gt;Does my team (or even I) need a code deprecation strategy?&lt;/strong&gt; &lt;em&gt;&amp;ldquo;My team is small enough and our project is small yet and evolve dynamically&amp;rdquo;&lt;/em&gt;. It doesn&amp;rsquo;t depend on the size of a team or even project. It&amp;rsquo;s about the maturity of every member and the whole team. Moreover, it&amp;rsquo;s related to how we solve problems.&lt;/p&gt;
&lt;p&gt;Our solutions may look like hotfix every time and there&amp;rsquo;s not a field to use a deprecation strategy. It&amp;rsquo;s not a way to work at all.&lt;/p&gt;
&lt;p&gt;By applying well–tested and checked principles to the work we can produce good quality code and solutions that are open to following improvements. Also, the whole process evolves so we can push next improvements like &lt;em&gt;deprecation strategy&lt;/em&gt; to it.&lt;/p&gt;
&lt;p&gt;If the code changes dynamically and we have to be sure that everything works as expected then we need an easy and understandable system that helps us remove old features. That&amp;rsquo;s what a deprecation strategy is about – removing the code in a save way.&lt;/p&gt;
&lt;p&gt;Maybe you develop an open–source library whose use thousands of developers. You may have tons of ideas about further development, new features and improvements to existing ones. But if you want to change something you have to give people easy to use replacement and encourage them to change. You can &lt;strong&gt;remove&lt;/strong&gt; the functionality in the next major release causing astonishment in many people. You can also &lt;strong&gt;depreciate&lt;/strong&gt; the functionality and announce the replacement to slowly accustom people to the new solution.&lt;/p&gt;
&lt;p&gt;The same situation happens when you work in a team. Developers also used to some solutions. We should encourage them to change but it needs time. That&amp;rsquo;s why it&amp;rsquo;s worth to keep the old implementation working. It&amp;rsquo;s not the only reason. It&amp;rsquo;s simply hard to perform a global change across the whole system.&lt;/p&gt;
&lt;p&gt;I loved the way how &lt;a href=&#34;http://symfony.com/doc/master/contributing/code/conventions.html#deprecation&#34;&gt;Symfony team delivers features that don&amp;rsquo;t have a backward compatibility&lt;/a&gt;. They don&amp;rsquo;t change interfaces, that could break everything. They manage by using a simple deprecation strategy. We can learn from them.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;All processes and principles should make our work easier. The deprecation strategy is like another contract between developers in the team – &lt;em&gt;I&amp;rsquo;m going to remove it, but I have a better alternative for you&lt;/em&gt;. We can also deliver new functionalities and improvements without worrying about the previous features, however, we will have to maintain one thing more.&lt;/p&gt;
&lt;p&gt;Needless to say, that keeping the codebase at the minimum is very important. Less code to read and understand make our work easier. An obsolete code is worse than legacy because the legacy code &lt;strong&gt;at least works&lt;/strong&gt; and we often know when we break something.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t hesitate to remove old code, if it&amp;rsquo;s really old and needless. But don&amp;rsquo;t forget about the &lt;a href=&#34;https://en.wikipedia.org/wiki/Principle_of_least_astonishment&#34;&gt;Principle of least astonishment&lt;/a&gt;. If something is in use but it needs to be replaced by the other, give people the alternative and depreciate the former feature. Try to encourage and accustom them to the new implementation. If it&amp;rsquo;s really better, people will catch up quickly.&lt;/p&gt;
&lt;p&gt;And how often do you remove old code? If you had joined to new team and project, where everything is completely new to you, would you like to explore the obsolete code? Probably it&amp;rsquo;ll be confusing. Therefore, maybe is worth to remove code that we don&amp;rsquo;t have in the codebase?&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/9208091/the-difference-between-deprecated-depreciated-and-obsolete&#34;&gt;The Difference Between Deprecated, Depreciated and Obsolete – Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://docs.phpdoc.org/references/phpdoc/tags/deprecated.html&#34;&gt;@deprecated annotation reference – phpDocumentator documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://php.net/manual/en/function.trigger-error.php&#34;&gt;&lt;em&gt;trigger_error&lt;/em&gt; function reference – PHP Manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://php.net/manual/en/function.set-error-handler.php&#34;&gt;&lt;em&gt;set_error_handler&lt;/em&gt; function reference – PHP Manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://symfony.com/doc/master/contributing/code/conventions.html#deprecations&#34;&gt;Symfony Coding Standards – Deprecations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Principle_of_least_astonishment&#34;&gt;Principle of least astonishment – Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Seldaek/monolog&#34;&gt;Monolog – PHP Library to build custom logging strategies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
    </item>
    
    <item>
      <title>Don’t use properties and methods from outside the interface</title>
      <link>https://szymonkrajewski.pl/dont-use-properties-and-methods-from-outside-the-interface/</link>
      <pubDate>Mon, 26 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/dont-use-properties-and-methods-from-outside-the-interface/</guid>
      <description>&lt;p&gt;It may sound obvious or even weird for people who programming in statically typed or compiled languages. In my work, I use PHP which is a dynamically typed language with optional strict typing introduced in version 7. At the beginning of my journey with PHP, I didn&amp;rsquo;t care so much about typing. I had a trivial cause — they didn&amp;rsquo;t exist yet.&lt;/p&gt;
&lt;p&gt;I used to write a code without thinking about types. It was convenient and fast. Furthermore, it allowed writing proxy functions which recognize parameters type and it runs the proper function. Yes, overriding doesn&amp;rsquo;t exist in PHP.&lt;/p&gt;
&lt;p&gt;But things changed when I discovered &lt;strong&gt;polymorphism&lt;/strong&gt;. The magic keyword &lt;code&gt;Interface&lt;/code&gt; that allows us to define the mandatory set of features in the object. The picture of writing a code without worrying about missing methods was incredible. I still didn&amp;rsquo;t understand one thing yet. How interfaces help me since I can pass everything on the function call?&lt;/p&gt;
&lt;p&gt;This question lied a long time in my head until I discovered I can define the required type of passed object. Since that time, I changed completely the way how I write the code.&lt;/p&gt;
&lt;p&gt;My first interfaces weren&amp;rsquo;t a piece of art. They were ugly, big, and sometimes overloaded by methods. However, they changed completely my approach to building applications. With time and practice, I started creating better, more consistent and useful interfaces.&lt;/p&gt;
&lt;p&gt;Unfortunately, PHP gave me a possibility to do &lt;em&gt;nasty&lt;/em&gt; things. Something, what is completely unacceptable in statically typed languages.&lt;/p&gt;
&lt;p&gt;Let me show you an example.&lt;/p&gt;
&lt;p&gt;Imagine we have the &lt;code&gt;ReadableInterface&lt;/code&gt; with could be anything where from we can read: stream, file, HTTP resource, whatever we want. Let&amp;rsquo;s say that we use one single implementation — &lt;code&gt;ReadableFile&lt;/code&gt; and we need to store parsed data in the file with a recognized name. Method &lt;code&gt;getFileName&lt;/code&gt; exists but only in this specific implementation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ReadableInterface&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$readable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;sd&#34;&gt;/** @var ReadableFile $readable **/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$fileName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$readable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getFileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$parsed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$readable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;file_put_contents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;__DIR__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fileName&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.parsed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What did I do? I lied. I lied that we can pass to the function something that is readable. Linters are happy, PHP is happy, what could go wrong? Nothing, unless we pass to this method e.g. &lt;code&gt;ReadableStream&lt;/code&gt; implementation which has no &lt;code&gt;getFileName&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This long introduction was on purpose showing the problem with polymorphism with PHP. Is technically possible to use methods and properties from outside the interface. It doesn&amp;rsquo;t mean we should do it. We should never assume that methods or properties which are not defined by interface will exist within every implementation.&lt;/p&gt;
&lt;p class=&#34;box info has-title&#34;&gt;&lt;strong&gt;I share this story just to give some context to the problem.&lt;/strong&gt;&lt;br&gt;
I also produced code like above but I stopped when I realized that is wrong. However, I sometimes notice that people confidently use methods from outside of interface just because they can and it&amp;rsquo;s easy. It&amp;rsquo;s not a good way.&lt;/p&gt;
&lt;h2 id=&#34;what-is-the-interface-for&#34;&gt;What is the interface for?&lt;/h2&gt;
&lt;p&gt;Basically, the interface is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;set of features, that object must have&lt;/li&gt;
&lt;li&gt;a contract that object delivers something but we don&amp;rsquo;t care which specific object&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But the best explanation of Interface I found on StackOverflow. Consider this situation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are in the middle of a large, empty room, when a zombie suddenly attacks you.&lt;/p&gt;
&lt;p&gt;You have no weapon.&lt;/p&gt;
&lt;p&gt;Luckily, a fellow living human is standing in the doorway of the room.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;&amp;ldquo;Quick!&amp;rdquo;&lt;/strong&gt;&lt;/em&gt; you shout at him. &lt;em&gt;&lt;strong&gt;&amp;ldquo;Throw me something I can hit the zombie with!&amp;rdquo;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You can read the full answer &lt;a href=&#34;https://stackoverflow.com/a/14244705&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What does the man ask about? About &lt;strong&gt;something&lt;/strong&gt; that implements interface! He didn&amp;rsquo;t specify what exactly but he defined his needs.&lt;/p&gt;
&lt;h2 id=&#34;what-is-the-problem-in-php&#34;&gt;What is the problem in PHP?&lt;/h2&gt;
&lt;p&gt;There is not a problem with PHP itself. Maybe a little bit. It will not stop you from using the methods or properties outside the interface.&lt;/p&gt;
&lt;p&gt;But the problem lies in us, developers. If we always treat interface as a contract, nothing wrong will happen. However, we often know what is behind the interface and it&amp;rsquo;s a potential field for abuse. Instead of using &lt;strong&gt;&amp;ldquo;something, that… (behavior)&amp;rdquo;&lt;/strong&gt; we use concrete implementation, even unconsciously. Because we can.&lt;/p&gt;
&lt;h2 id=&#34;how-to-solve-it&#34;&gt;How to solve it?&lt;/h2&gt;
&lt;p&gt;Simply, treat the &lt;em&gt;Interface&lt;/em&gt; as a &lt;strong&gt;contract&lt;/strong&gt;. Use only methods which are defined by this contract. Don&amp;rsquo;t think what it exactly is. As long as it implements the &lt;em&gt;Interface&lt;/em&gt; that you expect, it&amp;rsquo;s OK!&lt;/p&gt;
&lt;p&gt;If you really want to use something outside the interface, you should revalidate its. Is it still relevant? Maybe it&amp;rsquo;s not so useful as it should be? Or maybe should you expect a specific implementation rather than Interface?&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Needless to say, that polymorphism is a powerful tool. It is also one of the most important things in Object Oriented Programming paradigm. The described problem also involves &lt;em&gt;abstract classes&lt;/em&gt;, not only &lt;em&gt;interfaces&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There is one case where I decide to partially bypass the interface. Take a look at this example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MessageInterface&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LoggableMessageInterface&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MessageInterface&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MailerInterface&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sendTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MessageInterface&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Imagine, that we want to create &lt;code&gt;LoggableMailer&lt;/code&gt; which should be compatible with the &lt;code&gt;MailerInterface&lt;/code&gt;. This mailer is able to handle both &lt;code&gt;MessageInterface&lt;/code&gt; and &lt;code&gt;LoggableMessageInterface&lt;/code&gt; but for the second once, it implements the extra behavior. The easiest way is to check whether passed &lt;code&gt;$message&lt;/code&gt; is an instance of &lt;code&gt;LoggableMessageInterface&lt;/code&gt;. I&amp;rsquo;m still looking for a better solution.&lt;/p&gt;
&lt;p&gt;Generally speaking, we should use only methods and properties that are explicitly defined by the &lt;strong&gt;contract&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/2866987/what-is-the-definition-of-interface-in-object-oriented-programming&#34;&gt;What is the definition of “interface” in object oriented programming – Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://php.net/manual/pl/language.oop5.interfaces.php&#34;&gt;Object Interfaces – php.net manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/n4PlKsbu5RQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Igor Ovsyannykov&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/search/photos/buttons?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Why I removed my old accounts and why you should too</title>
      <link>https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/</link>
      <pubDate>Mon, 29 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/</guid>
      <description>&lt;p&gt;Whenever people say about New Year&amp;rsquo;s changes they, mainly think about resolutions, plans and set up goals. But this is also a great time to small retrospective both your personal and digital life.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s talk about the &lt;em&gt;digital&lt;/em&gt; once.&lt;/p&gt;
&lt;p&gt;Do you realize how many registrations have you performed over the last year? Or over the last 5 years? Who did you give your email, personal data, photos, documents or other files to? Too often, it&amp;rsquo;s too much to know about all this.&lt;/p&gt;
&lt;p&gt;Think about each website, application or service which you used and you didn&amp;rsquo;t use it for a long long time. Social profiles, cloud storage, dedicated tools and the other software. In each of them, you probably left some data. Maybe it&amp;rsquo;s not relevant and doesn&amp;rsquo;t have any value for you, but this information is about you.&lt;/p&gt;
&lt;p&gt;What if one day, someone will get access to all of these data you left in the Internet?&lt;/p&gt;
&lt;h2 id=&#34;nothing-lasts-forever&#34;&gt;Nothing lasts forever&lt;/h2&gt;
&lt;p&gt;I know, we all left a lot of data about ourselves only by browsing the Internet and our favorites sites. It&amp;rsquo;s also interesting but let&amp;rsquo;s leave this topic for another article. Instead, let&amp;rsquo;s focus on &lt;strong&gt;abandoned accounts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Services on the Internet grow quickly and it&amp;rsquo;s easy to switch to another one. All you need to do is create an account. What do you do with your old account? If nothing, it&amp;rsquo;s like me (until now).&lt;/p&gt;
&lt;h2 id=&#34;why-dont-i-want-to-remove-my-old-accounts&#34;&gt;Why don&amp;rsquo;t I want to remove my old accounts?&lt;/h2&gt;
&lt;p&gt;I had many reasons why I didn&amp;rsquo;t remove unused accounts. There are a few of them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I thought I would need it yet&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ll not be able to use the same e-mail for a new account&lt;/li&gt;
&lt;li&gt;I kept some data there and I didn&amp;rsquo;t have any better place for them&lt;/li&gt;
&lt;li&gt;My friends/family still used this service&lt;/li&gt;
&lt;li&gt;I couldn&amp;rsquo;t find an option to close the account&lt;/li&gt;
&lt;li&gt;I had a really nice username 😉&lt;/li&gt;
&lt;li&gt;Just in case&amp;hellip;&lt;/li&gt;
&lt;li&gt;Why should I delete my old accounts?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Time passed and I forgot about old accounts. I created new and abandoned old ones. Therefore, I received tons of newsletters, marketing messages, and other stuff from many companies. They knew my name, e-mail address, birthday and maybe another &lt;strong&gt;personal information&lt;/strong&gt;. Some of these probably know &lt;strong&gt;much more&lt;/strong&gt;. &lt;a href=&#34;https://haveibeenpwned.com/PwnedWebsites&#34;&gt;And nobody gives me the guarantee that my data is safe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I thought I have to do a little cleanup, but first, I had to face my doubts. That was not easy, but I figured out a good explanation for each of them.&lt;/p&gt;
&lt;h3 id=&#34;1-i-think-ill-need-it-yet&#34;&gt;1. I think I&amp;rsquo;ll need it yet&lt;/h3&gt;
&lt;p&gt;Yes, that&amp;rsquo;s the common problem. I figure out the solution &lt;a href=&#34;https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/&#34;&gt;I&amp;rsquo;ve started following a similar rule which I use with newsletters&lt;/a&gt;. If I don&amp;rsquo;t use a specific account but I think I&amp;rsquo;ll use it in the future I add it to the quarantine. If I don&amp;rsquo;t use it during this time, I&amp;rsquo;ll remove it.&lt;/p&gt;
&lt;h3 id=&#34;2-ill-not-be-able-to-use-the-same-e-mail-account-for-a-new-account&#34;&gt;2. I&amp;rsquo;ll not be able to use the same e-mail account for a new account&lt;/h3&gt;
&lt;p&gt;How likely is that I&amp;rsquo;ll come back to this service in the nearest future? If the probability is high I leave an account as is but I remove my data from it.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/04_twitter_existing_email.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/04_twitter_existing_email_hu_d178e0298f0e9a77.png 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/04_twitter_existing_email_hu_dd5b4bfaf3ef4873.png 426w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;426&#34;
          height=&#34;175&#34;
           alt=&#34;twitter email have been already taken&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Information on Twitter site.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you use &lt;em&gt;Gmail&lt;/em&gt;, you can modify your existing email address &lt;a href=&#34;https://gmail.googleblog.com/2008/03/2-hidden-ways-to-get-more-from-your.html&#34;&gt;by adding some dots or a plus sign&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Maybe it&amp;rsquo;s worth to put this account to the quarantine, the same as in the &lt;em&gt;1st&lt;/em&gt; point.&lt;/p&gt;
&lt;h3 id=&#34;3-i-keep-some-data-there-and-i-dont-have-any-better-place-for-them&#34;&gt;3. I keep some data there and I don&amp;rsquo;t have any better place for them&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve rejected this excuse quickly. My computer, backups or cloud storage on an account which I use regularly. Abandoned accounts aren&amp;rsquo;t a good place for my data.&lt;/p&gt;
&lt;h3 id=&#34;4-my-friendsfamily-still-use-this-service&#34;&gt;4. My friends/family still use this service&lt;/h3&gt;
&lt;p&gt;Do I lose a contact with my friends or family if I delete my account? Probably not. Moreover, don&amp;rsquo;t I use this account anymore, yes? So, I probably already have an alternative way of communication. There is no excuse.&lt;/p&gt;
&lt;h3 id=&#34;5-i-couldnt-find-an-option-to-close-the-account&#34;&gt;5. I couldn&amp;rsquo;t find an option to close the account&lt;/h3&gt;
&lt;p&gt;Some old services don&amp;rsquo;t have an option to close or remove the account. The only solution is to write an email to the help desk with an account removing requests.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_a51d3b2eb652d956.webp 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_eea359bf2f097a80.webp 640w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_e585c57c2286548f.webp 942w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_60bfb952845265d3.jpg 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_13ae8ca83f1296a7.jpg 640w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_how_can_i_remove_account_google_hints_hu_59890f65d9bb860.jpg 942w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;942&#34;
          height=&#34;702&#34;
           alt=&#34;google search hints how to remove&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      eople are looking for how to delete a specific account. Shouldn&amp;rsquo;t be this option easier to find?
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you have a problem with finding an option to remove an account, you can check the site called &lt;a href=&#34;http://justdelete.me&#34;&gt;&lt;em&gt;Justdelete.me&lt;/em&gt;&lt;/a&gt;. It is &lt;em&gt;&amp;ldquo;a directory of direct links to delete your account from web services&amp;rdquo;&lt;/em&gt; with most popular services and useful information how to perform the account deletion process.&lt;/p&gt;
&lt;h3 id=&#34;6-i-have-a-really-nice-username&#34;&gt;6. I have a really nice username&lt;/h3&gt;
&lt;p&gt;It wasn&amp;rsquo;t the most important factor in my case, but maybe in your case will it be?&lt;/p&gt;
&lt;p&gt;If you are proud you have an account with a cool username, such as your first name, a single letter and so on, you can keep it. That&amp;rsquo;s your choice. Many services don&amp;rsquo;t allow reusing before used usernames. But if you don&amp;rsquo;t use an account anymore is worthwhile to keep it just for keeping the username?&lt;/p&gt;
&lt;h3 id=&#34;7-just-in-case&#34;&gt;7. Just in case&lt;/h3&gt;
&lt;p&gt;Sometimes I can&amp;rsquo;t find a proper reason why I should keep an account. Maybe this is a reason why should I delete it?&lt;/p&gt;
&lt;h3 id=&#34;8-why-should-i-delete-my-old-accounts&#34;&gt;8. Why should I delete my old accounts?&lt;/h3&gt;
&lt;p&gt;Each service may contain a lot of data about their users. It depends on what it exactly is. Since simple services don&amp;rsquo;t know too much, bigger ones like cloud storages, social media and e-mail accounts possess more data.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do a quick think exercise.&lt;/p&gt;
&lt;p&gt;Do you want to store a private information within abandoned accounts?&lt;/p&gt;
&lt;p&gt;What if someday somebody gains access to your old account? Will you regret it? Will it hurt you in some way? Will you be ashamed of data that will leak, e.g. photos, notes or archival chats? If you&amp;rsquo;re in doubt, you&amp;rsquo;re on a good way.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s summarize. You don&amp;rsquo;t need it. Nobody else shouldn&amp;rsquo;t need it.&lt;/p&gt;
&lt;h2 id=&#34;what-did-i-find-in-abandoned-accounts&#34;&gt;What did I find in abandoned accounts?&lt;/h2&gt;
&lt;p&gt;Some of my old accounts, e.g. &lt;em&gt;Avast&lt;/em&gt; or &lt;em&gt;Ubuntu One&lt;/em&gt; contain information about the system and my other devices. Moreover, &lt;em&gt;Avast&lt;/em&gt; kept more data like my last known IP address or even space on my hard disk.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_avast_information_about_system.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_avast_information_about_system_hu_130dde9273b76f68.png 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_avast_information_about_system_hu_ac9c91c1118cf135.png 640w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_avast_information_about_system_hu_4e9f38d196d3eef8.png 960w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_avast_information_about_system_hu_1bc55fd562844ac2.png 1158w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1158&#34;
          height=&#34;792&#34;
           alt=&#34;avast computer statistics&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      My avast account had some data about my old computer.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;On my old &lt;em&gt;AirDroid&lt;/em&gt; account, I found the information about previously connected devices.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_f968e73ca0a48b55.webp 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_1b2bcb7faa950210.webp 640w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_5683a3ff30567445.webp 800w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices.jpeg&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_126ffb8c2ae9e323.jpeg 360w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_a313938d528fb99b.jpeg 640w, https://szymonkrajewski.pl/why-i-removed-my-old-accounts-and-why-you-should-too/images/014_android_air_devices_hu_413e8128a57cdcb1.jpeg 800w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;800&#34;
          height=&#34;532&#34;
           alt=&#34;airdroid list of devices&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      My old devices linked to my unused AirDroid account.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Although they contain neither personal nor important data, I just simply remove these accounts. That&amp;rsquo;s the idea behind the clean-up. Besides that I found a couple of accounts which contains more interesting data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;My old e-mail account&lt;/strong&gt;, with dozens of messages and information about other accounts and services,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wunderlist.com&lt;/strong&gt; account with my old lists and tasks,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pastebin.com&lt;/strong&gt; account with plenty of notes containing archival chats, logs, code snippets and so on,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StridesApp&lt;/strong&gt; account with some old entries,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RescueTime&lt;/strong&gt; account containing information about my computer, software usage, visited pages, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmarks at Chrome&lt;/strong&gt; with tons of URLs which I&amp;rsquo;ve never visited anymore.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Above list is not completed yet, but should give you a small preview, what kind of data you could have left somewhere on the Internet.&lt;/p&gt;
&lt;h2 id=&#34;clean-up-time&#34;&gt;Clean-up time&lt;/h2&gt;
&lt;p&gt;If you decided to perform some cleaning – good choice! I&amp;rsquo;ll give you some hints at the beginning.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start by identifying your old accounts. Accounts that you used to use but you completely forgot about them. Social media, online games, forums, instant messengers, online tools and services and so on.
&lt;ul&gt;
&lt;li&gt;You can also review the list of &lt;strong&gt;connected apps&lt;/strong&gt; to your &lt;a href=&#34;https://myaccount.google.com/permissions&#34;&gt;Google&lt;/a&gt;/&lt;a href=&#34;https://www.facebook.com/settings?tab=applications&amp;amp;section=all&#34;&gt;Facebook&lt;/a&gt; or the other OpenID account.&lt;/li&gt;
&lt;li&gt;If you use a &lt;strong&gt;password manager&lt;/strong&gt;, you can run through entries to see which of you can delete.&lt;/li&gt;
&lt;li&gt;Check your &lt;strong&gt;e-mail&lt;/strong&gt;. If you don&amp;rsquo;t clean your inbox regularly, you&amp;rsquo;ll be able to find a lot of useful information about lost or abandoned accounts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sign in to each of this account, download all data which is relevant to you.&lt;/li&gt;
&lt;li&gt;Remove an account. If you are in doubt, you&amp;rsquo;ll come back to my list to see how I deal with it. Or you can write down your own reasons why you don&amp;rsquo;t want to delete old accounts.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Since we don&amp;rsquo;t have any special reason why we should remove old accounts, most of the people don&amp;rsquo;t care about them. But data breaches are real and we have to be aware of them. The fewer places where our data are, the lower chance of the potential leak is.&lt;/p&gt;
&lt;p&gt;Sometimes it&amp;rsquo;s hard to get rid of all the old accounts because not every service gives a possibility to remove the account. But after 25 May 2018, when &lt;a href=&#34;https://gdpr-info.eu&#34;&gt;&lt;strong&gt;GDPR&lt;/strong&gt; (&lt;em&gt;General Data Protection Regulation&lt;/em&gt;)&lt;/a&gt; becomes effective, we as users and customers will give a powerful right called &lt;a href=&#34;https://gdpr-info.eu/art-17-gdpr/&#34;&gt;&lt;em&gt;right to erasure&lt;/em&gt;&lt;/a&gt;. Time will show, whether it&amp;rsquo;ll be effective or not.&lt;/p&gt;
&lt;p&gt;I encourage you to take the responsibility of your digital life in your own hands. You can follow this simple rule: &lt;em&gt;&amp;ldquo;If you don&amp;rsquo;t need it, remove it.&amp;rdquo;&lt;/em&gt;. We&amp;rsquo;re living in the digitalized world where every piece of information matters. We shouldn&amp;rsquo;t disregard it.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://haveibeenpwned.com/&#34;&gt;Have i been pwned? by Troy Hunt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gdpr-info.eu&#34;&gt;General Data Protection Regulation – gdpr-info.eu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gdpr-info.eu/art-17-gdpr/&#34;&gt;Art. 17 GDPR – Right to erasure (&lt;em&gt;right to be forgotten&lt;/em&gt;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://backgroundchecks.org/justdeleteme/&#34;&gt;Justdelete.me by BackgroundChecks.org – A directory of direct links to delete your account from web services.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured Photo by &lt;a href=&#34;https://unsplash.com/photos/9ShzlH_o2Yk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Markus Clemens&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Inbox zero – efficient way to manage emails</title>
      <link>https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/</link>
      <pubDate>Mon, 08 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/</guid>
      <description>&lt;p&gt;Every day at least 30 new messages land in my mailbox. Newsletters, reminders, status updates, mentions, recommendations and even personal or business messages – each of them need my time and an attention. And no one besides me is responsible for handling them.&lt;/p&gt;
&lt;p&gt;I had my very first email account on the public news site. I received tons of advertising messages until I got the invitation to the Gmail. This mail service was capable of identifying unwanted messages. That was what I have been looking for for a long time.&lt;/p&gt;
&lt;p&gt;Although I receive a bunch of messages each day (more important than spam or advertisements), at the end of the day my inbox is empty. Thanks to &lt;strong&gt;Inbox Zero&lt;/strong&gt; approach, I manage my matters without wasting time. It helps me preventing myself from being overwhelming by a number of emails.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t have any special system for managing my inbox. I deleted less important messages. Sometimes I left them in inbox without the reason or if messages have looked non-trivial. That way I had all messages marked as reading but the counter still shows more than 1000 messages in the inbox. It seemed to me, that these messages would be useful in the future. The fear that I&amp;rsquo;ll need something I before removed kept me from deleting. I needed something more efficient.&lt;/p&gt;
&lt;h2 id=&#34;what-is-inbox-zero&#34;&gt;What is inbox zero?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Inbox zero&lt;/em&gt; is one of this system. The system that helps you keep your inbox under control. It doesn&amp;rsquo;t take a lot of time, it only requires a lot of discipline. First, Inbox zero assumed, that email is only a communication channel. Each incoming message should be proceed until the empty inbox.&lt;/p&gt;
&lt;p&gt;Instead of interrupting by every message that arrives in your inbox, you should check for new emails and process them in a bulk. The list of operations you can perform on a single letter is as below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DELETE&lt;/li&gt;
&lt;li&gt;DELEGATE&lt;/li&gt;
&lt;li&gt;RESPOND&lt;/li&gt;
&lt;li&gt;DEFER&lt;/li&gt;
&lt;li&gt;DO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see the list is short but it contains all actions that are you need to keep your matters under control. I&amp;rsquo;m going to explain each one later in the article.&lt;/p&gt;
&lt;h2 id=&#34;advantage-of-inbox-zero-approach&#34;&gt;Advantage of Inbox zero approach&lt;/h2&gt;
&lt;p&gt;Treating email only as communication channel decreases the number of places where you can look for the next task to do. Although keeping information in the inbox may look convenient, it may lead to negative results, e.g. procrastination. Checking your inbox without special reason and seeing the same messages, again and again, force your subconscious mind to work on them. Even if it&amp;rsquo;s not necessary at this moment. Each day you see a lot of information that requires processing, so why you want to add another one?&lt;/p&gt;
&lt;p&gt;The second advantage is feeling, that you have everything under control. Imagine, that you look to your inbox and the counter shows 0 messages. Give yourself one minute and try to do this exercise in your head. How do you feel? Relaxed, easy, calm? Or maybe anxious, scared, afraid? First time can be strange but it&amp;rsquo;s normal, especially when you are not used to deleting or reading each email. Not processed emails are an invitation to losing time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;I don&amp;rsquo;t completely read this newsletter&amp;rdquo;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Nice photo, I remember that&amp;rdquo;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;I forgot to answer&amp;rdquo;&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;I read this funny story once again&amp;rdquo;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;rsquo;t let yourself waste a time. Take a look from the other side. &lt;strong&gt;The empty list of messages should tell you that you probably run a wrong program&lt;/strong&gt;. Or maybe you check the e-mail too often? If it&amp;rsquo;s nothing to do within your inbox you&amp;rsquo;ll go to the next task.&lt;/p&gt;
&lt;p&gt;Another advantage is searching. The more messages you delete, the easier potential seeking is. It doesn&amp;rsquo;t matter how advanced search engines we have. Sometimes there will show you a message that you don&amp;rsquo;t need but it can attract your attention (and time).&lt;/p&gt;
&lt;p&gt;I always leave my inbox empty. That makes me more relaxed and happier. I know all things to do I&amp;rsquo;ll find in other, more reliable place, designed for planning. Moreover, I don&amp;rsquo;t miss something important. For me email is for communication, not to organize life.&lt;/p&gt;
&lt;h2 id=&#34;how-to-work-with-inbox-zero&#34;&gt;How to work with Inbox zero?&lt;/h2&gt;
&lt;h3 id=&#34;1-clear-your-inbox&#34;&gt;1. Clear your inbox&lt;/h3&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_6762441b4ace25d3.webp 360w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_46d18a32e2cd6b86.webp 640w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_7c71e9ad13329a0b.webp 960w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_87f42e52079ca31e.webp 1200w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_82838626a8aa207d.webp 1360w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_3dac5e8a8bc758fc.jpg 360w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_f7135597b2e85537.jpg 640w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_c4468c36fe5ff1ab.jpg 960w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_2613fb20fe9e2908.jpg 1200w, https://szymonkrajewski.pl/inbox-zero-efficient-way-to-manage-emails/images/google_inbox_empty_hu_84730797b5c1712c.jpg 1360w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1360&#34;
          height=&#34;732&#34;
           alt=&#34;Empty inbox in Inbox by Google&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Inbox by Google – Do you recognize this screen?
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you want to try Inbox zero approach you have to clear your inbox. You have to realize. It&amp;rsquo;s inbox &lt;strong&gt;ZERO&lt;/strong&gt;. Nothing can be in your inbox. You have to realize that you don&amp;rsquo;t need all these messages. It&amp;rsquo;s not a cloud drive. Your photos, documents, and other important stuff can stay in the dedicated cloud drive, like Dropbox, Google Drive. I have tips for you – there are 3 different ways how to reach empty inbox:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hardcore version – select all your messages and delete them. Fastest and easiest approach but I don&amp;rsquo;t know if somebody went this way.&lt;/li&gt;
&lt;li&gt;Select messages older than 3 months and delete them. You have to revise the rest of emails and process them. You can adjust the time to yourself, but don&amp;rsquo;t be so sensitive. If you don&amp;rsquo;t need these messages for 3 months, probably you&amp;rsquo;ll never need there in the future.&lt;/li&gt;
&lt;li&gt;Hardcore version – process all your messages.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The choice is yours. It depends on how much time you&amp;rsquo;re going to spend sorting your messages and also how important information lay in your inbox.&lt;/p&gt;
&lt;h3 id=&#34;2-dont-sit-in-your-inbox&#34;&gt;2. Don&amp;rsquo;t sit in your inbox&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s nothing interesting to do in your inbox. Don&amp;rsquo;t waste the time keeping the mail application open waiting for the next message. Instead, keep your email app closed and check it for new emails from time to time. Email is not a real-time communication. You don&amp;rsquo;t have to respond immediately (unless your work or life requires that).&lt;/p&gt;
&lt;h3 id=&#34;3-turn-off-email-notifications&#34;&gt;3. Turn off email notifications&lt;/h3&gt;
&lt;p&gt;Notifications are everywhere. Sometimes are helpful but most of them are useless. Notification is a guy, who interrupts you when you are in the middle of the ground-breaking idea. Nobody wants to be distracted in this moment. You probably either so don&amp;rsquo;t let interrupt yourself.&lt;/p&gt;
&lt;p&gt;Each time you got a notification, you switch the context – and &lt;a href=&#34;https://www.petrikainulainen.net/software-development/processes/the-cost-of-context-switching/&#34;&gt;context switching is the productivity killer&lt;/a&gt;. Moreover, switching to process only one single email is a waste of time. Avoid it. Multitasking works in the car, but it&amp;rsquo;s no efficient during the work.&lt;/p&gt;
&lt;h3 id=&#34;4-unsubscribe-newsletters-that-you-dont-even-read&#34;&gt;4. Unsubscribe newsletters that you don&amp;rsquo;t even read&lt;/h3&gt;
&lt;p&gt;Do you ever know how many newsletters you subscribe? How many of them do you read? Or maybe you open them for marking as reading? If you got newsletters that you know you don&amp;rsquo;t read – unsubscribe them. If there is no option for that, mark it as a spam. When you have a doubt, you&amp;rsquo;ll do this simple trick: check how many times in a row you don&amp;rsquo;t read the newsletter. If it&amp;rsquo;s more than 3, you&amp;rsquo;ll unsubscribe it.&lt;/p&gt;
&lt;h3 id=&#34;5-dont-leave-a-message-without-the-action&#34;&gt;5. Don&amp;rsquo;t leave a message without the action&lt;/h3&gt;
&lt;p&gt;Now, I&amp;rsquo;m going to say more about actions you can perform on a single message, but instead of focusing on each operation I&amp;rsquo;ll focus on the flow instead.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the email doesn&amp;rsquo;t need any action – &lt;strong&gt;DELETE&lt;/strong&gt; or &lt;strong&gt;ARCHIVE&lt;/strong&gt; it.&lt;/li&gt;
&lt;li&gt;If the action will take &lt;strong&gt;less than 2 minutes&lt;/strong&gt;:
&lt;ol&gt;
&lt;li&gt;simply &lt;strong&gt;RESPOND&lt;/strong&gt;. Try to write short emails.&lt;/li&gt;
&lt;li&gt;simply &lt;strong&gt;DO&lt;/strong&gt;. That&amp;rsquo;s probably not a big deal. 2 minutes of your time is nothing compared to the time that your mind spend to thinking about it.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;If the action will take &lt;strong&gt;more than 2 minutes&lt;/strong&gt;:
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DELEGATE&lt;/strong&gt;, if this task can be done by someone else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DEFER&lt;/strong&gt;, and I recommend place other than the inbox. Extract the most important part of the message, redefine it in your own way and put it into reliable place:
&lt;ol&gt;
&lt;li&gt;Task list, if it&amp;rsquo;s a project or bigger task to do.&lt;/li&gt;
&lt;li&gt;Calendar, if it&amp;rsquo;s an appointment or something it&amp;rsquo;s scheduled.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This flow let you take a decision for each message you receive.&lt;/p&gt;
&lt;h3 id=&#34;6-dont-leave-your-inbox-without-processing-all-messages&#34;&gt;6. Don&amp;rsquo;t leave your inbox without processing all messages&lt;/h3&gt;
&lt;p&gt;Once you notice something in your inbox, you open a new &amp;ldquo;&lt;em&gt;loop&lt;/em&gt;&amp;rdquo; that resides in your mind. Although you don&amp;rsquo;t think about it, your subconscious mind process it taking resources.&lt;/p&gt;
&lt;p class=&#34;box info has-title&#34;&gt;&lt;strong&gt;What is loop?&lt;/strong&gt;&lt;br&gt;
&amp;ldquo;Loop&amp;rdquo; comes from &lt;em&gt;&amp;quot;&lt;a href=&#34;https://en.wikipedia.org/wiki/Getting_Things_Done&#34;&gt;Getting Things Done&lt;/a&gt;&amp;quot;&lt;/em&gt; philosophy David Alen. It means a matter that has not been completed and still sits in your head, even if you don&amp;rsquo;t think about it. Sometimes it comes back for a while in random moments and it can break your way of thinking.&lt;/p&gt;
&lt;p&gt;Try to close as many loops as you can even if it means that you have to &lt;strong&gt;DEFER&lt;/strong&gt; something for later (outside the inbox). It&amp;rsquo;s also a good decision, better than nothing. Do you want to come back to the same message again and again? If you have doubts just ask yourself – &lt;em&gt;Is any reason why I can&amp;rsquo;t process this message?&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;7-safe-peoples-time-as-much-as-yours&#34;&gt;7. Safe people&amp;rsquo;s time as much as yours&lt;/h3&gt;
&lt;p&gt;People don&amp;rsquo;t like long emails. Nobody has time to read them. Because of that, you shouldn&amp;rsquo;t write a novel – you probably also ignore this kind of emails. Be safe in words and try to write the message that you want to receive (when it comes to the volume, not the content). Don&amp;rsquo;t beat around the bush.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not a piece of advice about the email netiquette. I assume, that you already know how proper email should look. Short messages are easier to digest and process. Keeping it simple gives you a chance, that your letter doesn&amp;rsquo;t fall down to the bottom of the inbox.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Although I don&amp;rsquo;t process hundreds of e-mails every day, I feel that I work in the most efficient way with my inbox. After introducing this system into my life I spend less time in the mail app. I also stop worrying about things I didn&amp;rsquo;t complete. Paradoxically, I check my inbox less frequent and I spend much less time than before. I&amp;rsquo;m able to process more emails and quickly getting things done.&lt;/p&gt;
&lt;p&gt;This system is not for all kinds of job. If you have to have continuous contact with your customers, you work at help desk or support or your job is strongly connected to e-mail – I&amp;rsquo;m sorry, it happens. But if you are lucky and your job doesn&amp;rsquo;t need yours all the time sitting in your mailbox, that&amp;rsquo;s great. If you have read this text and it interests you, maybe it&amp;rsquo;s a good reason to give yourself a chance to check it in practice. Perhaps you&amp;rsquo;ll discover other benefits (or defect). I would like to hear what do you think about it and how it works for you.&lt;/p&gt;
&lt;p&gt;I have to also remind, that I&amp;rsquo;m not a coach or a personal trainer. I&amp;rsquo;m sharing only things that I tested on myself and that work for me. Please note, that everything I have written here  dapted to my lifestyle. I can&amp;rsquo;t promise, that all them will work for you but I&amp;rsquo;ll keep my fingers crossed. So, do you already know when you&amp;rsquo;ll be cleaning your inbox?&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://youtu.be/z9UjeTMb3Yk&#34;&gt;Merlin Mann at Google Tech Talk (video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://gettingthingsdone.com/fivesteps/&#34;&gt;Getting Things Done Methodology&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/vwwURFt-HNk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Xavier Massa&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Building the associative array – ideas</title>
      <link>https://szymonkrajewski.pl/building-the-associative-array-ideas/</link>
      <pubDate>Mon, 18 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/building-the-associative-array-ideas/</guid>
      <description>&lt;p&gt;When we talk about arrays we usually mean the &lt;strong&gt;vector&lt;/strong&gt; of something – primitives, objects or even arrays. But there are a lot of situation when we need to carry extra information with our data. We can use nested arrays but it doesn&amp;rsquo;t cause that the specific item within the array will be easy to identify. To achieve it we should use custom keys. They provide easy access to any element of the array as long as we know the key corresponding with that value.&lt;/p&gt;
&lt;p&gt;There are some reasons why we can use associative arrays instead of simple vectors, e.g. readability. Named keys are also more meaningful than numeric indexes. Instead of thinking about the whys, image the situation when we could use a hashmap. Let&amp;rsquo;s focus on how we can build the associative array from a vector.&lt;/p&gt;
&lt;h2 id=&#34;example-scenario&#34;&gt;Example scenario&lt;/h2&gt;
&lt;p&gt;In our application, we have users from different parts of the world. During the registration process, the user can select its own country. In our database, we store information about the country in the &lt;em&gt;ISO-3361-alpha-1&lt;/em&gt; format. The user should see the full country name instead.&lt;/p&gt;
&lt;p&gt;We actually have an array of &lt;code&gt;Country&lt;/code&gt; objects, which definition is presented below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Country&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One of our frontend components can render &lt;code&gt;Combo&lt;/code&gt; (knew as the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; node) and it expects associative array as an input in format &lt;code&gt;[&#39;value&#39; =&amp;gt; &#39;text visible for user&#39;]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We need to create this array.&lt;/p&gt;
&lt;h3 id=&#34;approach-0-the-basic-loop&#34;&gt;Approach #0: the basic loop&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;prepareCountriesForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$countriesSelect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;prepareCountriesForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The most basic approach I know. I often question solutions which use the raw loops because they are low-level being. But if they are encapsulated within the well-described function, that&amp;rsquo;s O.K.&lt;/p&gt;
&lt;p&gt;Creating a function designed to produce specific data for other component seems to be a nice idea. Let&amp;rsquo;s consider that we need to deliver data to different &lt;code&gt;Combo&lt;/code&gt;. It would be nice to use the same function but with different data. I need to generalize this approach.&lt;/p&gt;
&lt;h3 id=&#34;approach-1-generic-way-to-produce-associative-array&#34;&gt;Approach #1: Generic way to produce associative array&lt;/h3&gt;
&lt;p&gt;Besides the array containing the data, we need to pass two extra functions. The former will be responsible for transforming the element to the text visible for a user and the latter will transform the element to the key of the associative map.&lt;/p&gt;
&lt;p&gt;The function could look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$countriesSelect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  	&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  	&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks pretty good and probably we should stop discussing more this boring feature. After all, let&amp;rsquo;s try to find other implementations for this specific case.&lt;/p&gt;
&lt;h3 id=&#34;approach-2-use-array-map-functions&#34;&gt;Approach #2: use array map functions&lt;/h3&gt;
&lt;p&gt;I really like array functions. They are so powerful. We can transform keys and values of the associative array independently and we can combine them into the single array.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$keys&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$values&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_combine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$keys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$values&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$countriesSelect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code is much shorter, but under the hood, PHP has more work. Although there is no any visible loop in the userland, PHP engine must iterate over the array at least 3 times (twice for map and once for combine). It seems slow and not so efficient.&lt;/p&gt;
&lt;h3 id=&#34;approach-3-array-map-by-reduce&#34;&gt;Approach 3: array map by reduce&lt;/h3&gt;
&lt;p&gt;The next approach may be to use an &lt;code&gt;array_reduce&lt;/code&gt; function instead fo mapping. What this function exactly doing I describe &lt;a href=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/&#34;&gt;in one of my previous posts&lt;/a&gt;. Simply, this approach is to reducing array to the single value. But nobody said that the single value couldn&amp;rsquo;t be an array!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Closure&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fnKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fnValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$element&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$countriesSelect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mapForCombo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$countries&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getCode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Country&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$country&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only thing we should do is to create a function which will assign the transformed value to specific key in the array (similar as on 1st approach). The loop logic is hidden within the &lt;code&gt;array_reduce&lt;/code&gt; function so only care about the correct assignment.&lt;/p&gt;
&lt;p&gt;Which solution is better?&lt;/p&gt;
&lt;h2 id=&#34;tests&#34;&gt;Tests&lt;/h2&gt;
&lt;p&gt;Personally, I don&amp;rsquo;t like do and read benchmarks for things that usually doesn&amp;rsquo;t matter. Otherwise, I collect some ideas and I want to validate them in such way. That&amp;rsquo;s why I test which implementation work faster.&lt;/p&gt;
&lt;h3 id=&#34;test-file-and-environment&#34;&gt;Test file and environment&lt;/h3&gt;
&lt;p&gt;I prepared a simple test file, you can view it on &lt;a href=&#34;https://gist.github.com/skrajewski/7836843da573df5eb19439e77dad23fe&#34;&gt;this GitHub Gist&lt;/a&gt;. Although it&amp;rsquo;s not so complicated, it&amp;rsquo;s enough to see which implementation is better, relatively to each other. In short – I execute each function 5000 times and I measure the duration of the execution.&lt;/p&gt;
&lt;p&gt;First I tried to run this script on my local environment, but I came to the conclusion, that the bettser option is to use a completely different server. I rented a droplet &lt;em&gt;2GB RAM / 2 CPU&lt;/em&gt; from &lt;em&gt;DigitalOcean&lt;/em&gt; which ready-to-use &lt;em&gt;LAMP&lt;/em&gt; Stack on board and I run the test.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@lamp-2gb-nyc3-01:~# php -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PHP 7.0.22-0ubuntu0.16.04.1 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;cli&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt; NTS &lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The test generates an output which I copied and paste as a CSV to the Excel. It allows me to easily calculate the average time and plot a simple chart.&lt;/p&gt;
&lt;h3 id=&#34;test-results&#34;&gt;Test results&lt;/h3&gt;
&lt;p&gt;I have to admit, that results were really surprising to me. First, please take a look at the final outcomes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Approach #1&lt;/em&gt; – &lt;code&gt;foreach&lt;/code&gt; – &lt;strong&gt;0,283s&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Approach #2&lt;/em&gt; – &lt;code&gt;array_map&lt;/code&gt; – &lt;strong&gt;0,253s&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Approach #3&lt;/em&gt; – &lt;code&gt;array_reduce&lt;/code&gt; – &lt;strong&gt;1,054s&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Differences are more visible on a simple bar chart.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_71431229b77dd12c.png 360w, https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_961495466793c1c0.png 640w, https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_a40db8f3d351f15d.png 960w, https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_896d8d69a820eb6f.png 1200w, https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_636ba0d52aa319bc.png 1600w, https://szymonkrajewski.pl/building-the-associative-array-ideas/images/013_test_result_chart_hu_b3adc801ec4674ba.png 1755w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1755&#34;
          height=&#34;1054&#34;
           alt=&#34;chart preseting results of tests&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The chart presenting average duration of each approach.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;My first impression was something like a mixture of confusion with misunderstanding. The solution that seemed to be the best turned out, that it was the least efficient. It shows, that we should always test and validate our idea.&lt;/p&gt;
&lt;p&gt;I personally use the &lt;code&gt;array_reduce&lt;/code&gt; approach quite often. Fortunately, the test was deliberately exaggerated to shows differences between each approach. For smaller datasets, differences are not noticeable.&lt;/p&gt;
&lt;p&gt;It doesn&amp;rsquo;t mean, that you should not use the &lt;code&gt;array_reduce&lt;/code&gt; function. It has specific use cases and meaning so don&amp;rsquo;t hesitate to use this function if it fills your needs.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m not sure, whether this experiment will change the way how I write the code. To be honest, it&amp;rsquo;s only a micro-optimisation on a bigger scale. Although sometimes it does matter, e.g. if you have to write high-efficient algorithms, for most of us it&amp;rsquo;s just an unnecessary concern.&lt;/p&gt;
&lt;p&gt;Otherwise, it reveals something completely different. We believe that things &lt;strong&gt;work faster because it seems to be a faster.&lt;/strong&gt; They execute fewer loops and they contain much less complicated logic and conditions. This example shows that there is no truth in all cases.&lt;/p&gt;
&lt;p&gt;As you notice, there isn&amp;rsquo;t a golden rule to how to transform data to the associative array. Simplicity is the key, but we always should test, confirm and compare our solutions with other. It&amp;rsquo;s not only about the array transformation but also about many other things. Maybe your next solution will be better, easier and faster?&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/ISO_3166-1&#34;&gt;ISO 3166-1 at Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/RBGKew/Reconciliation-and-Matching-Framework/blob/master/reconciliation-service/dictionaries/iso-3166-1-alpha-2-countries.tsv&#34;&gt;The file containing list of Countries with corresponding ISO 3166-1 codes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/82TpEld0_e4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;G. Crescoli&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>What is idempotence?</title>
      <link>https://szymonkrajewski.pl/what-is-idempotence/</link>
      <pubDate>Mon, 04 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/what-is-idempotence/</guid>
      <description>&lt;p&gt;If you frown after reading &lt;em&gt;idempotence,&lt;/em&gt; don&amp;rsquo;t worry. Although this word sounds unusual it conceals a simple mathematics property. Needless to say, this characteristic has also a big meaning in Computer Science.&lt;/p&gt;
&lt;p&gt;Idempotence operation is an operation that can be applied multiple times without changing the result beyond the initial application. It may sound difficult but after a couple of examples, the general concept will be obvious.&lt;/p&gt;
&lt;p&gt;To be honest, I spent a lot of time looking for proper examples of idempotent functions or operations. Although I caught the idempotence concept very fast, I wanted to go deeper. I tried to understand not only what it is but also why does it matter. I&amp;rsquo;ll share my thoughts on this article.&lt;/p&gt;
&lt;h2 id=&#34;idempotence-in-mathematics&#34;&gt;Idempotence in mathematics&lt;/h2&gt;
&lt;p&gt;In math, each function produces a certain output. This is the reason why we ever use functions – we pass input arguments and we expect an output value. If we pass the same argument, again and again, each time we&amp;rsquo;ll get the same output. It means that &lt;strong&gt;math functions are pure&lt;/strong&gt; – their output depends only on the input arguments.&lt;/p&gt;
&lt;p&gt;$$f(x) = y$$&lt;/p&gt;
&lt;p&gt;Now, imagine that we call the function with a certain argument. It produces a specific output depends on the input value. Let&amp;rsquo;s call the function again, but for the input value, use the result from the previous execution.&lt;/p&gt;
&lt;h3 id=&#34;idempotent-functions&#34;&gt;Idempotent functions&lt;/h3&gt;
&lt;p&gt;If the output doesn&amp;rsquo;t change anymore, the function is &lt;strong&gt;idempotent&lt;/strong&gt;. Only the first execution of function produces a new value. Each subsequent call returns the same value, no matter how many times we call the function. In other words, the function fulfills below equation.&lt;/p&gt;
&lt;p&gt;$$f(f(x)) == f(x)$$&lt;/p&gt;
&lt;p&gt;Do you know the example of an often used idempotent function? The well-knowing example is the &lt;strong&gt;absolute value&lt;/strong&gt;. For each negative value as an input &lt;code&gt;abs&lt;/code&gt; returns its positive counterpart. For each positive value, it returns input argument as is.&lt;/p&gt;
&lt;p&gt;$$abs(abs(-5)) == abs(-5)$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pure function and idempotent function are not the same. Function can be pure, but not idempotent, e.g. &lt;code&gt;f(x) = x + 1&lt;/code&gt;. Essentially, f(x) is pure because its output depends only on the input argument. But it&amp;rsquo;s not idempotent because it not fulfills the condition &lt;code&gt;f(f(x)) == f(x)&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;idempotent-operations&#34;&gt;Idempotent operations&lt;/h3&gt;
&lt;p&gt;Under certain conditions, we can recognize some mathematical operations as an idempotent. Take a look at these two binary operations: &lt;em&gt;addition&lt;/em&gt; and &lt;em&gt;multiplication&lt;/em&gt;. They are not fully idempotent. But if one of the argument is an &lt;strong&gt;identity element&lt;/strong&gt;, the operation is idempotent.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For addition, the identity element is equal to 0 (zero). Whatever adds to zero, we&amp;rsquo;ll get the same value.&lt;/p&gt;
&lt;p&gt;$$5 + 0 = 5$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For multiplication, the identity element is equal to 1 (one). Something times one always returns that &amp;ldquo;something&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;$$5 * 1 = 5$$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It doesn&amp;rsquo;t matter how many times we&amp;rsquo;ll perform the operation with identity element in the pair. Each time we&amp;rsquo;ll get the same output.&lt;/p&gt;
&lt;h2 id=&#34;idempotence-in-programming&#34;&gt;Idempotence in programming&lt;/h2&gt;
&lt;p&gt;In programming, we have a lot of pure and idempotent functions in mathematical sense, e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;floor / ceil&lt;/li&gt;
&lt;li&gt;lowercase / uppercase&lt;/li&gt;
&lt;li&gt;trim&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In many programming languages, functions do much more operations beside returning an output. Sometimes they return nothing. Another time they return different results, although we use the same input arguments.&lt;/p&gt;
&lt;p&gt;But why? What is the difference between the mathematical function and the programming languages functions?&lt;/p&gt;
&lt;h3 id=&#34;side-effects&#34;&gt;Side effects&lt;/h3&gt;
&lt;p&gt;Of course, we can place maths functions within our program and everything will be fine. But in most cases, we use functions to &lt;strong&gt;manage the state&lt;/strong&gt;. These functions that depend not only on the input arguments. They also use something outside its local scope (e.g. &lt;em&gt;global state&lt;/em&gt;) so they &lt;strong&gt;are no longer pure&lt;/strong&gt; – they have &lt;strong&gt;side effects&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The function has &lt;strong&gt;side effects&lt;/strong&gt; when&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it reads or modifies the global state&lt;/li&gt;
&lt;li&gt;writes or reads data from I/O or database&lt;/li&gt;
&lt;li&gt;uses a timestamp, current date&lt;/li&gt;
&lt;li&gt;uses something that has effects beyond it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But side effects doesn&amp;rsquo;t exclude function to be idempotent. We&amp;rsquo;ll take a closer look at the non-pure but idempotent function.&lt;/p&gt;
&lt;h3 id=&#34;idempotent-functions-in-programming&#34;&gt;Idempotent functions in programming&lt;/h3&gt;
&lt;p&gt;In programming, a function is idempotent, when we can call it many times with the same arguments and the &lt;strong&gt;result won&amp;rsquo;t change beyond initial calling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It is close enough to the mathematical definition but instead of applying function, we execute it many times.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s assume, we have an array of figures. Now, we&amp;rsquo;ll sort this array in PHP language. Please notice, that &lt;a href=&#34;http://php.net/manual/en/function.sort.php&#34;&gt;PHP&amp;rsquo;s sort function&lt;/a&gt; expected input array as a reference. That means, it modifies the variable outside its scope, so the &lt;code&gt;sort&lt;/code&gt; function has the side effect.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$numbers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// [1, 2, 3, 4, 5]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// [1, 2, 3, 4, 5]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// [1, 2, 3, 4, 5]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It doesn&amp;rsquo;t matter how many times we&amp;rsquo;ll call a &lt;code&gt;sort&lt;/code&gt; function. We&amp;rsquo;ll always get the same outcome. The same for well-knowing &lt;strong&gt;setters&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Simon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Szymon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Only the first call of setter will change the name. If we use it many times, even by mistake, nothing bad will happen.&lt;/p&gt;
&lt;h2 id=&#34;idempotence-in-http&#34;&gt;Idempotence in HTTP&lt;/h2&gt;
&lt;p&gt;Idempotence plays a big role in HTTP protocol. Lots of web browsers lead to the concept of idempotency and the assumption which HTTP methods are idempotent and safe. According to the official HTTP specification, &lt;em&gt;GET&lt;/em&gt; and &lt;em&gt;HEAD&lt;/em&gt; should be considered as &lt;strong&gt;safe methods&lt;/strong&gt;. Moreover, &lt;em&gt;GET&lt;/em&gt;&lt;strong&gt;,&lt;/strong&gt; &lt;em&gt;HEAD, PUT, DELETE, OPTIONS&lt;/em&gt;&lt;strong&gt;,&lt;/strong&gt; and &lt;em&gt;TRACE&lt;/em&gt; should be considered as &lt;strong&gt;idempotent methods&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;thinking-in-rest&#34;&gt;Thinking in REST&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s assume, that we have a resource. When we read from it, nothing dangers should happen. This is exactly how &lt;strong&gt;GET&lt;/strong&gt; method works. It should only read from the server and that&amp;rsquo;s why it&amp;rsquo;s safe.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note, that we talk about the official &lt;a href=&#34;https://tools.ietf.org/html/rfc2616&#34;&gt;HTTP/1.1 specification&lt;/a&gt;. Everyone could implement these methods differently, according to own needs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now, let&amp;rsquo;s consider a &lt;strong&gt;PUT&lt;/strong&gt; method. This is idempotent operation. Only the first request should affect our resource. If we&amp;rsquo;ll send an extra request with the same data, nothing destructive should happen, because data is replaced, rather than appended.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DELETE&lt;/strong&gt; is also interesting. The first DELETE request is obvious – it deletes the resource from the server. But each next DELETE request to the same resource should be ignored. The server should respond to the client by the specific code, e.g. 404. That makes this request idempotent.&lt;/p&gt;
&lt;p&gt;But not all HTTP methods work in this way. Let&amp;rsquo;s take a look at the &lt;strong&gt;POST&lt;/strong&gt; method. This method is neither idempotent nor safe. It creates the new entity within the resource, identifiable by a unique id. Each subsequent POST request will be creating the new entity with completely new identity, so this method isn&amp;rsquo;t idempotent.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_36a66568589988d1.webp 360w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_91a0315aa5de6701.webp 640w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_2535e64d1941cb33.webp 960w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_d325e250e0bcf287.webp 972w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_6b45724643d909b9.jpg 360w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_d214bd5af0cfcaa5.jpg 640w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_224be7ddcba135ff.jpg 960w, https://szymonkrajewski.pl/what-is-idempotence/images/011_confirm_send_post_again_hu_1d40131ebadd0430.jpg 972w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;972&#34;
          height=&#34;328&#34;
           alt=&#34;Browser notifies if you try to resend the form&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Browser notifies if you try to resend the form.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;It&amp;rsquo;s so important that even web browsers show an alert in the case when we try to resend before processed form. Thanks to the &lt;em&gt;&lt;a href=&#34;https://tools.ietf.org/html/rfc2616&#34;&gt;HTTP/1.1 – RFC 2616&lt;/a&gt;&lt;/em&gt;, each browser handles all methods in the same way.&lt;/p&gt;
&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Idempotence is not a magic tool that we should use to build better things. It&amp;rsquo;s a &lt;strong&gt;property&lt;/strong&gt; of some functions and operations that protect us from errors caused by repetition. Very often we don&amp;rsquo;t even need this characteristic within your own functions. That&amp;rsquo;s O.K. Idempotence plays a big role not only in math and computer science but also in the real world and it often went unnoticed.&lt;/p&gt;
&lt;p&gt;Did you notice the featured image? What will happen if we press the same button in an elevator again? In elevators I know, nothing will happen. The floor will be still highlighted and the elevator does its work.&lt;/p&gt;
&lt;p&gt;Buttons at pedestrian crossing work in the same way. It doesn&amp;rsquo;t matter how long you keep it pressed or how many times you push it. The very first call (in each cycle, of course) is important.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_7e4000da5c510ea9.webp 360w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_5273e81c895e8bc2.webp 640w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_d67f3651cf26e582.webp 960w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_ae9c9fde4be454d5.webp 1200w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_f2dcf876c2afcd37.webp 1280w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_b32de28d167707c8.jpg 360w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_57c97b0132250ce4.jpg 640w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_e4395b4fa9f4c608.jpg 960w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_47b652375890b0e7.jpg 1200w, https://szymonkrajewski.pl/what-is-idempotence/images/011_pedestrian_crossing_button_hu_b85407ccf73bd54.jpg 1280w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1280&#34;
          height=&#34;849&#34;
           alt=&#34;pedestrian crossing button&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Photo by &lt;a href=&#34;https://unsplash.com/photos/OMF0olQno6M?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;PICSELI&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you select the current channel on your remote, the TV will do nothing.&lt;/p&gt;
&lt;p&gt;Some cars have a remote with two buttons dedicated to opening and closing a car. If you open the opened car, it&amp;rsquo;ll be still opened. That&amp;rsquo;s obvious.&lt;/p&gt;
&lt;p&gt;Do you know other examples? As you see, they are everywhere but I thought long before I found some good ones. Idempotence isn&amp;rsquo;t so important in our daily work but is worth-knowing property if you use the functional paradigm. Now you know why.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Idempotence&#34;&gt;Idempotence – Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tools.ietf.org/html/rfc2616&#34;&gt;RFC 2616 – Hypertext Transfer Protocol – HTTP/1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Eric Evans, &lt;em&gt;Domain-Driven Design: Tackling Complexity in the Heart of Software&lt;/em&gt;, Side-effect-free functions, (Polish edition)&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Why should you return early?</title>
      <link>https://szymonkrajewski.pl/why-should-you-return-early/</link>
      <pubDate>Mon, 20 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/why-should-you-return-early/</guid>
      <description>&lt;p&gt;When I started my journey as a computer programmer, I had written my code in various way. In most cases, I translated requirements into specific conditions and statements, but I hadn&amp;rsquo;t taken too much care of readability or quality of my code. &lt;em&gt;&amp;ldquo;It works, that&amp;rsquo;s it&amp;rdquo;&lt;/em&gt; – and the code base grew and grew in time. Thousands of written methods later, I had discovered that a lot of these little pieces of logic could look better if I reverse some condition within them. Unconsciously, I&amp;rsquo;ve started following the rule which I had later called the &lt;em&gt;&amp;ldquo;fail fast&amp;rdquo;&lt;/em&gt; rule, and subsequently &amp;ldquo;&lt;strong&gt;return early&lt;/strong&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I realized I hadn&amp;rsquo;t had the one preferred way to check conditions within the single method. Sometimes I let execution if the expression was &lt;em&gt;true&lt;/em&gt;. Another time I had waited for the &lt;em&gt;false&lt;/em&gt; result to show the error message before the main part of the code was executing. In my head, I discovered various styles of checking conditions.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 2020-03-14&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Although this article is 2yo, it’s one of the most popular articles I’ve written on my blog. I decided to review it, correct mistakes and simplify the content by eliminating visible inaccuracies. The topic this article touches is timeless, so I hope you’ll enjoy the reading. &lt;a href=&#34;https://szymonkrajewski.pl/contact/&#34;&gt;I appreciate every feedback&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;two-approaches-to-check-requirements&#34;&gt;Two approaches to check requirements&lt;/h2&gt;
&lt;p&gt;The most basic style of checking if something meets the requirement is to put the expectation into the condition simply. There is one example. I want to send a message only if the variable &lt;code&gt;$email&lt;/code&gt; is the valid e-mail address and if the variable &lt;code&gt;$message&lt;/code&gt; is not an empty string. Otherwise, I want to throw an exception.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sendEmail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filter_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;FILTER_VALIDATE_EMAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Mailer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Cannot send an empty message.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Email is not valid.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above snippet is one-to-one what I wrote before. Is it correct? Definitely. Is it readable? Not quite.&lt;/p&gt;
&lt;h3 id=&#34;what-is-wrong-with-moving-requirements-literally-to-the-code&#34;&gt;What is wrong with moving requirements literally to the code?&lt;/h3&gt;
&lt;p&gt;We can point &lt;strong&gt;two problems&lt;/strong&gt; with &lt;code&gt;sendMail&lt;/code&gt; method:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It has more than one level of indentation within the method body.&lt;/li&gt;
&lt;li&gt;You don&amp;rsquo;t know the success path without analyzing the flow of the method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since the first point concerns the &amp;ldquo;&lt;em&gt;Clean Code&lt;/em&gt;&amp;rdquo; side, let me focus on the second one. The code mentioned above has a couple of &lt;em&gt;branches&lt;/em&gt; and the &lt;code&gt;return&lt;/code&gt; statement isn&amp;rsquo;t visible – you have to look for it. I know that this example is short, however, try to imagine the more extended method with more arguments and conditions (or even probably more than one &lt;em&gt;return&lt;/em&gt; statement). Have you tried? Some pieces of code are awful, but the small change could improve readability drastically. The only thing you have to do is to &lt;strong&gt;reverse conditions&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sendEmail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filter_var&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;FILTER_VALIDATE_EMAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Email is not valid.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Cannot send an empty message.&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Mailer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I admit that I also remove one &lt;code&gt;else&lt;/code&gt; after the second condition. I know that it&amp;rsquo;s subjective, but in my opinion, this code looks much cleaner and prettier than the previous version. And the only thing I did is &lt;strong&gt;reversing conditions&lt;/strong&gt; within &lt;em&gt;if&lt;/em&gt; statements.&lt;/p&gt;
&lt;h3 id=&#34;what-i-reach-by-reversing-conditions&#34;&gt;What I reach by reversing conditions?&lt;/h3&gt;
&lt;p&gt;First, the max indentation level within method body is one, and we got rid of the nested condition. Thanks to these operations, the code is more readable, and it&amp;rsquo;s easier to understand. The expected result is at the end of the method, so it&amp;rsquo;s easy to find. The flow within this method is clear and visible.&lt;/p&gt;
&lt;p&gt;Now you probably guess what I meant by calling this approach &lt;em&gt;&amp;ldquo;fail fast&amp;rdquo;&lt;/em&gt;. Unfortunately, I figured out that Jim Shore, together with Martin Fowler, had defined &amp;ldquo;&lt;a href=&#34;https://martinfowler.com/ieeeSoftware/failFast.pdf&#34;&gt;&lt;em&gt;fail fast&lt;/em&gt;&lt;/a&gt;&amp;rdquo; before I created my very first &amp;ldquo;Hello World&amp;rdquo; app. The goal is almost the same, but I decided to change the name of my approach to &amp;ldquo;&lt;strong&gt;return early&lt;/strong&gt;&amp;rdquo;. I also figured out that this is already defined pattern. However, I&amp;rsquo;m glad I had noticed it on my own! Because of this, I&amp;rsquo;ll continue using the other name, which, in my opinion, is more accurate for this article.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s get a closer look at the &lt;em&gt;&amp;ldquo;return early&amp;rdquo;&lt;/em&gt; concept.&lt;/p&gt;
&lt;h2 id=&#34;return-early-concept&#34;&gt;&amp;ldquo;&lt;em&gt;Return early&lt;/em&gt;&amp;rdquo; concept&lt;/h2&gt;
&lt;p&gt;You should know what is the &lt;em&gt;&amp;ldquo;return early&amp;rdquo;&lt;/em&gt; rule. Nonetheless, I want to try to define this concept by myself&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Return early is the way of writing functions or methods so that the &lt;strong&gt;expected positive result&lt;/strong&gt; is returned at the end of the function and the rest of the code terminates the execution when conditions are not met.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I wrote the first version of the definition in 2017, but it was awkward and complicated, so I decided to make it simple. The old one sounds that:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;ldquo;Conception of writing functions (methods) so that the expected positive result is returned at the end of the function and the rest of the code should cause as soon as possible the termination in case of divergence with the purpose of this function.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you see a way to improve this definition, please &lt;a href=&#34;https://szymonkrajewski.pl/contact/&#34;&gt;let me know&lt;/a&gt;. I&amp;rsquo;m always open for discussion.&lt;/p&gt;
&lt;h3 id=&#34;follow-the-&#34;&gt;Follow the &lt;em&gt;&amp;ldquo;happy path&amp;rdquo;&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;&lt;em&gt;The pot of gold is at the end of the rainbow&lt;/em&gt;&amp;rdquo;. Do you remember this story? Maybe did you find one? The &lt;strong&gt;expected positive result&lt;/strong&gt; should be like a &lt;em&gt;pot of gold&lt;/em&gt; –  placed at the end of the function. Just look at the above &lt;code&gt;sendEmail&lt;/code&gt; example. Our goal was to send a message to the given e-mail address. The execution must follow the &lt;em&gt;&amp;ldquo;success path&amp;rdquo;&lt;/em&gt; or, as the header says, the &amp;ldquo;&lt;em&gt;happy path&lt;/em&gt;&amp;rdquo;. Happy, because this is the &lt;strong&gt;expected positive result&lt;/strong&gt; and this is the reason why we call this function.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_9e221d79559a2b46.png 360w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_146e57eaa2b06795.png 640w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_41f1c0392078cc4c.png 960w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_f8bbc7254b097c84.png 1200w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_7eae864eeedd7a13.png 1600w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_happy_path_horizontal_hu_69af1f1cba448ce.png 1672w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1672&#34;
          height=&#34;652&#34;
           alt=&#34;Happy path in return early&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The main path within the function is clearly visible.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;By placing the final operation at the end of the function, we always know what our &lt;em&gt;&amp;ldquo;happy path&amp;rdquo;&lt;/em&gt; looks like and what is the &lt;strong&gt;expected positive result&lt;/strong&gt;. No guessing, no analyzing – quick look should be enough to find out what is the goal.&lt;/p&gt;
&lt;h3 id=&#34;get-rid-of-bad-cases-early&#34;&gt;Get rid of bad cases early&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;&amp;ldquo;happy path&amp;rdquo;&lt;/em&gt; is the best result that we got from the function. But the execution might don&amp;rsquo;t go in the right way – and that&amp;rsquo;s ok. You can come back to our &lt;code&gt;sendEmail&lt;/code&gt; example. If someone provides an invalid e-mail address, then he&amp;rsquo;ll cause an &lt;em&gt;expected bad case&lt;/em&gt;. We don&amp;rsquo;t want to pass an invalid e-mail address to the further execution. We should &lt;strong&gt;terminate&lt;/strong&gt; the execution or simply &lt;strong&gt;return from function earlier&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The termination could be either the &lt;em&gt;return&lt;/em&gt; or the &lt;em&gt;exception&lt;/em&gt;. The choice is yours. You should use the way that is more convenient for you or what it would fit in a particular case.&lt;/p&gt;
&lt;p&gt;Many people may feel awkward by using more than one return statement per single function. If the language gives you a possibility to explicitly define return value, I don&amp;rsquo;t see any reason why you shouldn&amp;rsquo;t use it, even more gladly when it gives you real advantage, e.g. better readability and maintainability.&lt;/p&gt;
&lt;h4 id=&#34;the-bouncer-pattern&#34;&gt;The Bouncer Pattern&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Bouncer Pattern&lt;/em&gt; is what I described in the previous section. It&amp;rsquo;s also known as &amp;ldquo;&lt;em&gt;Assertions&lt;/em&gt;&amp;rdquo; or even &lt;em&gt;&amp;ldquo;Guard clauses&lt;/em&gt;&amp;rdquo;. All mean the same and they prevent code execution in case of the invalid state. Take a look at this code snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;matrixAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isMatrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$mA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;First argument is not a valid matrix.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isMatrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$mB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Second argument is not a valid matrix.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hasSameSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$mA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Arrays have not equal size.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$cA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$vB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$vA&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$vB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$cB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$mB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first part of the code is responsible for checking if an operation can proceed. The &lt;em&gt;return&lt;/em&gt; statement contains the main logic of our function. In the ideal world, you wouldn&amp;rsquo;t need the first part of the code.&lt;/p&gt;
&lt;p&gt;You may noticed that I use array functions instead of basic loops. I recommend this approach &lt;a href=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/&#34;&gt;in another article&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;return-early-from-the-controllers-action&#34;&gt;Return early from the controller&amp;rsquo;s action&lt;/h3&gt;
&lt;p&gt;Controller&amp;rsquo;s action is a great candidate to use a &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; approach. Actions often do much more things before they return a final response. Let&amp;rsquo;s consider below &lt;code&gt;updatePostAction&lt;/code&gt; method that comes from the &lt;code&gt;PostController&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* PostController.php */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;updatePostAction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Request&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isGranded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;POST_EDIT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;createPostUpdateForm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handleRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isValid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;manager&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;persist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;manager&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Post has been updated.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Post validation error.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Post doesn&amp;#39;t exist.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Insufficient permissions.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addFlash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;post.update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;RedirectResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;post.index&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Such a big piece of code here. It has one return statement and a lot of nested conditions. I don&amp;rsquo;t know what you feel looking at this code snippet, but I think that it should be a better way to do this. Another example takes advantage of the &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; approach.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* PostController.php */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;updatePostAction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Request&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$failResponse&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;post.update&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isGranded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;POST_EDIT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addFlash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Insufficient permissions.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$failResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$postId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addFlash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Post doesn&amp;#39;t exist.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$failResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;createPostUpdateForm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handleRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$form&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;isValid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;addFlash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Post validation error.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$failResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;manager&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;persist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;manager&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;RedirectResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;post.index&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can observe that this action has a &lt;strong&gt;clearly visible &amp;ldquo;&lt;em&gt;happy pat&lt;/em&gt;h&amp;rdquo;&lt;/strong&gt;. It&amp;rsquo;s not one-to-one as the previous examples, but it follows the same conception. If you expect that something may go wrong, check it and &lt;strong&gt;return early&lt;/strong&gt;! Moreover, the one-level of indentation makes the code more readable, and, just note, &lt;strong&gt;I don&amp;rsquo;t need to use the &lt;code&gt;else&lt;/code&gt; keyword anywhere&lt;/strong&gt;!&lt;/p&gt;
&lt;h3 id=&#34;return-early-from-recursive-functions&#34;&gt;Return early from recursive functions&lt;/h3&gt;
&lt;p&gt;Also recursive function always should have a terminator that can looks like &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; statement. This is a simple example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;substr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$acc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;objections-about-return-early-conception&#34;&gt;Objections about &amp;ldquo;return early&amp;rdquo; conception&lt;/h2&gt;
&lt;p&gt;The main question is – what is the advantage of this concept? Why should I take care of this? Or when I shouldn&amp;rsquo;t?&lt;/p&gt;
&lt;h3 id=&#34;problem-1-code-style-is-subjective&#34;&gt;Problem 1: Code style is subjective&lt;/h3&gt;
&lt;p&gt;We, as programmers, spend much more time reading code than writing. That&amp;rsquo;s common true. Therefore, we should write code which is easy to read and understand. I proposed to react on falsy state first, not only because it improves readability but also it ensures that the main gear within the function is always at its end (or really close). In other words, we see the expected success result as our &amp;ldquo;happy path&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. &amp;hellip;[Therefore,] making it easy to read makes it easier to write.&lt;/p&gt;
&lt;p&gt;– Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;But the code style is &lt;em&gt;subjective&lt;/em&gt;. Someone may feel better with single &lt;em&gt;return&lt;/em&gt; statement or with multiple assignments instead of writing the same method call again and again (like &lt;code&gt;addFlash&lt;/code&gt; in one of above code snippets).&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_9431316b7fc4d6be.png 360w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_edd34dc536dd0f86.png 640w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_5f3c87a0b395d00d.png 960w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_52c6a027eb2cece6.png 1200w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_f46e3f6b56153ab7.png 1600w, https://szymonkrajewski.pl/why-should-you-return-early/images/return_early_functions_placeholders_hu_dcf826c777e7e471.png 1616w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1616&#34;
          height=&#34;852&#34;
           alt=&#34;Code placeholders – non return early vs return early approach&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Could you imagine these two hypothetical functions? Which of them seems to be simpler?
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&#34;problem-2-sometimes-return-early-is-an-exaggeration&#34;&gt;Problem 2: Sometimes &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; is an exaggeration&lt;/h3&gt;
&lt;p&gt;I know a lot of cases where applying &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; approach could harm the code or at least gives nothing. Think about all these setters where you have to assign value to the field only when the param is different than &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s not the best way to improve code&amp;rsquo;s readability. You should know where and when you can use techniques like &lt;em&gt;&amp;quot; return early &amp;ldquo;&lt;/em&gt;. This situation shows that sometimes it&amp;rsquo;s good to ignore rules.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;problem-3-it-looks-like-break-statement&#34;&gt;Problem 3: It looks like &lt;em&gt;break&lt;/em&gt; statement&lt;/h3&gt;
&lt;p&gt;Because function may have more than one &lt;em&gt;return&lt;/em&gt; statement, we don&amp;rsquo;t accurately know where from the result comes. As long as you use &lt;em&gt;&amp;ldquo;return early&amp;rdquo;&lt;/em&gt; to stop reaching an invalid state, you shouldn&amp;rsquo;t care about this. Of course, are you writing tests?&lt;/p&gt;
&lt;h3 id=&#34;problem-4-sometimes-its-ok-to-use-a-variable-instead-of-many-returns&#34;&gt;Problem 4: Sometimes it&amp;rsquo;s ok to use a variable instead of many returns&lt;/h3&gt;
&lt;p&gt;There are some structures where &amp;ldquo;return early&amp;rdquo; doesn&amp;rsquo;t change anything. Please look at these two examples of the same function. It comes from the &amp;ldquo;game of life&amp;rdquo; algorithm where function calculates the next state of the cell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;nextState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$nextState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$nextState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;elseif&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$nextState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$nextState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The same function but using &amp;ldquo;&lt;em&gt;return early&lt;/em&gt;&amp;rdquo; looks as below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;nextState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$currentState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$neighbours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, as you see, there is no big advantage in this case.&lt;/p&gt;
&lt;p&gt;Are you wondering what is a &amp;ldquo;&lt;em&gt;happy path&lt;/em&gt;&amp;rdquo; in the above code snippet? That&amp;rsquo;s the exciting thing. The function&amp;rsquo;s name isn&amp;rsquo;t so meaningful, but its hidden goal is to &lt;em&gt;&amp;ldquo;kill&amp;rdquo;&lt;/em&gt; a cell. Everything that keeps our cell alive goes to the invalid state. So, maybe do you have a better proposal for the name for this function?&lt;/p&gt;
&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;The way how we write code comes from our customs. It&amp;rsquo;s hard to say that something is better than others when the outcome is the same. It&amp;rsquo;s very subjective like many more things in the programming.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;&lt;em&gt;Return early&lt;/em&gt;&amp;rdquo; concept introduces some rules. Rules that you shouldn&amp;rsquo;t blindly follow. As you can see there are a lot of cases where this approach helps and otherwise, where it doesn&amp;rsquo;t change anything. It&amp;rsquo;s crucial to keep a discipline during writing code because the &lt;strong&gt;consistent code style is much more important than anything else&lt;/strong&gt; when it comes to the cognitive load.&lt;/p&gt;
&lt;p&gt;I use &lt;em&gt;&amp;ldquo;return early&amp;rdquo;&lt;/em&gt; as often as I can. It means that I use it where I see the advantage of returning early from functions, e.g. because of invalid state or when the change improves readability. I&amp;rsquo;m used that success is always at the end of the function. The hidden mantra sounds:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Follow the success path (happy path) and react in case of errors.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://martinfowler.com/ieeeSoftware/failFast.pdf&#34;&gt;J. Shore, M. Folwer, &amp;ldquo;&lt;em&gt;Fail Fast&lt;/em&gt;&amp;rdquo;, IEEE Software 2004&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://wiki.c2.com/?BouncerPattern&#34;&gt;Bouncer Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement&#34;&gt;&amp;ldquo;Should I have only one return statement?&amp;rdquo;, Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured Photo by &lt;a href=&#34;https://unsplash.com/photos/Ji1Uz9S-hjQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Franz Spitaler&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>My start with React and its rich ecosystem</title>
      <link>https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/</link>
      <pubDate>Mon, 06 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/</guid>
      <description>&lt;p&gt;I remember how I was excited discovering how much frontend frameworks like AngularJS could make my work easier. Even though I was a strong backend-oriented programmer I decided to learn the concept of Angular. I dug into it at ease and I started using this tool in my daily work.&lt;/p&gt;
&lt;p&gt;Since I changed my job I lost the opportunity to use frontend technologies regularly. However, I&amp;rsquo;m still fascinated about this part of software development and I follow trends and latest changes. Therefore AngularJS 1.x was officially abandoned I started looking for something new and fresh.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t look for a long time. When I first saw &lt;a href=&#34;https://reactjs.org&#34;&gt;React library&lt;/a&gt;, I fall in love with it. I quickly caught the concept behind this tool, thanks to ubiquitous tutorials and examples. I read a lot about the flux pattern, redux, components but I didn&amp;rsquo;t chance to use them in something more ambitious than &lt;code&gt;&amp;lt;HelloWorld/&amp;gt;&lt;/code&gt; component. Till now.&lt;/p&gt;
&lt;h2 id=&#34;project-conception&#34;&gt;Project conception&lt;/h2&gt;
&lt;p&gt;Because I still study and I have some projects to do, I decided to smuggle some new stuff this way. This is a really nice opportunity to try something new because of at least two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have a &lt;strong&gt;specified goal&lt;/strong&gt; of project.&lt;/li&gt;
&lt;li&gt;Your project &lt;strong&gt;must&lt;/strong&gt; work.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simple, but enough. These rules prevent the further work from being abandonment in the half of project.&lt;/p&gt;
&lt;p&gt;My project is a simple online password manager with client-side encryption. This sounds like a good playground for a React application. It also needs some server-side work but first I want to focus on the front-end.&lt;/p&gt;
&lt;h2 id=&#34;i-started-working-with-react&#34;&gt;I started working with React&lt;/h2&gt;
&lt;p&gt;When I first tried to work with React I was frightened how much work it needs to set up a simple environment. Some time ago I tried it once again using an &lt;a href=&#34;https://github.com/facebookincubator/create-react-app&#34;&gt;official tool &lt;code&gt;create-react-app&lt;/code&gt;&lt;/a&gt;. A fresh React project was ready for several seconds. That&amp;rsquo;s impressive.&lt;/p&gt;
&lt;h3 id=&#34;bootstrap-comes-in&#34;&gt;Bootstrap comes in&lt;/h3&gt;
&lt;p&gt;Because of time limitation, I was looking for some existing component libraries dedicated to the UI development. I didn&amp;rsquo;t want to reinvent the wheel. After a small research, I choose the &lt;a href=&#34;https://reactstrap.github.io&#34;&gt;&lt;code&gt;reactstrap&lt;/code&gt; library&lt;/a&gt; which wraps Bootstrap components into React ones. Great.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_reactstrap-1.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_reactstrap-1_hu_8df1374972b35885.png 256w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;256&#34;
          height=&#34;256&#34;
           alt=&#34;Reactstrap logo&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Reactstrap logo.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I also started developing my custom components. This is one example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PasswordField&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;React&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Component&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kr&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hidden&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;toggleVisibility&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prevState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hidden&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prevState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hidden&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;className&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;PasswordField&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;InputGroup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;InputGroupAddon&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;FaEye&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;className&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;PasswordField-Icon&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;onClick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toggleVisibility&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)}/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;InputGroupAddon&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hidden&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;readOnly&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;InputGroup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;needs-for-routing&#34;&gt;Needs for routing&lt;/h3&gt;
&lt;p&gt;After prototyping one screen I realized that could be nice to have a router to add some interactions to my application. Another research led me to the &lt;a href=&#34;https://reacttraining.com/react-router/&#34;&gt;react-router&lt;/a&gt; library. Some examples from documentation gave me a good playground.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_react_router-1.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_react_router-1_hu_c0783964be3cd949.png 144w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;144&#34;
          height=&#34;144&#34;
           alt=&#34;React Router logo&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      React Router logo.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;m a little bit scary reading how these things change over time. According to official documentation, &lt;a href=&#34;https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/migrating.md&#34;&gt;react-router have been completely rewritten&lt;/a&gt;. It makes a migration to the newest version difficult.&lt;/p&gt;
&lt;h3 id=&#34;state-management&#34;&gt;State management&lt;/h3&gt;
&lt;p&gt;Subsequently, I added redux to my application. I learned about the concept behind this in past mostly from articles and video courses. &lt;a href=&#34;https://twitter.com/dan_abramov&#34;&gt;Dan Abramov&lt;/a&gt; created a brief &lt;a href=&#34;https://egghead.io/courses/getting-started-with-redux&#34;&gt;video introduction to redux&lt;/a&gt; – it&amp;rsquo;s a great starter. Now I check there in the action and I feel that without the good background it would be hard to use it ad hoc.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_redux-1.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_redux-1_hu_520f101bfaec0993.png 360w, https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_logo_redux-1_hu_6b7fcb2430c5801a.png 444w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;444&#34;
          height=&#34;120&#34;
           alt=&#34;Redux logo&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Redux logo.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;In React, I have to describe all changes explicitly. Even when it comes to binding input into the component&amp;rsquo;s state. Switching from &lt;em&gt;two-way binding&lt;/em&gt; to this is awkward but I know problems with bindings within AngularJS applications. After that, I prefer explicit solutions. Redux is &lt;strong&gt;intuitive&lt;/strong&gt;. All possible transitions in the state are described within the reducers. Reducers are activated by actions. Finally, actions are emitted by interactions. The beauty of reactivity.&lt;/p&gt;
&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;I was under huge impression how easy I can write a full client application without worrying about the server-side yet. I prepared some actions dedicated to gathering data from the server, but now instead of doing request, &lt;strong&gt;I resolved a delayed promise&lt;/strong&gt; with dummy data. I know all these things were possible but I never tried work in this way before.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_5cbd034f4776731d.webp 360w, https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_6a7833b0aeb72c07.webp 640w, https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_3373cd061fbc681c.webp 901w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_7e5dc75eee539ceb.jpg 360w, https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_962548deffd75ba4.jpg 640w, https://szymonkrajewski.pl/my-start-with-react-and-its-rich-ecosystem/images/012_vault_preview-1_hu_100b75395ceb85cc.jpg 901w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;901&#34;
          height=&#34;431&#34;
           alt=&#34;Screen of my application built using React&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The main screen of my application. Very first prototype 🙂
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Building with React gives me a lot of fun because it&amp;rsquo;s interrelated to object-oriented programming as well as to functional programming. It depends on who handles the state management. I also say building instead of programming. I notice a majority of work is a compose bigger components from smaller ones. It may be an illusion because lots of logic is hidden within their as well as within the actions and reducers.&lt;/p&gt;
&lt;p&gt;The one thing I miss is &lt;strong&gt;tests&lt;/strong&gt;, because of limitation of time I focus mostly on building the app rather than getting to know the full process. Because of that, I know I have something to catch up in this area.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/9HI8UJMSdZA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Braden Collum&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Why you should use array functions instead of basic loops</title>
      <link>https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/</link>
      <pubDate>Mon, 23 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/</guid>
      <description>&lt;p&gt;Loop is the basic construction in each programming language. All repeatable operations are being done using loops. I think you know how the basic loops like &lt;em&gt;while&lt;/em&gt;, &lt;em&gt;do&lt;/em&gt; and &lt;em&gt;for&lt;/em&gt; work and when you should use one of them. But are you sure? Maybe should you use array functions?&lt;/p&gt;
&lt;p&gt;How many of your operations in a loop rely on building objects based on the value? Or selecting only a few items from the array using a predicate? Maybe you use array values to calculate something? Whatever you do, read why you should replacing basic loops by array operations.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update 2019-12-11&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PHP 7.4 comes with support for short closures aka arrow functions. I enriched this article with examples that use this new functionality.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;working-with-for-loop&#34;&gt;Working with &lt;em&gt;for&lt;/em&gt; loop&lt;/h2&gt;
&lt;p&gt;Take a look at this example. Looks familiar?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;programming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Everyone knows the &lt;em&gt;for&lt;/em&gt; loop. It has three control statements: setup, condition, and instruction after each step. But what exactly this code &lt;strong&gt;means&lt;/strong&gt;?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an empty array called &lt;code&gt;$entities&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the counter &lt;code&gt;$i&lt;/code&gt; to 0 (zero).&lt;/li&gt;
&lt;li&gt;Iterate until the counter is less than a number of items in &lt;code&gt;$tags&lt;/code&gt; array and increment counter each time by one.&lt;/li&gt;
&lt;li&gt;Inside the loop, create a new instance of Tag. The counter points the name of the tag from &lt;code&gt;$tags&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;Add the new object to the &lt;code&gt;$entities&lt;/code&gt; array.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are a lot of operations which largely obfuscate the main goal. They are irrevelant. Why should you care about the number of elements, starting point, or the iteration process?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;ldquo;A programming language is low level when its programs require attention to the irrelevant.&amp;rdquo;&lt;/em&gt; — &lt;em&gt;Alan Perlis&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;working-with-foreach-loop&#34;&gt;Working with &lt;em&gt;foreach&lt;/em&gt; loop&lt;/h2&gt;
&lt;p&gt;You can clean up the previous snippet replacing &lt;em&gt;for&lt;/em&gt; in favor of &lt;em&gt;foreach&lt;/em&gt; loop.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;programming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Much better. All unnecessary operations are hidden thanks to the foreach loop. It creates an abstraction over basic iteration process. Thanks to this, you can focus on your main goal instead of repeat the loop control logic again and again.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Foreach&lt;/em&gt; loop creates a tiny abstraction over basic iteration process. It allows you to focus on the main goal and gives more readable code in the outcome.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Look how more understandable this code is by reading it step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an empty array called &lt;code&gt;$entities&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Iterate over &lt;code&gt;$tags&lt;/code&gt; array and make the current element available by &lt;code&gt;$tag&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Inside the loop, create a new instance of &lt;code&gt;Tag&lt;/code&gt; which &lt;code&gt;$tag&lt;/code&gt; as a name.&lt;/li&gt;
&lt;li&gt;Add the new object to &lt;code&gt;$entities&lt;/code&gt; array.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No details about the process of iteration, incrementation or even number of elements. Great! But both of these pieces of code have hidden &lt;strong&gt;side effect&lt;/strong&gt;. Can you point out where?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Side effect in computer science is a situation when function or expression modifies variables or resources outside its scope.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The problem lies in loops. Both of loops leave extra variable in execution scope – &lt;code&gt;$i&lt;/code&gt; and &lt;code&gt;$tag&lt;/code&gt;. The variable &lt;code&gt;$tag&lt;/code&gt; contains the last element of &lt;code&gt;$tags&lt;/code&gt; array. Therefore it’s recommended to &lt;em&gt;unset&lt;/em&gt; unnecessary variable directly behind the loop.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;programming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can improve this code, even more, by identifying your main goal. What is the main goal in above example? I defined an array of tags and I want to transform them to an array of &lt;code&gt;Tag&lt;/code&gt; objects. Each element from array should be mapped to the new one.&lt;/p&gt;
&lt;h2 id=&#34;array-functions-to-the-rescue&#34;&gt;Array functions to the rescue&lt;/h2&gt;
&lt;h3 id=&#34;introduce-mapping&#34;&gt;Introduce mapping&lt;/h3&gt;
&lt;p&gt;I revealed one of the array operations – &lt;strong&gt;mapping&lt;/strong&gt;. This operation allows you to define a function which will be executed over each element in the array. They create a new array contains the mapped entries. In PHP there is a function called &lt;a href=&#34;https://php.net/manual/en/function.array-map.php&#34;&gt;&lt;code&gt;array_map&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’ll change the code a bit to use the &lt;code&gt;array_map&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For PHP &amp;lt; 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;programming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Since PHP 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;programming&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$entities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No loop control logic, no side effects. I just defined a function that should be executed on each element. The returned value of supplied function should return the new element.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_hu_88346f2f20f0dd33.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_hu_a42e990b783f6b07.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_hu_8979a692dee2b590.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_hu_46b2054471e8fea8.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_hu_9b00e538ed85f144.png 1546w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1546&#34;
          height=&#34;528&#34;
           alt=&#34;graphical representation of map array function&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The Map function takes an argument and returns the new value.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Now, let’s try to explain this code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Map each element of &lt;code&gt;$tags&lt;/code&gt; array to the new one using a supplied callback function. This function defines how to map single element.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it. There is no step 2. The code is easier to read and &lt;a href=&#34;https://szymonkrajewski.pl/write-code-for-people/&#34;&gt;understand&lt;/a&gt;, and communicates proper intentions.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result_hu_b1511a1bcdcace27.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result_hu_c24c2a263c02d2f4.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result_hu_f8098994d61544b7.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result_hu_1925b191c715553.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_map_result_hu_a5a8b5a40a92cf6b.png 1450w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1450&#34;
          height=&#34;584&#34;
           alt=&#34;graphical representation of map result&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The result of map function. All items were mapped to the new ones.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Some people don’t like the anonymous function. Just remember that &lt;code&gt;array_map&lt;/code&gt; expects a &lt;code&gt;callable&lt;/code&gt;. You can pass it by string (e.g. when you want to use the internal function), variable, array – the choice is yours.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;meet-the-filter-function&#34;&gt;Meet the filter function&lt;/h3&gt;
&lt;p&gt;Sometimes you have a need to select only elements from the array that satisfying defined criteria. To accomplish it you could create a loop which conditionally adds elements to the new array.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Phone&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Notebook&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;SDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;HDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$inStock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$inStock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You already know that basic loops have a noticeable overhead. Instead, you should use the &lt;a href=&#34;https://php.net/manual/en/function.array-filter.php&#34;&gt;&lt;code&gt;array_filter&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For PHP &amp;lt; 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Phone&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Notebook&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;SDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;HDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$inStock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Since PHP 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Phone&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Notebook&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;SDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;HDD Drive&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$inStock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$stock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;qty&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;You probably note that the order of arguments in &lt;code&gt;array_filter&lt;/code&gt; function is different than e.g. in &lt;code&gt;array_map&lt;/code&gt; function. About the PHP inconsistency may write a book but it goes beyond the boundaries of this article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this example, &lt;code&gt;array_filter&lt;/code&gt; also takes a &lt;code&gt;callable&lt;/code&gt;. This function is a predicate. They must return a &lt;em&gt;boolean&lt;/em&gt; value which defines whether element must be included in the resulting array or not.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_a27fbe2e7f13d799.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_b93c61a937fdaac9.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_b585480c4dad203a.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_884a79f354fdb899.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_42ece02f968f51ec.png 1600w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_hu_735e5b1dba8b801b.png 1670w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1670&#34;
          height=&#34;1056&#34;
           alt=&#34;graphical representation of filter array function&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Representation of the filter function. For “green triangles” it returns true, for anything else it returns false.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you don’t supply the predicate function &lt;code&gt;array_filter&lt;/code&gt; returns all items except &lt;em&gt;false&lt;/em&gt;. Maybe is this the reason why the order of arguments is different than in other array functions?&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result_hu_4fc04ef5905d6c4d.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result_hu_fbff08bcabbdbbec.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result_hu_dc7c5e794ddd53d4.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result_hu_3fb634a3d42a0073.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_filter_result_hu_bef8409b6f9400ca.png 1534w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1534&#34;
          height=&#34;590&#34;
           alt=&#34;graphical representation of filter result&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The result contains only elements matched the criteria from filter function.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&#34;reduce-array-to-single-value&#34;&gt;Reduce array to single value&lt;/h3&gt;
&lt;p&gt;Maybe you remember a few situation when you need to iterate over an array to e.g. sum any value. Take a look at below example. The goal is to get total points for the team.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Marry&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Jacob&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joanna&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Tony&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We’ll switch immediately to the &lt;a href=&#34;https://php.net/manual/en/function.array-reduce.php&#34;&gt;&lt;code&gt;array_reduce&lt;/code&gt;&lt;/a&gt; function:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For PHP &amp;lt; 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Marry&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Jacob&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joanna&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Tony&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$carry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$carry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Since PHP 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Marry&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Jacob&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joanna&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Tony&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$team&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$carry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$carry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;points&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first impression – there is a more code than in the previous snippet!&lt;/p&gt;
&lt;p&gt;You’re right. But the number of lines/words/characters shouldn’t be the most important criteria. Especially if you want to write clean, understandable and side-effects free code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes is necessary to write more words to get a more understandable and side-effect free code.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I hope you have seen a difference in the &lt;code&gt;callback&lt;/code&gt; function. There are two arguments. The former, accumulator (I call it &lt;code&gt;$carry&lt;/code&gt;) contains the reduced value and the latter keeps the current element. The callback function handles returning the new value of &lt;code&gt;$carry&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2_hu_cc2a86f002c8c822.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2_hu_beb7624bf82775.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2_hu_dbe178b78458aa7f.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2_hu_5144ce0de6e2de7.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce-2_hu_ff0fcf5cb2d2a8b3.png 1506w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1506&#34;
          height=&#34;436&#34;
           alt=&#34;graphical representation of reduce array function&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Reduce function takes two arguments – element and accumulator. In outcome, it returns a new value of the accumulator.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Value of &lt;code&gt;$carry&lt;/code&gt; is also passed to the next callback execution. Finally returned from the &lt;code&gt;array_reduce&lt;/code&gt; function after all. The third argument of this function is the initial value of &lt;code&gt;$carry&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result_hu_91d19f4fb03bb092.png 360w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result_hu_d2f1748c8113695.png 640w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result_hu_5076d9c8ec6fb66d.png 960w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result_hu_779f746bfea64d9d.png 1200w, https://szymonkrajewski.pl/why-you-should-use-array-functions-instead-of-basic-loops/images/004_fn_reduce_result_hu_78736c4b108c7a1c.png 1558w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1558&#34;
          height=&#34;586&#34;
           alt=&#34;graphical representation of reduce result&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      The result of reduce function is usually a single value. In this case array of triangles was reduced to the sum of its elements.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;mixed-operations-during-iteration&#34;&gt;Mixed operations during iteration&lt;/h2&gt;
&lt;p&gt;Let’s consider case when you want to do something with elements which are satisfying defined criteria, e.g.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Mike&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ray&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ana&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joe&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$advancedPlayersNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$advancedPlayersNames&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Adv.&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inside this loop, you could find both &lt;em&gt;filter&lt;/em&gt; and &lt;em&gt;mapping&lt;/em&gt;. We want to get names of players whose level is more than 4. To accomplish it using the array methods you should split this problem into small parts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For PHP &amp;lt; 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Mike&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ray&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ana&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joe&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$advancedPlayers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$advancedPlayerNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Adv&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$advancedPlayers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Since PHP 7.4:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Mike&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ray&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Ana&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Joe&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$advancedPlayers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$players&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$advancedPlayerNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;array_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Adv&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$player&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$advancedPlayers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So as you see there is no big deal. Instead of defining an algorithm to extract final data, think more about the process. It contains steps like mapping, filtering and reducing. Is this code excessive? Maybe. Is it easier to read and understand? I think yes.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;If you don’t use the array functions yet, just try. I highly encourage you to write your loops in this way. You’ll be surprised how easy to understand will be your code particularly for your coworkers or even you after a long long time. This approach brings us closer to the next interesting things – &lt;em&gt;Collections&lt;/em&gt;. Personally, I prefer array functions over basic loops. In my opinion, they’re more meaningful and expressive, because the code is more declarative – it means that I define what I want to reach instead of describing the way how to do it.&lt;/p&gt;
&lt;p&gt;Of course, if you work on the critical system you should consider this advice. Array functions are &lt;a href=&#34;https://stackoverflow.com/a/26527704&#34;&gt;a little bit slower&lt;/a&gt; than basic loops but I think that there is no significant impact on performance.&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://php.net/manual/en/function.array-map.php&#34;&gt;http://php.net/manual/en/function.array-map.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://php.net/manual/en/function.array-filter.php&#34;&gt;http://php.net/manual/en/function.array-filter.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://php.net/manual/en/function.array-reduce.php&#34;&gt;http://php.net/manual/en/function.array-reduce.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Featured photo by &lt;a href=&#34;https://unsplash.com/photos/IBnAVRHBYgA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Photo Design Lab&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Synchronize local files with Cryptomator Vault</title>
      <link>https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/</link>
      <pubDate>Mon, 09 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/</guid>
      <description>&lt;p&gt;Among thousands of files on my computer, I have some with special meanings for me. Photos, videos, papers, and spreadsheets – they occupy dedicated places within my filesystem. That’s the problem – they exist only on my hard drive. However, I sometimes need a quick access to these files from my smartphone or other computers. The solution seems to be simple: migrating to the cloud. But what if I definitely don’t want to keep these files outside?&lt;/p&gt;
&lt;p&gt;Personally, I don’t tap into the cloud storage too much. That’s my private choice that may change in the future. But cloud storage is so powerful, cheap and widely available. It would be nice to use available space on free storage providers, especially in a case when I need some data outside the home.&lt;/p&gt;
&lt;p&gt;This is the time when encryption comes in.&lt;/p&gt;
&lt;h2 id=&#34;meet-cryptomator&#34;&gt;Meet Cryptomator&lt;/h2&gt;
&lt;p&gt;Last time I found a nice project called &lt;a href=&#34;https://cryptomator.org/&#34;&gt;Cryptomator&lt;/a&gt;. It’s a client-side encryption software which supports transparent encryption. That means you have easy access to your encrypted files as if they are decrypted. But it doesn’t mean that they are insecure – they are decrypted on-the-fly on demand. Moreover, Cryptomator stores each file separately. That’s cool, cloud-friendly solution!&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_42439b28640ba486.png 360w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_2301f8f22610d508.png 640w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_55ca98bafb056cf4.png 960w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_79b7068728c0daa0.png 1200w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_5a987a7f1b7bc146.png 1600w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/cryptomator-logo-text_hu_861bc589256636dc.png 1788w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1788&#34;
          height=&#34;384&#34;
           alt=&#34;Cryptomator logo with text&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;p&gt;I started playing with this and I decided to make a small experiment. I’ve tried to write a simple mirroring solution that once a day synchronizes a bunch of my local files to the encrypted Cryptomator’s vault.&lt;/p&gt;
&lt;h2 id=&#34;cryptomator-nad-macos-automator&#34;&gt;Cryptomator nad macOS’ Automator&lt;/h2&gt;
&lt;p&gt;I use macOS, so I started by running Automator, Apple’s solution to easily create workflows. Automator is the great tool to create a custom task which interacts with the macOS.&lt;/p&gt;
&lt;p&gt;Because Cryptomator is a GUI application, I tried to set up a semi-automatic workflow, similar to following&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run Cryptomator&lt;/li&gt;
&lt;li&gt;Wait for X seconds and let user manually unlock vault&lt;/li&gt;
&lt;li&gt;Copy files to vault&lt;/li&gt;
&lt;li&gt;Close Cryptomator&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Single blocks within the Automator allow me to create a workflow like above. Unfortunately, it has several problems.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It was not fully automated&lt;/li&gt;
&lt;li&gt;Files always be copied regardless of changes&lt;/li&gt;
&lt;li&gt;Workflow didn’t recognize the WebDAV destination after the re-run&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These problems enforce me to find other solution. Ideally when Cryptomator would offer a CLI access to manipulate the vault directly by e.g. &lt;em&gt;shell script&lt;/em&gt;. After a small research, I found the project &lt;a href=&#34;https://github.com/cryptomator/cli&#34;&gt;cryptomator/cli&lt;/a&gt; on GitHub. That was exactly what I was looking for.&lt;/p&gt;
&lt;h2 id=&#34;synchronize-local-files-with-encrypted-vault&#34;&gt;Synchronize local files with encrypted vault&lt;/h2&gt;
&lt;p&gt;CLI for Cryptomator allows us to easily start the WebDAV server as an access layer to the encrypted vault. This server could be running in the background. To get a quick access to the files you need to connect to it. I decided to mount it as an external drive. Therefore, I can interact with this virtual space like with a normal directory.&lt;/p&gt;
&lt;h3 id=&#34;platform-and-software-requirements&#34;&gt;Platform and Software requirements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java – Cryptomator is written in Java languages&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html&#34;&gt;JCE unlimited strength policy files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/cryptomator/cli/releases&#34;&gt;Cryptomator CLI&lt;/a&gt; JAR file&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;install-jce-unlimited-strength-policy-files&#34;&gt;Install &lt;em&gt;JCE unlimited strength policy files&lt;/em&gt;&lt;/h4&gt;
&lt;p&gt;If you use &lt;em&gt;homebrew&lt;/em&gt; you can install the necessary policy files by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew cask install jce-unlimited-strength-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unfortunately, it didn’t work for me so I had to go a manually way. According to &lt;em&gt;Readme&lt;/em&gt; file, I extracted files directly to the &lt;code&gt;/lib/security&lt;/code&gt;. The `` directory depends on your installation but in most cases, it should be in &lt;code&gt;/Library/Java/JavaVirtualMachines/&amp;lt; jdk_version_of_your_pc &amp;gt;/Contents/Home/jre&lt;/code&gt;, according to &lt;a href=&#34;https://stackoverflow.com/questions/37741142/how-to-install-unlimited-strength-jce-for-java-8-in-os-x#comment69317600_37741837&#34;&gt;this StackOverflow answer&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;flow-of-mirroring-files-to-the-encrypted-vault&#34;&gt;Flow of mirroring files to the encrypted vault&lt;/h3&gt;
&lt;p&gt;The flow is quite simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run cryptomator-cli and start WebDAV server&lt;/li&gt;
&lt;li&gt;Create a dedicated directory as mount point within the filesystem&lt;/li&gt;
&lt;li&gt;Attach WebDAV server to new directory&lt;/li&gt;
&lt;li&gt;Synchronize files from specific directory using &lt;code&gt;rsync&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Unmount the WebDAV server&lt;/li&gt;
&lt;li&gt;Remove unnecessary directory&lt;/li&gt;
&lt;li&gt;Exit Cryptomator&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you see there is no a big deal and basic shell script can handle it. And that was the goal.&lt;/p&gt;
&lt;h3 id=&#34;prototype-the-mirror-script&#34;&gt;Prototype the mirror script&lt;/h3&gt;
&lt;h4 id=&#34;configuration&#34;&gt;Configuration&lt;/h4&gt;
&lt;p&gt;First, I set up some configuration variables. They are widely used in the whole script.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CRYPTOMATOR_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/local/bin/cryptomator-cli.jar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DIR_TO_SYNC_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/Private&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MOUNT_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/VAULT_&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$RANDOM&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/PATH/TO/ENCRYPTED/VAULT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;`security find-generic-password -a KEYCHAIN_ENTRY_VAULT_PASSPHRASE -w`&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;vault&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BIND_HOST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BIND_PORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;8198&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I think they are self-described and they don’t need more comments.&lt;/p&gt;
&lt;p class=&#34;box info has-title&#34;&gt;&lt;strong&gt;Note about variables&lt;/strong&gt;&lt;br&gt;
Some of above variables are used as &lt;strong&gt;constants&lt;/strong&gt;. In the &lt;em&gt;shell&lt;/em&gt; there is no difference between them. As you can see, names of variables describe the value rather than containing the value. Because of that, they are change-resistant as long as the context is matched. For instance, instead of &lt;code&gt;DOCUMENTS_DIR&lt;/code&gt; I use &lt;code&gt;DIR_TO_SYNC_PATH&lt;/code&gt;, because I could change the directory and the previus name would not match. You can read more about it in my post: &lt;a href=&#34;https://szymonkrajewski.pl/do-your-constants-make-sense/&#34;&gt;&lt;strong&gt;&lt;em&gt;Do your constants make sense?&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The one variable is interesting – &lt;code&gt;VAULT_PASSWORD&lt;/code&gt;. Instead of hard-coding plaintext password within the script, I use &lt;em&gt;security&lt;/em&gt; tool. This is a macOS tool to retrieve data and password from your &lt;em&gt;keychain&lt;/em&gt;. In this case, the script is looking for entry &lt;code&gt;KEYCHAIN_ENTRY_VAULT_PASSPHRASE&lt;/code&gt; (you can also adjust this name).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Keychain&lt;/em&gt; is very restrictive. You cannot retrieve anything from it, even using the &lt;code&gt;security&lt;/code&gt; tool. Before the first use, macOS will ask for permissions to keychain access.&lt;/p&gt;
&lt;h4 id=&#34;run-cryptomator-cli-in-the-background&#34;&gt;Run Cryptomator CLI in the background&lt;/h4&gt;
&lt;p&gt;Using above configuration you can run WebDAV server which serves the content of your vault:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/java -jar &lt;span class=&#34;nv&#34;&gt;$PATH_TO_CRYPTOMATOR&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--vault &lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_PATH&lt;/span&gt;  &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--password &lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_PASSWORD&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--bind &lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--port &lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$!&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;PID&lt;/code&gt; will be needed in the very last step to finally go server down.&lt;/p&gt;
&lt;h4 id=&#34;mount-webdav-server-to-the-userspace&#34;&gt;Mount WebDAV server to the userspace&lt;/h4&gt;
&lt;p&gt;To ensure easy access to the vault you can mount it like a normal storage. The mount point may be anywhere, but I recommend keeping it in your home directory. You will not a &lt;em&gt;root&lt;/em&gt; access to mount it. Moreover, the vault contains your private data, right?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/sbin/mount_webdav http://&lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt;:&lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt;/&lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class=&#34;box info has-title&#34;&gt;&lt;strong&gt;Tip:&lt;/strong&gt;&lt;br&gt;
Instead of using &lt;code&gt;mount_webdav&lt;/code&gt; you can also use a &lt;code&gt;mount -t davfs&lt;/code&gt;command.&lt;/p&gt;
&lt;h4 id=&#34;synchronization-private-data-with-the-vault&#34;&gt;Synchronization private data with the vault&lt;/h4&gt;
&lt;p&gt;The easy approach is to copy all data from a private dir to the mounted vault. Although it looks fine, it enforces full cloud synchronization, because all files are modifying. Instead of copy, you can use the &lt;code&gt;rsync&lt;/code&gt; tool. Below command uses recursive synchronization and preserves modification times, so it’s cloud-friendly. Additionally, you can exclude some files, e.g. &lt;em&gt;temporary files&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/rsync -rvtP --update --exclude &lt;span class=&#34;s2&#34;&gt;&amp;#34;~*&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$DIR_TO_SYNC_PATH&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;cleanup&#34;&gt;Cleanup&lt;/h4&gt;
&lt;p&gt;The very last point is to unmount WebDAV server, remove the mounting point and eventually kill the Cryptomator. Let’s do this!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/sbin/umount &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -rf &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$PID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;put-these-parts-together&#34;&gt;Put these parts together&lt;/h4&gt;
&lt;p&gt;Final script has some extra &lt;code&gt;echo&lt;/code&gt; statements for debugging purposes. It has also a couple of timeouts and checks to ensure that every step has proceeded properly.&lt;/p&gt;
&lt;p&gt;I noticed that sometimes WebDAV is not mounted correctly to the file system. I don’t know the reason, but as a workaround, I created a loop that tries mount the vault after the short pause.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;until&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; -ge &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Try &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Mount http://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/ to &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /sbin/mount_webdav http://&lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt;:&lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt;/&lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;$&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;+1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    sleep &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the vault is mounted then we’ll exit the loop, thanks to the logical &lt;code&gt;AND&lt;/code&gt;operator and &lt;code&gt;break&lt;/code&gt; statement. As you see, the maximum count of trials is 5, so in the worst case, the script will continue the execution without a mounted vault. To avoid synchronize files with non-mounted directory I created an extra check.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; /sbin/mount &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt; &amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /usr/bin/rsync -hrvtP --update --exclude &lt;span class=&#34;s2&#34;&gt;&amp;#34;~*&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$DIR_TO_SYNC_PATH&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /sbin/umount &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;WebDAV not mounted correctly...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Eventually, the whole script looks like below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CRYPTOMATOR_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/local/bin/cryptomator-cli.jar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DIR_TO_SYNC_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/Private&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MOUNT_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/VAULT_&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$RANDOM&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/PATH/TO/ENCRYPTED/VAULT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;`security find-generic-password -a KEYCHAIN_ENTRY_VAULT_PASSPHRASE -w`&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;vault&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BIND_HOST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BIND_PORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;8198&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/java -jar &lt;span class=&#34;nv&#34;&gt;$PATH_TO_CRYPTOMATOR&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--vault &lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_PATH&lt;/span&gt;  &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--password &lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_PASSWORD&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--bind &lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;--port &lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;PID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$!&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Cryptomator started under PID: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$PID&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sleep &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Create new mounting point: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Attach WebDAV interface to mount point&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;until&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; -ge &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Try &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Mount http://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/ to &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /sbin/mount_webdav http://&lt;span class=&#34;nv&#34;&gt;$BIND_HOST&lt;/span&gt;:&lt;span class=&#34;nv&#34;&gt;$BIND_PORT&lt;/span&gt;/&lt;span class=&#34;nv&#34;&gt;$VAULT_NAME&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;$&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;+1&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    sleep &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; /sbin/mount &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt; &amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /usr/bin/rsync -hrvtP --update --exclude &lt;span class=&#34;s2&#34;&gt;&amp;#34;~*&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$DIR_TO_SYNC_PATH&lt;/span&gt;/ &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    /sbin/umount &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;WebDAV not mounted correctly...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Removing mounting point&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -rf &lt;span class=&#34;nv&#34;&gt;$MOUNT_PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Close cryptomator&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$PID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;link-the-script-to-macos&#34;&gt;Link the script to macOS&lt;/h2&gt;
&lt;h3 id=&#34;store-vault-passphrase-in-the-keychain&#34;&gt;Store vault passphrase in the &lt;em&gt;keychain&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;You should store a passphrase to your vault in the keychain. I use a &lt;em&gt;System&lt;/em&gt;keychain. After running above script for the first time you’ll be asked for the permission to access this specific entry. To avoid prompting every time when a script is executing, consider adding &lt;code&gt;security&lt;/code&gt; to the allowed application list.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_ab1e984f90e4cac6.webp 360w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_c468de0b5e371b83.webp 640w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_b6d5305c5075c6ff.webp 960w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_194083c4ce514f28.webp 1200w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_c1c21d5f58ceba7b.webp 1600w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_8aeeb098284d7415.webp 1920w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_9c5f50b5290073e1.jpg 360w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_9b9e407b91836e20.jpg 640w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_356f0ea049280de9.jpg 960w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_aba1b6a2e25e92e7.jpg 1200w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_933f11c5639959d8.jpg 1600w, https://szymonkrajewski.pl/synchronize-local-files-cryptomator-vault/images/010_key_chain_hu_12aac4ca179ca0d7.jpg 1920w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1920&#34;
          height=&#34;1275&#34;
           alt=&#34;Keys image&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Photo by &lt;a href=&#34;https://pixabay.com/en/key-metal-home-security-wedding-96233/&#34;&gt;Pixabay&lt;/a&gt;
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&#34;schedule-execution&#34;&gt;Schedule execution&lt;/h3&gt;
&lt;p&gt;I’m very used to cron, but I read that &lt;a href=&#34;https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html&#34;&gt;this is not a recommended solution in macOS&lt;/a&gt;. To define scheduled jobs you should use &lt;code&gt;launchd&lt;/code&gt; daemon.&lt;/p&gt;
&lt;p&gt;Start by creating the &lt;em&gt;plist&lt;/em&gt; job file. In my case this is &lt;code&gt;pl.skrajewski.backup.plist&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;version=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;pl.skrajewski.backup&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/custom/backup.sh&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartCalendarInterval&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;22&lt;span class=&#34;nt&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;45&lt;span class=&#34;nt&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/var/log/vault-backup.error.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class=&#34;nt&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/var/log/vault-backup.log&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file seems to be more complicated than simple crontab entry but is more powerful. In truth, this is a basic XML file. If you want to know more about the possible configuration options, please take a look on this &lt;a href=&#34;http://www.launchd.info/&#34;&gt;launchd site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I briefly describe above file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Label&lt;/code&gt; is a mandatory key and should be unique within the &lt;em&gt;launchd&lt;/em&gt;instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Program&lt;/code&gt; contains complete path to the executable.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StartCalendarInterval&lt;/code&gt; allows you to define the specific time when the job run.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StandardErrorPath&lt;/code&gt; is a path to file for &lt;em&gt;stderr&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StandardOutPath&lt;/code&gt; is a path to file dedicated for &lt;em&gt;stdout&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;load-the-job-into-the-launchd-daemon&#34;&gt;Load the job into the &lt;em&gt;launchd&lt;/em&gt; daemon&lt;/h3&gt;
&lt;p&gt;Load the &lt;em&gt;plist&lt;/em&gt; definition into the &lt;em&gt;launchd&lt;/em&gt; is very simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo launchctl load pl.skrajewski.backup.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that’s it. The job is currently managed by &lt;em&gt;launchd&lt;/em&gt;. If you have an error &lt;code&gt;Path had bad ownership/permissions&lt;/code&gt; don’t worry! Change the owner of this file to &lt;code&gt;root:wheel&lt;/code&gt; by running the below command&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chown root:wheel pl.skrajewski.backup.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and try to load the file again. If you want to change the definition of your job, remember about the &lt;code&gt;unload&lt;/code&gt; it first using &lt;code&gt;launchctl unload&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;You can find both of this files on &lt;a href=&#34;https://gist.github.com/skrajewski/cc6f42c8e8a5a23b776257a7b0d43a16&#34;&gt;GitHub Gists&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cryptomator is a good piece of software. Moreover, it’s open source. I like the idea of a transparent encryption because it significantly facilitates access to encrypted files. The client-side encryption also put a limit on ways how your data is used by cloud storage providers. This is a double-edged weapon – your data is secure, but nobody helps you if you lost the master key.&lt;/p&gt;
&lt;p&gt;When it comes to the proposed mirroring solution – this is only my experiment. I treat this more like curio rather than a way to synchronize files. That’s why I mirror my files instead of storing them directly in cryptomator’s vault. When I wrote this article, &lt;em&gt;Cryptomator CLI&lt;/em&gt; was in &lt;em&gt;0.3.1-Pre-release&lt;/em&gt; version. It’s not stable yet but I continue my tests.&lt;/p&gt;
&lt;p&gt;I can’t wait for the JavaScript library for the Cryptomator. I’d like to see web-based clients and possible integration with popular cloud storage providers. Client-side encryption is good for folks, not companies that live from their users’ data. Maybe this is the reason why we don’t have this kind of encryption yet?&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://cryptomator.org/&#34;&gt;Cryptomator – Official site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/cryptomator/cli&#34;&gt;cryptomator/cli on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.launchd.info/&#34;&gt;launchd.info – A lot of information about the &lt;em&gt;launchd&lt;/em&gt; configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html&#34;&gt;launchd Manual Page from Apple Developer Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/42159690/why-are-the-jce-unlimited-strength-not-included-by-default&#34;&gt;&lt;em&gt;Why are the JCE Unlimited Strength not included by default?&lt;/em&gt; – Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Arguments in arrow function explained</title>
      <link>https://szymonkrajewski.pl/arguments-in-arrow-functions/</link>
      <pubDate>Mon, 25 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/arguments-in-arrow-functions/</guid>
      <description>&lt;p&gt;ECMAScript 6 has an amazing feature called &lt;strong&gt;Arrow Function&lt;/strong&gt;. It allows writing a function expression faster and smarter than traditional way (using &lt;em&gt;function&lt;/em&gt;keyword). Worth to know, it’s not a &lt;em&gt;one to one&lt;/em&gt; replacement for standard functions.&lt;/p&gt;
&lt;p&gt;Everyone knows that the &lt;em&gt;arrow function&lt;/em&gt; doesn’t have own &lt;code&gt;this&lt;/code&gt;. More precisely: &lt;em&gt;arrow function&lt;/em&gt; doesn’t have the own &lt;strong&gt;lexical scope&lt;/strong&gt;. It uses an immediately enclosing scope instead. What does it mean?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arguments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Please notice, that this function use &lt;code&gt;arguments&lt;/code&gt; variable, which is the reference to the array of arguments provided to lexical scope of the &lt;em&gt;sumMany&lt;/em&gt; function. Let’s try something similar in ES6.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arguments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unfortunately, it doesn’t work. We used an &lt;code&gt;arguments&lt;/code&gt; variable as before but in this case, this variable is bound to enclosing scope. In this case, it doesn’t have defined &lt;code&gt;arguments&lt;/code&gt;. According to the &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions-runtime-semantics-evaluation&#34;&gt;ECMAScript 2015 Language Specification&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any reference to &lt;code&gt;arguments&lt;/code&gt;, &lt;code&gt;super&lt;/code&gt;, &lt;code&gt;this&lt;/code&gt;, or &lt;code&gt;new.target&lt;/code&gt; within an &lt;em&gt;ArrowFunction&lt;/em&gt; must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And this is ok! Because &lt;em&gt;arrow function&lt;/em&gt; wasn’t designed to be a substitute of a traditional function expression. Probably you have a dozen of places in your code where its behavior was desirable. Just think about all these &lt;code&gt;.bind(this)&lt;/code&gt;in your code. Looks familiar?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAll&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Jake&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}]});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UserController&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;printNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;forEach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is a mistake in above code, take a look at the usage of &lt;code&gt;findAll&lt;/code&gt; function. The nested function has different scope, so instead of assign users to the controller, it assigns theirs to the &lt;strong&gt;global object&lt;/strong&gt;, e.g. &lt;em&gt;Window&lt;/em&gt;. To fix it, we should bind a nested function to the enclosing scope.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UserController&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or pass reference to correct &lt;em&gt;this&lt;/em&gt;, e.g.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UserController&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both of them look hacky and tricky. If you use an ES6, you’ll have an easy way to handle it. By using an &lt;em&gt;arrow function&lt;/em&gt; you’ll make your code more understandable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UserController&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;printNames&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;forEach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wherever you need to provide a simple callback function, you may apply &lt;em&gt;arrow functions&lt;/em&gt;. They are also very helpful when you want to write the code in a functional style. Ultimately, let’s fix our &lt;em&gt;sumMany&lt;/em&gt; implementation&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;call&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;arguments&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sumMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Keep in mind when you have to use a function literal instead of an arrow function. It’s very helpful and saves a lot of time.&lt;/p&gt;
&lt;h3 id=&#34;resources&#34;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/&#34;&gt;http://www.ecma-international.org/ecma-262/6.0/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Blackboxing JavaScript using Chrome DevTools</title>
      <link>https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/</link>
      <pubDate>Mon, 11 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/</guid>
      <description>&lt;p&gt;Lots of JavaScript projects use a dozen of various frameworks, libraries, and third-party scripts. More often code which you produce largely depends from vendors’ work. The good example is jQuery and Lodash libraries which are involved in our pretty code. They are designed to facilitate the work and they are the remedy for especially commonly used, repeatable pieces of code.&lt;/p&gt;
&lt;p&gt;This kind of libraries (and frameworks) provide an another abstraction layer behind our code making the execution process more complex. It’s invisible until you’ll need to debug your code because of errors.&lt;/p&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;p&gt;Let’s consider this simple snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$tableBody&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.js-table-body&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;chain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;times&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;td&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$td&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;tr&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$td&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;forEach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$tr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;$tr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;appendTo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;$tableBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This part of code takes advantage of &lt;em&gt;jQuery&lt;/em&gt; and &lt;em&gt;Lodash&lt;/em&gt; libraries and simply put rows with the random even number into the table. Unfortunately, this logic is mixed together with third party functions. If you put the breakpoint trying to debug the flow of the chain step by step, you’ll be flipped to the library code.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/6_code_after_step.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/6_code_after_step_hu_8628b9d0958f18d9.png 360w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/6_code_after_step_hu_d7f0eba675cde0e4.png 640w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/6_code_after_step_hu_258c3f242b78a318.png 776w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;776&#34;
          height=&#34;279&#34;
           alt=&#34;Step to the next function call&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;h2 id=&#34;solution&#34;&gt;Solution&lt;/h2&gt;
&lt;p&gt;To avoid exploring the third party code during debugging you can use the feature from &lt;strong&gt;Chrome DevTools&lt;/strong&gt; called &lt;strong&gt;Blackboxing&lt;/strong&gt;. When you reach the file you have to omit, call the context menu on source code and use the option &lt;em&gt;Blackbox Script&lt;/em&gt;.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/4_context_menu_blackbox.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/4_context_menu_blackbox_hu_f81006bc4c6d02d1.png 360w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/4_context_menu_blackbox_hu_f16c3152fad96922.png 640w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/4_context_menu_blackbox_hu_b785d2312c35adee.png 942w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;942&#34;
          height=&#34;690&#34;
           alt=&#34;Blackbox Script option in context menu&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;p&gt;In &lt;em&gt;Call Stack&lt;/em&gt;, you’ll have an information that some frames are hidden because of blackboxing the script. Now if you use &lt;em&gt;Step into next function call&lt;/em&gt; the debugger will skip all unnecessary frames.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames_hu_4610bf8d13b4b512.png 360w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames_hu_437208c217f6e646.png 640w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames_hu_b3aaaf2fc8065352.png 960w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames_hu_60ab187f7e0dde79.png 1200w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/3_hidden_frames_hu_5690a83bcdba425d.png 1312w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1312&#34;
          height=&#34;510&#34;
           alt=&#34;Hidden frames in call stack because of blackboxing&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;p&gt;If you want to see the list of blackboxed scripts, you’ll simply go to &lt;strong&gt;Chrome DevTools -&amp;gt; Settings -&amp;gt; Blackboxing&lt;/strong&gt;. You can also add the new blackboxed script by hand.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing_hu_cacc5a1885a32a51.png 360w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing_hu_2de272fec7362413.png 640w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing_hu_b8767049e93dc6a7.png 960w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing_hu_216d9f3db699d1cb.png 1200w, https://szymonkrajewski.pl/blackboxing-javascript-code-using-chrome-devtools/images/5_settings_blackboxing_hu_a4f5c83379fd9baa.png 1288w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1288&#34;
          height=&#34;524&#34;
           alt=&#34;List of patterns to blackboxing&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;h3 id=&#34;tip-use-regular-expressions-in-pattern-to-blackbox-common-scripts&#34;&gt;Tip: Use regular expressions in pattern to blackbox common scripts&lt;/h3&gt;
&lt;p&gt;As long as you don’t need to dig into some libraries or frameworks you can put them into the blackbox. Prepare your list and put into the pattern list. You can also omit all minified files by using this regular expression&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;/\.+\.min\.js$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is one of the many features in &lt;strong&gt;Chrome DevTools&lt;/strong&gt; that I haven’t used before because I haven’t known it even exists. It only shows that you should get to know better with tools which you use in your work. They are to make your work easy and fast.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Try explain the JavaScript’s truth inconsistency</title>
      <link>https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/</link>
      <pubDate>Mon, 28 Aug 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/</guid>
      <description>&lt;p&gt;Everyone has an own version of a truth. I have the own truth, you have your own. JavaScript has many truths and it’s probably no problem until you’ll start to use it implicitly. This article shows and explains one of the popular JavaScript’s trap – inconsistent evaluation in &lt;em&gt;If Statement&lt;/em&gt; and &lt;em&gt;Equality Comparison&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;p&gt;We could start by running simple snippet:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Condition true, check again and is&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Have you tried? Probably not just once. It’s not a joke from ECMAScript developers, just mechanisms which are used to resolving final value from &lt;em&gt;Expression&lt;/em&gt;. In this article, I’ll explain this confusing behavior.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/green_stop_hu_bc06cbd3988328fb.webp 360w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/green_stop_hu_6f4d8d45b57e73f.webp 410w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/green_stop.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/green_stop_hu_3923f5a10ab18587.jpg 360w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/green_stop_hu_f13289d953607562.jpg 410w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;410&#34;
          height=&#34;374&#34;
           alt=&#34;green stop sign&#34; 
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;h2 id=&#34;evaluate-an-empty-array-inside-if-statement&#34;&gt;Evaluate an empty array inside &lt;em&gt;If Statement&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at official ECMAScript Language Specification at the &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-if-statement-runtime-semantics-evaluation&#34;&gt;13.6.7 section – The &lt;/a&gt;&lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-if-statement-runtime-semantics-evaluation&#34;&gt;&lt;em&gt;If&lt;/em&gt;&lt;/a&gt;&lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-if-statement-runtime-semantics-evaluation&#34;&gt; Statement – Runtime Semantics: Evaluation&lt;/a&gt;. A whole document contains a clear description of how all things works in ECMAScript. Right now, let’s focus on this specific behavior.&lt;/p&gt;
&lt;p&gt;Our &lt;em&gt;If Statement&lt;/em&gt; contains empty array&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We would consider first two steps from the official specification. The document says:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let exprRef be the result of evaluating Expression.&lt;/li&gt;
&lt;li&gt;Let exprValue be ToBoolean(GetValue(exprRef)).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our &lt;code&gt;exprRef&lt;/code&gt; should be a result of evaluating &lt;em&gt;Expression&lt;/em&gt;. However, we don’t need to evaluate anything, because we supplied an object (empty array) in place of &lt;em&gt;Expression&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Subsequently, we need to obtain &lt;code&gt;exprValue&lt;/code&gt; which is a result of the composition of two functions &lt;em&gt;GetValue&lt;/em&gt; and &lt;em&gt;ToBoolean&lt;/em&gt;. I don’t want to dig into the first, just assume, that the result of &lt;code&gt;GetValue(exprRef)&lt;/code&gt; is simply an empty array.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Under the hood, the &lt;em&gt;GetValue&lt;/em&gt; function resolve path to the value based on the reference. In this example given &lt;em&gt;Expression&lt;/em&gt; has concrete Type (not Reference) and there is no need to resolve it.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The last thing before we got the &lt;code&gt;exprValue&lt;/code&gt; is called a &lt;em&gt;ToBoolean&lt;/em&gt; function with empty array passed through. This function works as a simple switch statement – It returns a &lt;em&gt;boolean&lt;/em&gt; value based on matching argument.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_a346dd9f25216e27.png 360w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_b9f0c26ec97a7976.png 640w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_d6509d1f5f8167e7.png 960w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_890a7871e0d35aae.png 1200w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_288d5a5b12dc8767.png 1600w, https://szymonkrajewski.pl/try-explain-javascripts-truth-inconsistency/images/table_to_boolean_hu_829b80da5ba8e2df.png 1652w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1652&#34;
          height=&#34;766&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;p&gt;Above image presents the &lt;em&gt;ToBoolean&lt;/em&gt; results based on the supplied argument. Have you noticed, that there is no &lt;em&gt;Array&lt;/em&gt; type in this table? It is nothing wrong with it because what we know as an &lt;em&gt;Array&lt;/em&gt;, in JavaScript is an &lt;em&gt;Object&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typeof&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;              
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Based on above table, the expected result for &lt;em&gt;Object&lt;/em&gt; is &lt;em&gt;&lt;strong&gt;True&lt;/strong&gt;&lt;/em&gt;, so that is our &lt;code&gt;exprValue&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([])&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;equality-comparison-with-empty-array-on-one-side&#34;&gt;Equality Comparison with empty array on one side&lt;/h2&gt;
&lt;p&gt;The next confusing element in this example is the &lt;code&gt;console.log&lt;/code&gt; statement containing equality comparison&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks logically, but how these expressions were evaluated? The algorithm is described in section &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-abstract-equality-comparison&#34;&gt;7.2.6 Abstract Equality Comparison&lt;/a&gt; in ECMAScript Specification:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ReturnIfAbrupt(&lt;em&gt;x&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;ReturnIfAbrupt(&lt;em&gt;y&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is the same as Type(&lt;em&gt;y&lt;/em&gt;), then, return the result of performing Strict Equality Comparison &lt;em&gt;x&lt;/em&gt; === &lt;em&gt;y&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;em&gt;x&lt;/em&gt; is null and &lt;em&gt;y&lt;/em&gt; is undefined, return true.&lt;/li&gt;
&lt;li&gt;If &lt;em&gt;x&lt;/em&gt; is undefined and &lt;em&gt;y&lt;/em&gt; is null, return true.&lt;/li&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is Number and Type(&lt;em&gt;y&lt;/em&gt;) is String, return the result of the comparison &lt;em&gt;x&lt;/em&gt; == ToNumber(&lt;em&gt;y&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is String and Type(&lt;em&gt;y&lt;/em&gt;) is Number, return the result of the comparison ToNumber(&lt;em&gt;x&lt;/em&gt;) == &lt;em&gt;y&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is Boolean, return the result of the comparison ToNumber(&lt;em&gt;x&lt;/em&gt;) == &lt;em&gt;y&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If Type(&lt;em&gt;y&lt;/em&gt;) is Boolean, return the result of the comparison &lt;em&gt;x&lt;/em&gt; == ToNumber(&lt;em&gt;y&lt;/em&gt;).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is either String, Number, or Symbol and Type(&lt;em&gt;y&lt;/em&gt;) is Object, then return the result of the comparison &lt;em&gt;x&lt;/em&gt; == ToPrimitive(&lt;em&gt;y&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is Object and Type(&lt;em&gt;x&lt;/em&gt;) is either String, Number, or Symbol, then return the result of the comparison ToPrimitive(&lt;em&gt;x&lt;/em&gt;) == &lt;em&gt;y&lt;/em&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Return false.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In our expression, we have boolean and object value. First matched condition is no 9 which converts a &lt;em&gt;boolean&lt;/em&gt; value to &lt;em&gt;Number&lt;/em&gt;. According to &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber&#34;&gt;ToNumber&lt;/a&gt;function, for &lt;em&gt;boolean&lt;/em&gt; it returns &lt;strong&gt;1&lt;/strong&gt; when the argument is true, otherwise &lt;strong&gt;+0&lt;/strong&gt;. Returned value is used in place of the old one, so actually, the expression looks like&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;toprimitive-evaluation&#34;&gt;&lt;em&gt;ToPrimitive&lt;/em&gt; evaluation&lt;/h2&gt;
&lt;p&gt;Now, the conditions match to the second marked rule. It’s not possible to quickly compare both of these values because there is a comparison between an object and a primitive value. Therefore we should convert this object to the primitive using &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive&#34;&gt;ToPrimitive&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;ToPrimitive algorithm for &lt;em&gt;object&lt;/em&gt; parameter looks very complex. The second parameter of this function is &lt;em&gt;PreferredType&lt;/em&gt;, which is used to set a correct &lt;em&gt;hint&lt;/em&gt;variable. Unfortunately, there is an optional argument and it’s no provided in this function call. Instead, the algorithm use the &lt;strong&gt;“default”&lt;/strong&gt; value, it performs some checks, and later on, it assigns &lt;strong&gt;“number”&lt;/strong&gt; as a &lt;em&gt;hint&lt;/em&gt; and passed it together with the &lt;em&gt;object&lt;/em&gt; to the &lt;em&gt;OrdinaryToPrimitive&lt;/em&gt; function. Its algorithm looks very interesting.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Assert: Type(&lt;em&gt;O&lt;/em&gt;) is Object&lt;/li&gt;
&lt;li&gt;Assert: Type(&lt;em&gt;hint&lt;/em&gt;) is String and its value is either &lt;code&gt;&amp;quot;string&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;number&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;em&gt;hint&lt;/em&gt; is &lt;code&gt;&amp;quot;string&amp;quot;&lt;/code&gt;, then
&lt;ol&gt;
&lt;li&gt;Let &lt;em&gt;methodNames&lt;/em&gt; be «&lt;code&gt;&amp;quot;toString&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;valueOf&amp;quot;&lt;/code&gt;».&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Else,
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Let &lt;em&gt;methodNames&lt;/em&gt; be «&lt;code&gt;&amp;quot;valueOf&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;toString&amp;quot;&lt;/code&gt;».&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;For each &lt;em&gt;name&lt;/em&gt; in &lt;em&gt;methodNames&lt;/em&gt; in List order, do
&lt;ol&gt;
&lt;li&gt;Let &lt;em&gt;method&lt;/em&gt; be Get(&lt;em&gt;O&lt;/em&gt;, &lt;em&gt;name&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;ReturnIfAbrupt(&lt;em&gt;method&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;If IsCallable(&lt;em&gt;method&lt;/em&gt;) is true, then
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Let &lt;em&gt;result&lt;/em&gt; be Call(&lt;em&gt;method&lt;/em&gt;, O).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;ReturnIfAbrupt(&lt;em&gt;result&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If Type(&lt;em&gt;result&lt;/em&gt;) is not Object, return &lt;em&gt;result&lt;/em&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Throw a &lt;strong&gt;TypeError&lt;/strong&gt; exception.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First, we have to construct a &lt;em&gt;methodNames&lt;/em&gt; list – for &lt;strong&gt;“number”&lt;/strong&gt; it would contain &lt;code&gt;[&amp;quot;valueOf&amp;quot;, &amp;quot;toString&amp;quot;]&lt;/code&gt; methods. To get the final value we should execute these methods in order until the &lt;em&gt;result&lt;/em&gt; will be a primitive value. Function &lt;code&gt;valueOf&lt;/code&gt; for &lt;em&gt;array&lt;/em&gt; returns an array itself, so the &lt;code&gt;toString&lt;/code&gt; must be called. Finally, our conditions will be executed similarly to these&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;valueOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;valueOf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or simpler&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Right, it’s not finished yet but it looks more familiar and more logically. According to the &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-abstract-relational-comparison&#34;&gt;Abstract Equality Comparison&lt;/a&gt; Algorithm:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;If Type(&lt;em&gt;x&lt;/em&gt;) is String and Type(&lt;em&gt;y&lt;/em&gt;) is Number, return the result of the comparison ToNumber(&lt;em&gt;x&lt;/em&gt;) == &lt;em&gt;y&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/blockquote&gt;
&lt;p&gt;A full description of converting the &lt;em&gt;string&lt;/em&gt; to &lt;em&gt;Number&lt;/em&gt; is in section &lt;a href=&#34;https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber-applied-to-the-string-type&#34;&gt;ToNumber Applied to the String Type&lt;/a&gt;. In this case, an empty string is converted to the &lt;strong&gt;0&lt;/strong&gt; value. That’s it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;The problem with an inconsistent truth in JavaScript results from the process of conversion values. It’s not a simple casting from one type to another, rather a complex selection algorithm working behind the scene. So maybe next time when you’ll be in trouble, try to understand how specific things work. The 5 minutes spent on the investigation will be a better investment than another couple of nasty adjectives that do not give anything.&lt;/p&gt;
&lt;p&gt;And remember this simple trick:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;===&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Try to guess what I mean, or...&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Say precisely what you expect :)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;resources&#34;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/37021335/how-is-1-1-in-javascript/37021387&#34;&gt;http://stackoverflow.com/questions/37021335/how-is-1-1-in-javascript/37021387&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.ecma-international.org/ecma-262/6.0/&#34;&gt;http://www.ecma-international.org/ecma-262/6.0/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Do your constants make sense?</title>
      <link>https://szymonkrajewski.pl/do-your-constants-make-sense/</link>
      <pubDate>Mon, 14 Aug 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/do-your-constants-make-sense/</guid>
      <description>&lt;p&gt;Do you use &lt;em&gt;constants&lt;/em&gt; in your code? Probably a lot. Even if you don’t define your own is highly probable that you use some constants defined by other libraries or a language itself. Constants are very powerful unless you use it in the wrong way.&lt;/p&gt;
&lt;h2 id=&#34;why-constants&#34;&gt;Why constants?&lt;/h2&gt;
&lt;p&gt;Let’s consider some math constants&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;PI&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;3.14159265&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;E&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;2.71828183&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The value of &lt;em&gt;Pi&lt;/em&gt; (or &lt;em&gt;e&lt;/em&gt;) will never change in the future so it is a great candidate to put it into the constant. How does it look in our programs?&lt;/p&gt;
&lt;p&gt;Everyone tells the difference between &lt;em&gt;variable&lt;/em&gt; and &lt;em&gt;const&lt;/em&gt;. It’s easy to imagine and the names of this two things leave no doubt. The value of constant doesn’t be changed during normal program execution but could be set by the developer, therefore quite often they are used to store &lt;strong&gt;configuration data&lt;/strong&gt;, &lt;strong&gt;named states&lt;/strong&gt; and many other things.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;API_KEY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4c4712a4141d261ec0ca8f9037950685&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;VERSION&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;9.4.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;STATE_NEW&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;STATE_COMPLETE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;STATE_NEW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;finish&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;STATE_COMPLETE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Constants describe the value, so the code is more readable and clean. If you use constants in this way, then it’s perfect. What if someone wants to get rid of all &lt;strong&gt;magic numbers&lt;/strong&gt; and &lt;strong&gt;magic strings&lt;/strong&gt; from code? Probably something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Printer&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AbstractPrinter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FORMAT_A4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;A4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$format&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;FORMAT_A4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Document should be in &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;FORMAT_A4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;handlePrint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;changing-value-of-wrong-named-constant&#34;&gt;Changing value of wrong-named constant&lt;/h2&gt;
&lt;p&gt;Now imagine that someone has to change the format in the printer, e.g. to &lt;strong&gt;B5&lt;/strong&gt;. For this example, let’s assume, that it have to be done without creating a new implementation. The easiest way to do this is by changing the value of the constant &lt;code&gt;FORMAT_A4&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FORMAT_A4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;B5&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It seems at least strange and may lead to misunderstanding and mistakes. The better name for this value is, for instance, &lt;code&gt;PRINTER_FORMAT&lt;/code&gt; or simply &lt;code&gt;FORMAT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The previous example was a little bit exaggerated but it shows the main problem with naming things. We use constants to prevent doing changes in many places so, in conclusion, the name of the constant should be resistant to change their value. &lt;strong&gt;Name the constant according to the purpose of its value, rather than the value itself.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://secure.php.net/manual/en/language.constants.php&#34;&gt;https://secure.php.net/manual/en/language.constants.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>What I learned by doing my simple personal project</title>
      <link>https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/</link>
      <pubDate>Fri, 21 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/</guid>
      <description>&lt;p&gt;In September, I wanted to try something new, so I started a simple project to check my abilities in UX and front-end development. The idea was simple — create customizable, reusable and highly accessible buttons. I have experience with front-end but I usually use CSS frameworks like Bootstrap or Foundation. But not now. I decided to design the whole page completely from scratch.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This post was originally published on Medium platform. You can see the former version &lt;a href=&#34;https://medium.com/@szykra/what-i-learned-by-doing-my-simple-personal-project-c8a7c7cb0194&#34;&gt;directly in medium.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;figure class=&#34;figure left&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/01_buttons.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/01_buttons_hu_f32f1fb35d49711c.png 360w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/01_buttons_hu_c960b44965893bf4.png 640w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/01_buttons_hu_b9796bc327e304af.png 960w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/01_buttons_hu_f612948641979281.png 964w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;964&#34;
          height=&#34;120&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Alfred input window with prepared File Filter to look for notes.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The final project is hosted on Github pages. Click on the link below if you want to explore this project. Check the &lt;em&gt;Playground&lt;/em&gt; page and play with many variants of buttons!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://skrajewski.github.io/the-buttons&#34;&gt;&lt;strong&gt;The Buttons Project –&lt;/strong&gt; Project of highly accessible and customizable buttons&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Are you a front-end developer? Feel free to review my code on&lt;/em&gt; &lt;em&gt;&lt;a href=&#34;https://github.com/skrajewski/the-buttons&#34;&gt;Github&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before I started, I tried to answer these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why do I want to do it?&lt;/li&gt;
&lt;li&gt;Why buttons and not something else instead?&lt;/li&gt;
&lt;li&gt;How can I customize these buttons?&lt;/li&gt;
&lt;li&gt;How can I ensure accessibility?&lt;/li&gt;
&lt;li&gt;How should I start?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My answers are below…&lt;/p&gt;
&lt;h2 id=&#34;why-do-i-want-to-do-it&#34;&gt;Why do I want to do it?&lt;/h2&gt;
&lt;p&gt;For learning purposes. Simple. It’s a kind of test for myself. I can say that something is pretty or ugly but I can’t rate my own work. And it’s a great opportunity to try something new (in my case I throw away LESS in favor of SASS).&lt;/p&gt;
&lt;h2 id=&#34;why-buttons-and-not-something-else-instead&#34;&gt;Why buttons and not something else instead?&lt;/h2&gt;
&lt;p&gt;I looked for a simple element which has a lot of variants, states, possibilities and use cases. Buttons are omnipresent and they are used on every website.&lt;/p&gt;
&lt;h2 id=&#34;how-can-i-customize-these-buttons&#34;&gt;How can I customize these buttons?&lt;/h2&gt;
&lt;p&gt;I wanted to change many properties of my buttons without any extra line in CSS (SCSS in this case) files. I tried to list which parameters should be changeable. For each parameter, I wrote set of special CSS classes to change the final appearance.&lt;/p&gt;
&lt;p&gt;This approach is followed by Bootstrap, Semantic UI, Foundation, and many other front-end frameworks. But it has one serious inconvenience — use HTML to describe, how the button looks like. It should be the CSS role. Instead, I have to change HTML if I want to change appearance. There is a big advantage — I can insert good-looking buttons on my site without any extra line of CSS.&lt;/p&gt;
&lt;h2 id=&#34;how-can-i-ensure-accessibility&#34;&gt;How can I ensure accessibility?&lt;/h2&gt;
&lt;p&gt;That was a good question. I didn’t need this kind of functionality and I didn’t know what the problem is. So first I search something information about color blindness. Later on, I tried to emulate various kinds of color blindness to felt it. I had no idea how much this emulation reflects reality but I started to do something with it.&lt;/p&gt;
&lt;h2 id=&#34;how-should-i-start&#34;&gt;How should I start?&lt;/h2&gt;
&lt;p&gt;Usually, I start a project by defining dependencies and directory structure. In this case, I tried to keep everything as minimal as possible. I added just things which I was sure that I will use them. My first HTML element on the site was… button.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;A month later I had finished and I had decided to publish my work. There are my conclusions about this “simple” project.&lt;/p&gt;
&lt;h2 id=&#34;i-was-the-victim-of-new-project-illusion&#34;&gt;I was the victim of “new project” illusion&lt;/h2&gt;
&lt;p&gt;The idea for a new project is always great. The perspectives for doing creative and interesting things drive to start working as fast as possible, but when I started, an enthusiasm had disappeared when the first big problem appeared.&lt;/p&gt;
&lt;h2 id=&#34;i-questioned-my-original-idea&#34;&gt;I questioned my original idea&lt;/h2&gt;
&lt;p&gt;Sometimes in my mind, a lot of random thoughts disturb me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Is it necessary?”&lt;/li&gt;
&lt;li&gt;“Are you sure it is that what you should do now?”&lt;/li&gt;
&lt;li&gt;“It doesn’t make any sense…”&lt;/li&gt;
&lt;li&gt;“Nobody needs this.”&lt;/li&gt;
&lt;li&gt;“You don’t have enough skill to do this”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have similar thoughts, stop. Stop thinking in this way immediately. I had these thoughts too often but I knew what I had said at the beginning. I started this project because I wanted to train something. But I had another reason to continue working. I wanted to finish it because I had too many incomplete side projects on my mind or my hard drive. Truly, I was motivated by the answer for my first question (&lt;em&gt;answer?&lt;/em&gt;) and willingness to complete this project. Because I did it for myself.&lt;/p&gt;
&lt;h2 id=&#34;many-states--many-problems-with-presentation&#34;&gt;Many states = many problems with presentation&lt;/h2&gt;
&lt;p&gt;Designing a button which shows all its states properly is hard. We can’t freely design physical button’s state. In HTML, we have a few states to visualize (&lt;em&gt;normal, hover, active, focus, visited&lt;/em&gt;). Each of them should look different to recognize which state is currently in use.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;img
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        src=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/02_buttons_focus.gif&#34;
        
        
        
      /&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Focus state with normal button.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I tried to change hue, colors, and shadows to make state transitions similar to that physical buttons have. I am satisfied with the final effect but I try to find another, maybe better settings.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;img
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        src=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/03_buttons_hover.gif&#34;
        
        
        
      /&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Hover state with filled button.
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&#34;a-million-colors-none-of-them-was-suitable&#34;&gt;A Million colors, none of them was suitable&lt;/h2&gt;
&lt;p&gt;Colors are meaningful. It’s impossible to change the meaning of some colors e.g. red or green. We know that green color used to mean that something good happens. Appropriately, the red color usually means that something danger happened or will happen. The appearance of buttons is determined by colors and style. For me, it was the worst thing to do in this project, because I am a programmer, not a designer.&lt;/p&gt;
&lt;p&gt;I said colors are meaningful. What happens if some (or all) colors disappear? It is an inconvenient situation for people with color vision deficiency (CVD). So, if a color doesn’t work as we expect, what else can take responsibility for giving a meaning?&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/04_buttons_a11y.png&#34;
          srcset=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/04_buttons_a11y_hu_abb3c2716740ee79.png 360w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/04_buttons_a11y_hu_f640ccf1ce660a3c.png 640w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/04_buttons_a11y_hu_3e2c549c1cb6c3a7.png 940w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;940&#34;
          height=&#34;142&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;figcaption class=&#34;center&#34; &gt;
      Buttons with accessibility mode (with color blindness emulation).
    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The answer is a pattern. If we add a specific pattern to buttons with the same meaning, they still meaningful even without colors. Seems good, but how design these patterns? To be honest, I found some examples of CSS patterns and I have applied it to buttons. I haven’t done any research but in my opinion, it will be helpful.&lt;/p&gt;
&lt;h2 id=&#34;toolset-changes-dynamically&#34;&gt;Toolset changes dynamically&lt;/h2&gt;
&lt;p&gt;I started the project with simple &lt;em&gt;Gulpfile&lt;/em&gt; and a few files of HTML and SCSS. Later I added &lt;a href=&#34;https://mozilla.github.io/nunjucks/&#34;&gt;&lt;em&gt;nunjuck&lt;/em&gt;&lt;/a&gt; templating system but I quickly replaced them by AngularJS. It’s perfectly OK when you add or change tools to ease your work. Building the whole toolbox at the beginning doesn’t make much sense because sooner or later changes will need.&lt;/p&gt;
&lt;figure class=&#34;figure center&#34;&gt;&lt;picture&gt;&lt;source type=&#34;image/webp&#34; srcset=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_3dd7944064fb1c31.webp 360w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_1278e2f3d190ce87.webp 640w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_b474c7d3a51934a9.webp 960w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_267bf1a9cd86b9c4.webp 1200w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_df0d127ed83b6e05.webp 1280w&#34; sizes=&#34;(min-width: 900px) 78ch, 100vw&#34; /&gt;&lt;img
          loading=&#34;lazy&#34;
          decoding=&#34;async&#34;
          src=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools.jpg&#34;
          srcset=&#34;https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_ce8cb4f91d068edf.jpg 360w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_705208a1d3b99b53.jpg 640w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_7a5a2022ac50d861.jpg 960w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_23e1c0f8e07207e1.jpg 1200w, https://szymonkrajewski.pl/what-i-learned-by-doing-my-simple-personal-project/images/05_tools_hu_4579795d31b3ac63.jpg 1280w&#34;
          sizes=&#34;(min-width: 900px) 78ch, 100vw&#34;
          width=&#34;1280&#34;
          height=&#34;853&#34;
          
          
          
        /&gt;
      &lt;/picture&gt;&lt;/figure&gt;

&lt;p&gt;The problems arise during work. Get the right tool to solve each of them. Don’t use a hammer to everything.&lt;/p&gt;
&lt;h2 id=&#34;i-had-too-little-time-than-i-thought-before&#34;&gt;I had too little time than I thought before&lt;/h2&gt;
&lt;p&gt;I had a plan to spend minimum two hours per day to push the project forward.&lt;/p&gt;
&lt;p&gt;Not a chance.&lt;/p&gt;
&lt;p&gt;In my case of course. I have a regular job, a private life, other hobbies which need my attention and time. Sometimes I have been done completely different things to prevent boredom. I had to change my approach to the project and I tried to do small steps every day. I defined my tasks to keep it as small as possible, so I was able to do each of them at once.&lt;/p&gt;
&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;I know that the project which I have done is easy, small and “to-do in a few hours”. Although it wasn’t a technical challenge for me, I learned much non-technical stuff. I’m proud of myself that I did it. So, maybe you have any abandoned side project on your computer. Maybe is it a good time to refreshing it and finally finish it?&lt;/p&gt;</description>
    </item>
    
  </channel>
</rss>
