<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dimitri Merejkowsky</title>
    <description>The latest articles on DEV Community by Dimitri Merejkowsky (@dmerejkowsky).</description>
    <link>https://dev.to/dmerejkowsky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F223%2F0ae18c13-0136-4142-b69f-1f87ad8e8bda.jpg</url>
      <title>DEV Community: Dimitri Merejkowsky</title>
      <link>https://dev.to/dmerejkowsky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmerejkowsky"/>
    <language>en</language>
    <item>
      <title>Classes vs Types (or Yet Another Reason To Learn Rust)</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Thu, 23 Jun 2022 10:11:47 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/classes-vs-types-or-yet-another-reason-to-learn-rust-208h</link>
      <guid>https://dev.to/dmerejkowsky/classes-vs-types-or-yet-another-reason-to-learn-rust-208h</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Note: this is both a follow-up to the &lt;a href="https://dmerej.info/blog/post/classes-suck/"&gt;do classes suck&lt;/a&gt; or &lt;a href="https://dmerej.info/blog/post/classes-rock/"&gt;do classes suck&lt;/a&gt; articles, &lt;em&gt;and&lt;/em&gt; yet another reason why you should &lt;a href="https://dmerej.info/blog/post/rust-secrets-and-logs/"&gt;learn Rust&lt;/a&gt;, so you should have read at least one of them in order to understand the context for this post.&lt;/p&gt;

&lt;h1&gt;
  
  
  Back to the spec
&lt;/h1&gt;

&lt;p&gt;As a reminder, here’s our spec:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When robots come off the factory floor, they have no name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The first time a robot boots, a random name is generated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every once in a while, the robots are reset to their factory settings, and the next time they boot, they get a &lt;em&gt;new&lt;/em&gt; random name.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Using Rust
&lt;/h1&gt;

&lt;p&gt;The goal of Rust is to allow to code to be 3 things at once: correct, fast, and expressive. This is no easy task and that’s why Rust is a bit complicated to learn (which is not such a bad thing in my opinion, but I digress).&lt;/p&gt;

&lt;p&gt;Anyway, what’s interesting with the Rust programming language is that we can express the spec in such a way that &lt;strong&gt;invalid states lead to compilation errors&lt;/strong&gt; and &lt;strong&gt;without using anything but structs and methods&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how. Since the specs talks about names that may or may not exist, we’re going to use two different types - one for the unnamed robots, and one for the robots that have a name. To be precise, those are called &lt;em&gt;structs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We wrap the robot name inside a struct&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;NamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// We create a brand new type for "robots that don't have names"&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;UnnamedRobot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Note : interestingly, this struct costs *nothing* to allocate and is&lt;/span&gt;
&lt;span class="c1"&gt;// known as a zero-sized type (ZST) in Rust parlance.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can express the &lt;em&gt;transitions&lt;/em&gt; between allowed states as public methods on those types (inside a &lt;code&gt;impl&lt;/code&gt; block) or as free functions&lt;/p&gt;

&lt;p&gt;Note: that most of the bodies are omitted here, but you can find tho whole source code &lt;a href="https://github.com/dmerejkowsky/robots/blob/main/rust/src/lib.rs"&gt;on github&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Note: this is a private implementation detail, so no&lt;/span&gt;
&lt;span class="c1"&gt;// `pub` here!&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;generate_random_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_robot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UnnamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;UnnamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Note: emit a compiler warning if users call start() without&lt;/span&gt;
    &lt;span class="c1"&gt;// using the return value&lt;/span&gt;
    &lt;span class="nd"&gt;#[must_use]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_random_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;NamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;NamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Note: taking ownership of `self` here!&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UnnamedRobot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid code compiles of course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_robot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New robot with name: {name}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And invalid code won’t compile. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_robot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// Error: method name() not found for UnnamedRobot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can reset a robot, restart it and get a new name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_robot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_ne!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may wondering why we need &lt;code&gt;.to_string()&lt;/code&gt; here. Well, let me explain.&lt;/p&gt;

&lt;h1&gt;
  
  
  Owning and borrowing
&lt;/h1&gt;

&lt;p&gt;I mentioned earlier that &lt;code&gt;reset&lt;/code&gt; &lt;em&gt;takes ownership&lt;/em&gt; of the robot.&lt;/p&gt;

&lt;p&gt;That’s because the method uses &lt;code&gt;self&lt;/code&gt; as first parameter.&lt;/p&gt;

&lt;p&gt;In contrast, the &lt;code&gt;name&lt;/code&gt; method uses &lt;code&gt;&amp;amp;self&lt;/code&gt; and &lt;em&gt;borrows&lt;/em&gt; the robot.&lt;/p&gt;

&lt;p&gt;This matters because in addition to having a rich type system as we saw above, the Rust compiler also enforce rules about ownership.&lt;/p&gt;

&lt;p&gt;Here are those rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each value in Rust has an owner.&lt;/li&gt;
&lt;li&gt;There can only be one owner at a time.&lt;/li&gt;
&lt;li&gt;At any given time, you can have either &lt;strong&gt;one mutable&lt;/strong&gt; reference or any number of &lt;strong&gt;immutable&lt;/strong&gt; references.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What this means is that when the robot is started, you can access the robot name any time you want, as long as you don’t try to modify it. &lt;em&gt;But&lt;/em&gt;, once &lt;code&gt;reset()&lt;/code&gt; is called, the value has been moved, and thus you can no longer access the robot name.&lt;/p&gt;

&lt;p&gt;Let’s this in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_robot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;robot&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Error: value `robot` moved during called to reset reset()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Rust had allowed this code to compile, we would have a &lt;code&gt;name3&lt;/code&gt; variable &lt;em&gt;after&lt;/em&gt; the robot has been reset, which is not allowed by our spec!&lt;/p&gt;

&lt;p&gt;Note that this also means we can’t have &lt;code&gt;name1&lt;/code&gt; and &lt;code&gt;name2&lt;/code&gt; be merely string references (&lt;code&gt;&amp;amp;str&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;That’s why we use the &lt;code&gt;.to_string()&lt;/code&gt; method, which gives us as a &lt;em&gt;mutable copy&lt;/em&gt; (a &lt;code&gt;String&lt;/code&gt;) for the robot name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you learn Rust, you’ll find that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There’s more to Object-Oriented Programming than just classes&lt;/li&gt;
&lt;li&gt;The borrow checker, when used well, can prevent a bunch of mistakes &lt;strong&gt;at compile time&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Side note: I used two different types to make a point. You will find a more idiomatic version of the code in the &lt;a href="https://github.com/dmerejkowsky/robots/blob/idiomatic/rust/src/lib.rs"&gt;aptly named “idiomatic” branch on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The again show again how the borrow checker rules prevent invalid states &lt;em&gt;at compile time&lt;/em&gt;, this time because some methods (like &lt;code&gt;start&lt;/code&gt; or &lt;code&gt;reset&lt;/code&gt;) use &lt;code&gt;&amp;amp;mut self&lt;/code&gt; and other just &lt;code&gt;&amp;amp;self&lt;/code&gt; (like &lt;code&gt;name&lt;/code&gt;). It also shows the “new type” pattern, in order to enforce robot name format at runtime.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out my &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;contact page&lt;/a&gt; for more ways to get in touch with me.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>classes</category>
      <category>types</category>
    </item>
    <item>
      <title>Is Rust worth learning? Part 1: logs and secrets</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Tue, 04 Jan 2022 10:11:47 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/is-rust-worth-learning-part-1-logs-and-secrets-43im</link>
      <guid>https://dev.to/dmerejkowsky/is-rust-worth-learning-part-1-logs-and-secrets-43im</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dmerej.info/blog/post/rust-secrets-and-logs/"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A language that doesn’t affect the way you think about programming, is not worth knowing. – Alan J. Perlis - Yale University&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To see if Rust is worth trying, then, let me tell you a story - based on real events.&lt;/p&gt;

&lt;h1&gt;
  
  
  Logs and secrets
&lt;/h1&gt;

&lt;p&gt;Let’s say you are writing a Java application that needs to make HTTP calls on an external API.&lt;/p&gt;

&lt;p&gt;To do this, you have a client class that implements a &lt;code&gt;call&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;doRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you need do send an &lt;code&gt;app secret&lt;/code&gt; along the url to make the call, hence the separate &lt;code&gt;authenticate()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;You also have a Config record to hold the configuration of the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you have a main class that reads the values from the environment, builds a Config and a Client instances and uses it to make calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APP_SECRET"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_URL"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;Client&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since logs are important, you also add a call to &lt;code&gt;logger.info()&lt;/code&gt; when making the call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appSecret&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Making the call"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- new&lt;/span&gt;
  &lt;span class="n"&gt;doRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A vulnerability happens
&lt;/h1&gt;

&lt;p&gt;You push the code into production, and some time later you get assigned to the following bug in the issue tracker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUG: APP_SECRET found in the logs
SEVERITY : critical
PRIORITY : high

The logs sent by the application at startup contains the
APP_SECRET

...
INFO: config: Config[appSecret=s3cret, url=https://api.dev]
...
INFO: Making the call

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you take a look at the code base, and sure enough, you find the problem: someone from an other team added a log containing the contents of the Config class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SomewhereElse.java&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure, the bug is easy to fix. You can just remove the call to &lt;code&gt;logger.info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But after thinking about it more, you decide to also override the &lt;code&gt;toString()&lt;/code&gt; method of the &lt;code&gt;Config&lt;/code&gt; struct so that the app secret is &lt;em&gt;always&lt;/em&gt; redacted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;public record Config(...) {
&lt;/span&gt;&lt;span class="gi"&gt;+ @Override
+ public String toString() {
+ return String.format("Config[appSecret:REDACTED, url:%s]", this.url);
+ }
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There. You can mark the bug as “fixed” and move to something else, like take a look at this new programming language called Rust …&lt;/p&gt;

&lt;p&gt;A few hours later, here’s what you learned.&lt;/p&gt;

&lt;h1&gt;
  
  
  Ownership
&lt;/h1&gt;

&lt;p&gt;First, Rust has this notion of &lt;em&gt;ownership&lt;/em&gt;: every piece of data must have exactly one owner. By default, data is &lt;em&gt;moved&lt;/em&gt; and can’t be used after the move.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;StringOwner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- note: fields are private by default&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Creates a new string&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringOwner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Move the string into&lt;/span&gt;
                                        &lt;span class="c1"&gt;// the 'StringOwner' struct&lt;/span&gt;

  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Compile error: cannot use the string after it's moved&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, if you don’t want to move the value, you have to “borrow” it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;borrow_the_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt; Note the '&amp;amp;'&lt;/span&gt;
   &lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Creates a new string&lt;/span&gt;

  &lt;span class="nf"&gt;borrow_the_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Note the '&amp;amp;'&lt;/span&gt;

  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// OK&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a immutable (or shared) borrow: you won’t be able to modify the string.&lt;/p&gt;

&lt;p&gt;You’ve also learned about mutable (or exclusive) borrows. They would allow you to modify the string, but they are not relevant for this story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trait system
&lt;/h2&gt;

&lt;p&gt;You also learned that classes don’t exist in Rust. Instead, Rust uses &lt;em&gt;traits&lt;/em&gt;, and adds methods to structs using &lt;code&gt;impl&lt;/code&gt; blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* in stuff.rs */&lt;/span&gt;

&lt;span class="c1"&gt;// This says that the Stuffer trait&lt;/span&gt;
&lt;span class="c1"&gt;// contains a method named do_stuff&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Stuffer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This says that Foo implements the Stuffer trait,&lt;/span&gt;
&lt;span class="c1"&gt;// and than compilation will fail if do_stuff() is not present&lt;/span&gt;
&lt;span class="c1"&gt;// or has not the proper signature&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Stuffer&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Implementation here&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trait must be in the scope where you are using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;stuff&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="nf"&gt;.do_stuff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Error: Stuffer trait is not in scope&lt;/span&gt;


&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;stuff&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;stuff&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stuffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- new&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="nf"&gt;.do_stuff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// OK: Stuffer trait is in scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A rewrite
&lt;/h1&gt;

&lt;p&gt;So, feeling adventurous, you decide to try and re-implement your application in Rust, just to feel like how the code would look like and if you can find a better way of handling the security issue you had to fix in Java.&lt;/p&gt;

&lt;p&gt;You start with the &lt;code&gt;Config&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you add the &lt;code&gt;Client class&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Making the call"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.do_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, the main() function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APP_SECRET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_URL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.app_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“Well, that was easy” you think. “I wonder what people mean when they’re talking about ‘fighting the borrow checker’”.&lt;/p&gt;

&lt;p&gt;Then you try to use &lt;code&gt;client.call&lt;/code&gt; a second time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.app_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.app_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- new&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0382]: use of moved value: `config.app_secret`
  --&amp;gt; src/main.rs:27:17
   |
23 | client.call(config.app_secret, config.url);
   | ----------------- value moved here
...
27 | client.call(config.app_secret, config.url);
   | ^^^^^^^^^^^^^^^^^ value used here after move

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“Oh, right”, I need to “borrow” the &lt;code&gt;app_secret&lt;/code&gt; and the &lt;code&gt;url&lt;/code&gt; in the client if I want to be able to keep using the config struct.&lt;/p&gt;

&lt;p&gt;So you change the code to be like this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  impl Client {
&lt;span class="gd"&gt;- fn call(&amp;amp;self, app_secret: String, url: String) {
&lt;/span&gt;&lt;span class="gi"&gt;+ fn call(&amp;amp;self, app_secret: &amp;amp;str, url: &amp;amp;str) {
&lt;/span&gt;          //
      }

  }

- client.call(config.app_secret, config.url);
&lt;span class="gi"&gt;+ client.call(&amp;amp;config.app_secret, &amp;amp;config.url);
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There. Now the client &lt;em&gt;borrows&lt;/em&gt; the app secret and the url and the code compiles and runs fine.&lt;/p&gt;

&lt;p&gt;Feeling confident, you try and reproduce the logging issue.&lt;/p&gt;

&lt;p&gt;First, you add a &lt;code&gt;#[derive(Debug)]&lt;/code&gt; annotation on top of the &lt;code&gt;Config&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ #[derive(Debug)]
&lt;/span&gt;  struct Config {

  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you add a log displaying the contents of the config struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  fn main() {
    let config = Config { app_secret, url };
&lt;span class="gi"&gt;+ info!("config: {:?}", &amp;amp;config);
&lt;/span&gt;   }
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works because Rust knows how to print debug representations of strings and booleans and the &lt;code&gt;derive(Debug)&lt;/code&gt; annotation can automatically generates the code to print a debug representation of the &lt;code&gt;Config&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Indeed, the code compile, and, sure enough, the secret shows up in the log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] config: Config { app_secret: "s3cret", url: "https://api.dev" }
[INFO] Making the call to https://api.dev

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, you’ve reproduced the problem. But can you find a better solution this time?&lt;/p&gt;

&lt;h1&gt;
  
  
  Time to think
&lt;/h1&gt;

&lt;p&gt;You’ve heard good things about Rust type system, so you wonder: “Would it be possible to solve the problem by adding a new type?”.&lt;/p&gt;

&lt;p&gt;You start with a &lt;code&gt;SecretString&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The constructor takes ownership of the string, which is a good thing, because it means the inner value &lt;em&gt;can no longer be accessed&lt;/em&gt; once the &lt;code&gt;SecretString&lt;/code&gt; struct is created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APP_SECRET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SecretString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="nd"&gt;dbg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt; does not compile!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you change the type of &lt;code&gt;app_secret&lt;/code&gt; in Config from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;SecretString&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;struct Config {
&lt;/span&gt;&lt;span class="gd"&gt;- app_secret: String,
&lt;/span&gt;&lt;span class="gi"&gt;+ app_secret: SecretString,
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which means you have to get the inner value when authenticating the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;impl Client {
&lt;/span&gt;&lt;span class="gd"&gt;- fn call(&amp;amp;self, app_secret: &amp;amp;str, url: &amp;amp;str) {
- self.authenticate(&amp;amp;app_secret);
&lt;/span&gt;&lt;span class="gi"&gt;+ fn call(&amp;amp;self, app_secret: &amp;amp;SecretString, url: &amp;amp;str) {
+ self.authenticate(app_secret.inner);
+ }
&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are then faced with 2 compile errors:&lt;/p&gt;

&lt;p&gt;First, the &lt;code&gt;Debug&lt;/code&gt; macro no longer works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0277]: `SecretString` doesn't implement `std::fmt::Debug`
  --&amp;gt; src/main.rs:26:5
   |
24 | #[derive(Debug)]
   | ----- in this derive macro expansion
25 | struct Config {
26 | app_secret: SecretString,
   | ^^^^^^^^^^^^^^^^^^^^^^^^ `SecretString` cannot be formatted using `{:?}`
   |
   = help: the trait `std::fmt::Debug` is not implemented for `SecretString`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix this, you implement the debug trait yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ^-- new - remember: traits needs to be in scope&lt;/span&gt;
&lt;span class="c1"&gt;// in order to use them&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Debug&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"REDACTED"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, you can’t use the &lt;code&gt;inner&lt;/code&gt; field directly in the &lt;code&gt;call()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0616]: field `inner` of struct `SecretString` is private
  --&amp;gt; src/client.rs:13:39
   |
13 | let secret_value = app_secret.inner;
   | ^^^^^ private field

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you add a public method to access the inner value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expose_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you fix the client code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SecretString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_secret&lt;/span&gt;&lt;span class="nf"&gt;.expose_value&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- new!&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Going further
&lt;/h1&gt;

&lt;p&gt;Then you think back about the rule about traits having to be in scope, and you decide to move the &lt;code&gt;expose_value&lt;/code&gt; method into a trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ExposeSecret&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expose_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ExposeSecret&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SecretString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ^-- used to be `impl SecretString`&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expose_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then the compiler says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0599]: no method named `expose_value` found for reference
`&amp;amp;SecretString` in the current scope
  --&amp;gt; src/client.rs:13:39
   |
13 | let secret_value = app_secret.expose_value();
   | ^^^^^^^^^^^^

   = help: items from traits can only be used if the trait is in scope
   = help: the following trait is implemented but not in scope; perhaps add a
           `use` for it:

use crate::secrets::ExposeSecret;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you comply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ExposeSecret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- new!&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;secrecy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SecretString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There - the code compiles, and it’s now pretty hard to leak the app secret:&lt;/p&gt;

&lt;p&gt;Indeed, if a value has been wrapped in the SecretString struct, anyone attempting to access it must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call a method that &lt;em&gt;sounds&lt;/em&gt; dangerous (&lt;code&gt;expose_value()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;import a trait that &lt;em&gt;also&lt;/em&gt; sounds dangerous (&lt;code&gt;secrets::ExposeSecret&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s also very easy to edit the code for safety: just list the usages of the &lt;code&gt;ExposeSecret&lt;/code&gt; trait.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope you found this post interesting: it shows how you can use some unique features of the Rust programming language to tackle an old issue in a novel way.&lt;/p&gt;

&lt;p&gt;I mentioned at the beginning that the this post is based on true stories, so here are my two sources of inspiration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The issue of a secret value exposed by mistake is based on an actual security vulnerability I reported to the Miniflux maintainers. You can see the details in the &lt;a href="https://github.com/miniflux/v2/commit/87d58987a698296fa4306ed43e8400568edd51f1"&gt;miniflux repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;SecretString&lt;/code&gt; struct is based on a real crate, called &lt;a href="https://docs.rs/secrecy/latest/secrecy/"&gt;secrecy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for taking this journey with me, and if you’re a reading this while trying to fix the log4j security vulnerability, you have my sincere sympathy :)&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out my &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;contact page&lt;/a&gt; for more ways to get in touch with me.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>logs</category>
      <category>secrets</category>
      <category>learning</category>
    </item>
    <item>
      <title>Merge first, ask questions later - Optimistic Merging in practice</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Tue, 14 Sep 2021 14:06:46 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/optimistic-merging-in-practice-1j4a</link>
      <guid>https://dev.to/dmerejkowsky/optimistic-merging-in-practice-1j4a</guid>
      <description>&lt;h1&gt;
  
  
  Introduction - Pessimistic Merging
&lt;/h1&gt;

&lt;p&gt;If you’ve contributed to an open-source project on GitHub or GitLab, or written code for a company, you’re probably familiar with the concept of “Pessimistic Merging”. You write a patch (or a pull request or a merge request), but it is not merged (or applied) right away. Instead, you need to wait for human approval to be given and/or for continuous integration to pass.&lt;/p&gt;

&lt;p&gt;I can think of two reasons why this strategy is often the one which is used:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s been used by really big projects (like the Linux kernel) for quite some time&lt;/li&gt;
&lt;li&gt;It’s kind of the &lt;em&gt;default&lt;/em&gt; way to collaborate on platforms like GitHub or GitLab&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are even tools like Gerrit which work by specifying a list of constraints before a patch can be merged &lt;sup id="fnref1"&gt;1&lt;/sup&gt; - which is used among other things by Google’s Android Open Source Project or the Qt framework.&lt;/p&gt;

&lt;p&gt;There’s also the fact that it allows to enforce some rules. Used in combination with source control software (like Git), Pessimistic Merging can ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A “clean” git history (from the commit messages to their contents to the size of the patches)&lt;/li&gt;
&lt;li&gt;A consistent coding style&lt;/li&gt;
&lt;li&gt;A test suite that always pass on the main development branch&lt;/li&gt;
&lt;li&gt;A code that is composed only of patches that have been reviewed and approved by an arbitrary number of people&lt;/li&gt;
&lt;li&gt;… and so on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And indeed, there are some projects where this matters a lot - in a corporate environment, or in a “sensitive” project like a browser, a database engine or an operating system …&lt;/p&gt;

&lt;p&gt;That being said, we rarely talk about the &lt;em&gt;downsides&lt;/em&gt; of Pessimistic Merging - which is why you probably never thought that this concept needed a &lt;em&gt;name&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Issues with Pessimistic Merging
&lt;/h1&gt;

&lt;p&gt;For this, I’ll let you read the article which prompted me to write this one: &lt;a href="http://hintjens.com/blog:106"&gt;Why Optimistic Merging Works Better&lt;/a&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The idea behind Optimistic Merging is very simple: you &lt;em&gt;merge the patch first&lt;/em&gt;, and &lt;em&gt;then&lt;/em&gt; deal with issues like broken builds, code style violation and other problems.&lt;/p&gt;

&lt;p&gt;Sure, this means that the main development branch of your Git repository may now contain commits that do not fully work, code that does not always compile, or commits that only fix code styling errors.&lt;/p&gt;

&lt;p&gt;But for a small or medium-sized project, that does nothing too security-sensitive, and for which maintainers and collaborators are hard to find - is it such a bad deal?&lt;/p&gt;

&lt;p&gt;On this topic, see also Aleksey Kladov’s piece on &lt;a href="https://matklad.github.io/2021/01/03/two-kinds-of-code-review.html"&gt;Two Kinds of Code Review&lt;/a&gt; (which is the reason I learned about Optimistic Merging in the first place and wanted to try it out), where he explains how pull requests are reviewed and merged for the rust-analyzer project.&lt;/p&gt;

&lt;p&gt;A note before I continue: they are plenty of cases where using Pessimistic Merging &lt;em&gt;is&lt;/em&gt; the best strategy - here I’m just describing an alternate strategy you may find interesting.&lt;/p&gt;

&lt;p&gt;With that out of the way, let’s now look how Optimistic Merging works for me in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  My workflow for Optimistic Merging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting the stage
&lt;/h3&gt;

&lt;p&gt;I’m the maintainer of a project called “foo”, written in Python, and hosted in GitHub.&lt;/p&gt;

&lt;p&gt;The project uses &lt;code&gt;flake8&lt;/code&gt; as a linter to catch small mistakes, the code is formatted with &lt;code&gt;black&lt;/code&gt;, and it has has some tests (of course). It is also &lt;em&gt;very easy&lt;/em&gt; to run the linters, the tests and the code formatter from the developer machine (this will matter later on).&lt;/p&gt;

&lt;p&gt;Here comes a developer I never heard of (let’s call them Mallory) - and they open their first ever merge request to the GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merging Mallory’s changes
&lt;/h3&gt;

&lt;p&gt;The first step is for me to get the proposed changes on my machine and to prepare a list of notes for later feedback - a simple text file is enough.&lt;/p&gt;

&lt;p&gt;Since it’s a PR on GitHub, I can do something like this, where “some-branch” is the &lt;em&gt;source ref&lt;/em&gt; of Mallory’s PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git remote add mallory git@github.com:mallory/foo
$ git fetch mallory
From github.com:mallory/foo
* [new branch] some-branch -&amp;gt; mallory/some-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I rebase &lt;code&gt;some-branch&lt;/code&gt; on top of the main branch. This allows me to make sure the commits can be merged without conflicts.&lt;/p&gt;

&lt;p&gt;During the rebase I look at each commit diff and I take notes.&lt;/p&gt;

&lt;p&gt;Since everything is on my machine, it’s trivial to amend Mallory’s commits if a linter complains or to run &lt;code&gt;black&lt;/code&gt; if I need to.&lt;/p&gt;

&lt;p&gt;I can also choose to run the tests locally if I fear a commit may have caused a regression.&lt;/p&gt;

&lt;p&gt;Finally, if there’s a small issue (like a misleading comment) I can fix it right away! There’s no need to go through the hassle of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;telling Mallory they did something wrong,&lt;/li&gt;
&lt;li&gt;waiting for them to update the merge request,&lt;/li&gt;
&lt;li&gt;re-starting the process from scratch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When all of this is done, it’s time to push Mallory’s branch. I re-run the linters and the tests just to be sure, then I push the code and leave a message in the PR discussion telling Mallory the changes have been merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking at the interaction from Mallory’s point of view
&lt;/h3&gt;

&lt;p&gt;It’s very likely that Mallory was not paid to write the patch, (this is a medium-size open-source project, after all) but they still worked on it and thought their work was good enough to open a pull request.&lt;/p&gt;

&lt;p&gt;They probably don’t know much about the project, and it’s likely they don’t know everything about Git, Python, flake8 or black. And it’s even possible they know nothing about testing!&lt;/p&gt;

&lt;p&gt;Yet, the first reply they got for the maintainer is “Thanks! Your commits have been merged”. Boom - the collaboration is already working - what a great start!&lt;/p&gt;

&lt;p&gt;Contrast with a typical response when using Pessimistic Merging, which would look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thank you for your contribution, but the CI does not pass (test_foo_bar4 is failing and there are some lint errors). Also, please merge or rebase your work on top of the main branch so that the merge is fast-forward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;… followed by a bunch of nitpicks about the merge request.&lt;/p&gt;

&lt;h3&gt;
  
  
  After the merge
&lt;/h3&gt;

&lt;p&gt;Anyway, now it’s time to put my notes to good use.&lt;/p&gt;

&lt;p&gt;If there are some trivial changes I had to do, I can just tell Mallory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It was a good idea to refactor the do_stuff() method inside the Foo class, but you did not adapt the doc string, which I’ve done in commit …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, we’re &lt;em&gt;working together&lt;/em&gt; here, and I’m willing to bet Mallory is more likely to watch their docstrings next time they write a new patch :)&lt;/p&gt;

&lt;p&gt;If there was some non-trivial changes (like a missed opportunity for a refactoring), I can just create an issue with a task list for instance.&lt;/p&gt;

&lt;p&gt;I can also start a pull request and have &lt;em&gt;Mallory&lt;/em&gt; review it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;To be frank, I’ve just started implementing this strategy for my open-source projects, so it’s a bit early to draw any conclusions. That being said, so far it has been going great, and I wanted to share the idea of Optimistic Merging right way.&lt;/p&gt;

&lt;p&gt;I’ll let you know how it goes in the future. Until then, happy coding!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;my contact page&lt;/a&gt; for more ways to get in touch with me&lt;/em&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Fun fact: to do this,  Gerrit embeds &lt;a href="https://gerrit-review.googlesource.com/Documentation/prolog-cookbook.html"&gt;an implementation of the Prolog Language&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;If you have trouble with opening this link, make sure you are using the http:// URL, the https:// version is  unfortunately broken. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>opensource</category>
      <category>workflow</category>
      <category>review</category>
    </item>
    <item>
      <title>Two awesome Rust warnings</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Tue, 02 Feb 2021 13:35:21 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/two-awesome-rust-warnings-4lcp</link>
      <guid>https://dev.to/dmerejkowsky/two-awesome-rust-warnings-4lcp</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dmerej.info/blog/post/awesome-rust-warnings/"&gt;my blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;If you’ve ever used a compiler (or any other tool) that can produce warnings about your code, you may be used to messages that are hard to understand, confusing or just plain wrong.&lt;/p&gt;

&lt;p&gt;But today I want to show you that good compiler warnings do exist and how they can make your code more correct.&lt;/p&gt;

&lt;h1&gt;
  
  
  The incorrect server API
&lt;/h1&gt;

&lt;p&gt;The first example comes from code I wrote to test the Rust implementation of the Tanker client SDK. You don’t need to know precisely how Tanker works to follow this example - let’s just say the client needs a “Tanker permanent identity” from an identity server to start a session. &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In my case, the identity server was already written in Go using the Gin-Gonic framework.&lt;/p&gt;

&lt;p&gt;Here’s what the implementation of the &lt;code&gt;sign_in&lt;/code&gt; route looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// This struct is used by various Gin handlers&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="n"&gt;PermanentIdentity&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"tanker_permanent_identity"&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// handler for the /sign_in route&lt;/span&gt;
&lt;span class="nf"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="py"&gt;.Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="n"&gt;SignInBody&lt;/span&gt;
    &lt;span class="n"&gt;jsonErr&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.BindJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;jsonErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="py"&gt;.H&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsonErr&lt;/span&gt;&lt;span class="nf"&gt;.Error&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.Email&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="py"&gt;.Password&lt;/span&gt;

    &lt;span class="c"&gt;// Not shown: check email and password, fetch user from the db&lt;/span&gt;

    &lt;span class="n"&gt;userResponse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="py"&gt;.Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="py"&gt;.UserID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;PermanentIdentity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="py"&gt;.PermanentIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To parse the JSON response from the server in the Rust client, I used serde, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;UserResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tanker_permanent_identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;sign_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.client&lt;/span&gt;&lt;span class="nf"&gt;.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/sign_in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="py"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.start_tanker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start_tanker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;private_identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user_response&lt;/span&gt;&lt;span class="py"&gt;.tanker_permanent_identity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user_response&lt;/span&gt;&lt;span class="py"&gt;.email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.tanker&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;private_identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c"&gt;// Not shown: verify identity by email when required&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I first compiled the code, I got this warning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: field is never read: `user_id`
  --&amp;gt; src/notepad.rs:24:5
   |
24 | user_id: String,
   | ^^^^^^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made sense: as you can see, to start a Tanker session we only need the &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;tanker_permanent_identity&lt;/code&gt; fields of the JSON response returned by the server. In particular, we don’t need the user ID at all.&lt;/p&gt;

&lt;p&gt;What’s interesting with this example is that at Tanker, we already wrote &lt;em&gt;six other clients&lt;/em&gt; for the server in question (using Python, Go, Java, Javascript, Objective-C and Ruby) and that was the first time we got an indication that our JSON API was wasting bandwidth by sending information that was actually not needed!&lt;/p&gt;

&lt;h1&gt;
  
  
  The useless mutation
&lt;/h1&gt;

&lt;p&gt;The second example comes from a personal project - a week ago I started implementing an interpreter in Rust, following the instructions from the &lt;a href="https://interpreterbook.com/"&gt;Writing an Interpreter in Go&lt;/a&gt;, by Throsten Ball.&lt;/p&gt;

&lt;p&gt;As I worked on the the lexer, I wrote the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Peekable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;str&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CharIndices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Lexer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Peekable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CharIndices&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Lexer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="nf"&gt;.char_indices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.peekable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;

    &lt;span class="c"&gt;/// Try to read an identifier starting at start_pos&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// end_pos is either `None` if we reached the end of the input code,&lt;/span&gt;
        &lt;span class="c"&gt;// or Some(index) if we reached a character that is not part of&lt;/span&gt;
        &lt;span class="c"&gt;// of a valid identifier&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;end_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// Is the next character valid?&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.iter&lt;/span&gt;&lt;span class="nf"&gt;.peek&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;peek&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;next_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nn"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;is_valid_ident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next_c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c"&gt;// Yes: advance next_pos&lt;/span&gt;
                    &lt;span class="n"&gt;end_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.iter&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;next_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c"&gt;// No: advance next_pos and exit the loop&lt;/span&gt;
                    &lt;span class="n"&gt;end_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c"&gt;// There's no next character : we've reached the end&lt;/span&gt;
                    &lt;span class="c"&gt;// of the input code&lt;/span&gt;
                    &lt;span class="n"&gt;end_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;end_pos&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// end_pos is None: return all the code from start_pos to the end&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start_pos&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="c"&gt;// end_pos is not None: return the code from start_pos to end_pos&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start_pos&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what the compiler had to say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning: value assigned to `end_pos` is never read
   --&amp;gt; src/lexer.rs:131:13
    |
131 | let mut end_pos = Some(start_pos);
    | ^^^^^^^^^^^
    |
    = note: `#[warn(unused_assignments)]` on by default
    = help: maybe it is overwritten before being read?

warning: value assigned to `end_pos` is never read
   --&amp;gt; src/lexer.rs:136:21
    |
136 | end_pos = Some(*next_pos);
    | ^^^^^^^
    |
    = help: maybe it is overwritten before being read?

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second warning was the easiest to fix: we keep setting &lt;code&gt;end_pos&lt;/code&gt; to&lt;code&gt;Some(*next_pos)&lt;/code&gt; but never read it until the loop exits. So we can just remove the line that sets &lt;code&gt;end_pos&lt;/code&gt; before calling &lt;code&gt;self.iter.next()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    fn read_identifier(&amp;amp;mut self, start_pos: usize) -&amp;gt; &amp;amp;'a str {
        let mut end_pos = Some(start_pos);

        loop {
              match peek {
&lt;span class="gd"&gt;- Some((next_pos, next_c)) if Self::is_valid_ident(*next_c) =&amp;gt; {
- end_pos = Some(*next_pos);
&lt;/span&gt;&lt;span class="gi"&gt;+ Some((_, next_c)) if Self::is_valid_ident(*next_c) =&amp;gt; {
&lt;/span&gt;                      self.iter.next();
                  }
                  Some((next_pos, _)) =&amp;gt; {
                      end_pos = Some(*next_pos);
                      break;
                  }
                  None =&amp;gt; {
                      end_pos = None;
                      break;
                  }
              }
        }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the compiler was still unhappy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning: value assigned to `end_pos` is never read
   --&amp;gt; src/lexer.rs:131:13
    |
131 | let mut end_pos = Some(start_pos);
    | ^^^^^^^^^^^
    |
    = note: `#[warn(unused_assignments)]` on by default
    = help: maybe it is overwritten before being read?

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This took me a while to realize, but the compiler is telling me that I can declare the &lt;code&gt;end_pos&lt;/code&gt; binding but not initialize it right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    fn read_identifier(&amp;amp;mut self, start_pos: usize) -&amp;gt; &amp;amp;'a str {
&lt;span class="gd"&gt;- let mut end_pos = Some(start_pos);
&lt;/span&gt;&lt;span class="gi"&gt;+ let end_pos;
&lt;/span&gt;        loop {
              match peek {
                  Some((_, next_c)) if Self::is_valid_ident(*next_c) =&amp;gt; {
                      end_pos = Some(*next_pos);
                      self.iter.next();
                  }
                  Some((next_pos, _)) =&amp;gt; {
                      end_pos = Some(*next_pos);
                      break;
                  }
                  None =&amp;gt; {
                      end_pos = None;
                      break;
                  }
              }
        }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I love this : the &lt;code&gt;end_pos&lt;/code&gt; binding is no longer &lt;code&gt;mut&lt;/code&gt;, and can &lt;em&gt;only&lt;/em&gt; be assign a value in two cases: either in the &lt;code&gt;None&lt;/code&gt; arm of the &lt;code&gt;match peek&lt;/code&gt; block, or to &lt;code&gt;end_pos&lt;/code&gt; when the peek character is not valid.&lt;/p&gt;

&lt;p&gt;It’s much less likely for the code to be buggy!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope those two examples gave you an idea of what it’s like to write code using a language that has such a focus on &lt;em&gt;correctness&lt;/em&gt; and how nice it is to have &lt;em&gt;useful&lt;/em&gt; warnings.&lt;/p&gt;

&lt;p&gt;For now, Rust is the only language I’ve used which gave me this feeling of “the compiler has my back”, but I’m sure they are others - please tell me bellow if you know one.&lt;/p&gt;

&lt;p&gt;And if this gave you a motivation to try and learn Rust, go for it!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;my contact page&lt;/a&gt; for more ways to get in touch with me.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;More info in the &lt;a href="https://docs.tanker.io/latest"&gt;Tanker documentation&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>rust</category>
      <category>compilers</category>
      <category>warnings</category>
    </item>
    <item>
      <title>dmerej.info: now on the Gemini space</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Thu, 19 Nov 2020 16:10:22 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/dmerej-info-now-on-the-gemini-space-bd6</link>
      <guid>https://dev.to/dmerejkowsky/dmerej-info-now-on-the-gemini-space-bd6</guid>
      <description>&lt;p&gt;Hi there,&lt;/p&gt;

&lt;p&gt;Just a quick note to let you know my personal website is now available on the Gemini space too.&lt;/p&gt;

&lt;p&gt;Get a &lt;a href="https://gemini.circumlunar.space/clients.html"&gt;Gemini client&lt;/a&gt; and see you at gemini://dmerej.info!&lt;/p&gt;

&lt;h1&gt;
  
  
  But Why?
&lt;/h1&gt;

&lt;p&gt;Good question! - the answer is only available on the Gemini version of the blog, though …&lt;/p&gt;

</description>
      <category>gemini</category>
    </item>
    <item>
      <title>The quest for a better hiring process</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Wed, 22 Jul 2020 15:20:36 +0000</pubDate>
      <link>https://dev.to/tanker/the-quest-for-a-better-hiring-process-2708</link>
      <guid>https://dev.to/tanker/the-quest-for-a-better-hiring-process-2708</guid>
      <description>&lt;h1&gt;
  
  
  The quest for a better hiring process
&lt;/h1&gt;

&lt;p&gt;I’ve been a software engineer for about ten years, and here’s something I learned: hiring software engineers is hard. One of the main challenges is to get the most information about the candidates in the minimum amount of time. You want to know if they will be a good fit for your team, and you want to know it quickly, without wasting their time.&lt;/p&gt;

&lt;p&gt;In this article, I’ll go through a list of the various techniques I’ve seen, describe the one we are using at &lt;a href="https://tanker.io"&gt;Tanker&lt;/a&gt;, and why it works best.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tour of existing techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The whiteboard
&lt;/h3&gt;

&lt;p&gt;Let’s start with the infamous whiteboard. You ask the candidates to write some code from scratch on a whiteboard to solve a given problem. This can give you an idea of how they think or how they approach new challenges, but bear in mind the whiteboard can get very intimidating for some developers, so they may appear less bright than usual.&lt;/p&gt;

&lt;p&gt;All in all, the whiteboard is a peek into the reasoning abilities of the candidate. Still, it doesn’t give any information about the ability to work in a team, understand a new codebase, or their skills with a given programming language.&lt;/p&gt;

&lt;h3&gt;
  
  
  The homework
&lt;/h3&gt;

&lt;p&gt;Another technique is to ask the candidate to implement a project “from scratch” at home. This technique avoids some of the above pitfalls, and you can quickly gauge the candidate’s level of competence.&lt;/p&gt;

&lt;p&gt;Note that you only get to see how they work alone and when writing code from scratch. Moreover, either it means a big-time investment for the candidate or the project is too small to correctly judge their skills.&lt;/p&gt;

&lt;p&gt;None of these techniques match our needs: we are a team with a flat organization in which each member has the right to contribute to important decisions. Besides, we already have a pretty large codebase, so future recruits will mostly work on &lt;em&gt;existing&lt;/em&gt; code.&lt;/p&gt;

&lt;p&gt;For these reasons, we use a &lt;em&gt;refactoring exercise&lt;/em&gt;, a technique I learned about during &lt;a href="https://www.rubytapas.com/2018/05/24/refactoring-for-interviews-nickolas-means/"&gt;an episode of Ruby Tapas presented by Nickolas Means&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As the name implies, the candidates will have to refactor some code. They also will have to demonstrate other abilities like reasoning about new features and dealing with tests.&lt;/p&gt;

&lt;p&gt;Here’s how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Refactoring Exercise
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before meeting the candidate
&lt;/h3&gt;

&lt;p&gt;First, take a simple problem and implement it. The code does not have to be very long, but you should make sure there’s room for improvement: leave out some duplication, use functions with lots of parameters, or other anti-patterns. However, do use features you want the candidate to be familiar with.&lt;/p&gt;

&lt;p&gt;Then write some basic tests and think about a new feature to add, but &lt;em&gt;don’t implement it&lt;/em&gt;. This will be important later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the exercise
&lt;/h3&gt;

&lt;p&gt;Remember, your goal is to gather the most valuable information about the candidates: you want to see how they work when they are at their best.&lt;/p&gt;

&lt;p&gt;For instance, it’s crucial to &lt;em&gt;tell the candidate&lt;/em&gt; about the refactoring exercise, so it’s not a surprise for them to make sure &lt;em&gt;they bring their own development machine&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When the candidate arrives, show them the code and tell them about the problem it’s solving.&lt;/p&gt;

&lt;p&gt;At this point, the code should be in a state that makes it hard to implement the new feature without refactoring.&lt;/p&gt;

&lt;p&gt;Then announce the new requirements and wait. Make it clear that the code should be production-ready.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Do they ask for existing tests?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do they try and refactor first?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your goal is to see if they think about those “good practices” without a prompt from your end. But if they start writing the new code directly, it’s not a red flag per se.&lt;/p&gt;

&lt;p&gt;You do want to see how they refactor and use tests, though, so if they skip all of the above, just gently nudge them in the right direction with questions like “Is there something you want to do before implementing the new feature?” or remarks like: “By the way, there are some tests if you think that’s helpful.”&lt;/p&gt;

&lt;p&gt;Your goal is to keep them comfortable, so don’t make them think they have failed if they did not ask about tests or refactoring right away. It happens.&lt;/p&gt;

&lt;p&gt;The rest is straightforward: watch the candidate do the refactor, implement the solution, and add tests. Note that the whole session should last about 30 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What running the exercise tells you
&lt;/h2&gt;

&lt;p&gt;Let’s recap what you’ve learned during the refactoring session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have a pretty good idea about the candidates’ skills and knowledge in your programming language&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can see how they make decisions about adding features, adding tests, and architecture of their code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You make sure that they can convert requirements into code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You know how it would be like to pair-program with them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At &lt;a href="https://tanker.io"&gt;Tanker&lt;/a&gt;, we’ve been using this technique for quite some time, and it has worked very well. It gives us a good idea of how candidates will do all day, every day — and all of that in 30 minutes — not bad!&lt;/p&gt;

&lt;p&gt;Interested in joining us? &lt;a href="https://tanker.io/jobs"&gt;Have a look at our job openings&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hiring</category>
      <category>interview</category>
    </item>
    <item>
      <title>Automating version number updates: what could go wrong?</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Thu, 11 Jun 2020 09:35:17 +0000</pubDate>
      <link>https://dev.to/tanker/automating-version-number-updates-what-could-go-wrong-83e</link>
      <guid>https://dev.to/tanker/automating-version-number-updates-what-could-go-wrong-83e</guid>
      <description>&lt;h1&gt;
  
  
  Automating version number updates: what could go wrong?
&lt;/h1&gt;

&lt;p&gt;Say you need to update (bump) your software. It’s currently at version 1.2, all the required changes have been merged, and it’s time to publish version 1.3. That’s really easy, right? Change the version in one file, commit, tag, and push. Done!&lt;/p&gt;

&lt;p&gt;I thought that too once, but the truth is that it’s harder than it looks — let me tell you my story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Releasing software for Softbank robotics
&lt;/h2&gt;

&lt;p&gt;Our story begins in 2008, when I was release manager at Softbank Robotics.&lt;/p&gt;

&lt;p&gt;I had two big projects to release: Choregraphe, a desktop GUI to control the NAO and Pepper robots, and qiBuild, a command-line application to ease C++ development.&lt;/p&gt;

&lt;p&gt;For qiBuild, I had three files to patch because the code version number was hard-coded in several places (&lt;code&gt;setup.py&lt;/code&gt;, &lt;code&gt;__init__.py&lt;/code&gt;, and &lt;code&gt;docs/conf.py&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;On the other hand, for Choregraphe the version number was hard-coded in the top &lt;code&gt;CMakeLists.txt&lt;/code&gt; file only, but there was quite a bit of code to “forward” the version number from the top &lt;code&gt;CMake&lt;/code&gt; file down to the &lt;code&gt;version.hpp&lt;/code&gt; and &lt;code&gt;main.cpp&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Both solutions had their pros and cons but I could not decide which one was best. Since I was pretty sure I was not the first one to have encountered this issue, I started to look around for better solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying out bumpversion
&lt;/h2&gt;

&lt;p&gt;The way bumpversion works is a nice combination of the two approaches I’ve mentioned before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, keep the hard-coded version number in as many files as required&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Second, add a &lt;em&gt;dedicated&lt;/em&gt; configuration file (&lt;code&gt;.bumpversion.cfg&lt;/code&gt;) containing the current version and the aforementioned list of files.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, when bumping the project from version X to version Y:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Iterate over the file list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replace all occurrences of X with Y, including in the configuration file itself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, I started with the qiBuild project and added the following configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# in .bumpversion.cfg
&lt;/span&gt;&lt;span class="nn"&gt;[bumpversion]&lt;/span&gt;
&lt;span class="py"&gt;current_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1.0.1&lt;/span&gt;
&lt;span class="py"&gt;commit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="py"&gt;tag&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;

&lt;span class="nn"&gt;[bumpversion:file:setup.py]&lt;/span&gt;

&lt;span class="nn"&gt;[bumpversion:file:qiBuild/__init__.py]&lt;/span&gt;

&lt;span class="nn"&gt;[bumpversion:file:docs/conf.py]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I had to bump qiBuild from 1.0.1 to 1.0.2, I ran:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bumpversion patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A commit was automatically made along with a matching tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; git show
&lt;span class="p"&gt;commit ec50897893ce4ecfb1debaa1df266ae4c555f45b (HEAD -&amp;gt; master, tag: v1.0.2)
Author: Dimitri Merejkowsky &amp;lt;dimitri.merejkowsky@tanker.io&amp;gt;
Date: Wed May 20 16:58:27 2020 +0200
&lt;/span&gt;
Bump version: 1.0.1 → 1.0.2
&lt;span class="gh"&gt;diff --git a/.bumpversion.cfg b/.bumpversion.cfg
&lt;/span&gt;&lt;span class="gd"&gt;-------- a/.bumpversion.cfg
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/.bumpversion.cfg
&lt;/span&gt; [bumpversion]
&lt;span class="gd"&gt;-current_version = 1.0.1
&lt;/span&gt;&lt;span class="gi"&gt;+current_version = 1.0.2
&lt;/span&gt; commit = True
 tag = True

diff --git a/qiBuild/__init__.py b/qiBuild/__init__.py
&lt;span class="gd"&gt;-------- a/qiBuild/__init__.py
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/qiBuild/__init__.py
&lt;/span&gt;&lt;span class="gd"&gt;-__version__ = “1.0.1”
&lt;/span&gt;&lt;span class="gi"&gt;+__version__ = “1.0.2”
&lt;/span&gt;
diff --git a/setup.py b/setup.py
&lt;span class="gd"&gt;-------- a/setup.py
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/setup.py
&lt;/span&gt; setup(
    name="qiBuild",
&lt;span class="gd"&gt;-   version="1.0.1",
&lt;/span&gt;&lt;span class="gi"&gt;+   version="1.0.2",
&lt;/span&gt;     )

diff --git a/docs/conf.py b/docs/conf.py
&lt;span class="gd"&gt;-------- a/docs/conf.py
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/docs/conf.py
&lt;/span&gt;&lt;span class="p"&gt;project = "qiBuild"
&lt;/span&gt;&lt;span class="gd"&gt;- version="1.0.1",
&lt;/span&gt;&lt;span class="gi"&gt;+ version="1.0.2",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now I just had to run &lt;code&gt;python setup.py sdist upload&lt;/code&gt; and the 1.0.2 release was published on pypi.org.&lt;/p&gt;

&lt;p&gt;For the NAOqi project, it worked very well too.&lt;/p&gt;

&lt;p&gt;I could delete a bunch of CMake and preprocessor code and replace it with just one line of C++ code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in naoqi/version.hpp&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;NAOQI_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.3.0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time the &lt;code&gt;.bumpversion.cfg&lt;/code&gt;file looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[bumpversion]&lt;/span&gt;
&lt;span class="py"&gt;current_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2.3.0&lt;/span&gt;
&lt;span class="py"&gt;files&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;include/naoqi/version.hpp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now bumping naoQI’s version could be done with a command similar to the one used to bump qiBuild.&lt;/p&gt;

&lt;p&gt;Brilliant! So, what went wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  Wandering off-track
&lt;/h2&gt;

&lt;p&gt;The problem can be described as being “too smart by half” and has to do with the command line syntax of bumpversion.&lt;/p&gt;

&lt;p&gt;Indeed, bumpversion is smart enough to bump various “parts” of the version number, namely the &lt;em&gt;major&lt;/em&gt;, &lt;em&gt;minor&lt;/em&gt;, and &lt;em&gt;patch&lt;/em&gt; components used in the semver spec.&lt;/p&gt;

&lt;p&gt;Here are some examples, assuming that the current version is 1.2.3:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bumpversion patch : 1.2.3 -&amp;gt; 1.2.4
bumpversion minor : 1.2.3 -&amp;gt; 1.3.0
bumpversion major : 1.2.3 -&amp;gt; 2.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We were using &lt;em&gt;semver&lt;/em&gt; for qiBuild and NAOqi too at the beginning — but sometimes &lt;em&gt;semver&lt;/em&gt; is not enough.&lt;/p&gt;

&lt;p&gt;Let’s continue our story. When qiBuild got more usage and the software team grew, publishing qiBuild releases started becoming… scary.&lt;/p&gt;

&lt;p&gt;New features were added, refactorings were made and qiBuild had become an essential tool for &lt;em&gt;all&lt;/em&gt; the developers in the software team (100 of them) — I was getting nervous about what would happen if I shipped a buggy release.&lt;/p&gt;

&lt;p&gt;So, with the help of members of my team, I decided to start making release candidates. That way, a few brave colleagues could help me catch bugs before everyone upgraded to the latest stable version.&lt;/p&gt;

&lt;p&gt;Since Python developers had already come up with a version scheme that allowed for release candidates, I started using that. See &lt;a href="https://www.python.org/dev/peps/pep-0440/"&gt;PEP 440&lt;/a&gt; for details. Basically, I could add a &lt;em&gt;rcX&lt;/em&gt; suffix after the &lt;em&gt;patch&lt;/em&gt; part.&lt;/p&gt;

&lt;p&gt;And… it turned out that doing so was far from trivial. Why?&lt;/p&gt;

&lt;p&gt;Well, bumpversion assumes you are using semver and, if you don’t, you need to specify a custom regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parse = (?P&amp;lt;major&amp;gt;\d+) 
 (?P&amp;lt;minor&amp;gt;\d+) 
 (\.(?P&amp;lt;patch&amp;gt;\d+))? .
 ((?P&amp;lt;release&amp;gt;rc)(?P&amp;lt;rel_num&amp;gt;\d+))?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not stop there.&lt;/p&gt;

&lt;p&gt;Now you need a way to tell bumpversion how to go from 2.0.0rc1 to 2.0.0rc2 (if the release candidate had bugs) or from 2.0.0rc2 to 2.0.1 (if it did not).&lt;/p&gt;

&lt;p&gt;So you add even more configuration, but it’s still not enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;serialize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
 &lt;span class="err"&gt;{major}.{minor}&lt;/span&gt;
 &lt;span class="err"&gt;{major}.{minor}.{patch}&lt;/span&gt;
 &lt;span class="err"&gt;{major}.{minor}{release}{rel_num}&lt;/span&gt;

&lt;span class="nn"&gt;[bumpversion:part:release]&lt;/span&gt;
&lt;span class="py"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
 &lt;span class="err"&gt;a&lt;/span&gt;
 &lt;span class="err"&gt;b&lt;/span&gt;
 &lt;span class="err"&gt;rc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find all the gory details in &lt;a href="https://github.com/peritus/bumpversion/issues/128"&gt;the GitHub issue&lt;/a&gt; but, in short, it was a show stopper.&lt;/p&gt;

&lt;p&gt;I believe that bumpversion is still not used at Softbank Robotics to this day, and, as far as I know, bumping qiBuild still needs version numbers to be fixed manually in several files.&lt;/p&gt;

&lt;p&gt;But our story does not end here — that would be a rather sad ending!&lt;/p&gt;

&lt;h2&gt;
  
  
  Arriving at Tanker
&lt;/h2&gt;

&lt;p&gt;In March 2016, I handed in my resignation letter at Softbank and, three months later, I started my current job at Tanker.&lt;/p&gt;

&lt;p&gt;In short, Tanker sells a product that can be used for end-to-end encryption, as well as client-side anonymization, packaged in open source SDKs (see &lt;a href="https://tanker.io"&gt;our website&lt;/a&gt; for details).&lt;/p&gt;

&lt;p&gt;Anyway, given we were releasing software and, because of my past experience, I was also in charge of release management at Tanker.&lt;/p&gt;

&lt;p&gt;As you might expect, Tanker also had hard-coded version numbers &lt;em&gt;and&lt;/em&gt; chunks of code whose only role was “forwarding” the version number from one file to another.&lt;/p&gt;

&lt;p&gt;“This is exactly the same problem I had last time!”, I thought. So, I took a look at bumpversion again — but even after all this time the bug I opened was still not fixed.&lt;/p&gt;

&lt;p&gt;That’s when I realized there were two big problems with bumpversion which would be pretty hard to fix without rewriting a lot of code.&lt;/p&gt;

&lt;p&gt;First, it’s very hard to reliably identify “parts” of version numbers. Semantics can vary from one version scheme to the next. Even &lt;a href="https://packaging.pypa.io/en/latest/_modules/packaging/version/#parse"&gt;comparing version numbers is a hard task&lt;/a&gt;, but guessing how to bump from a release candidate to a stable one is near impossible.&lt;/p&gt;

&lt;p&gt;Secondly, there are some hidden defaults at play which make understanding what’s going on under the hood pretty hard.&lt;/p&gt;

&lt;p&gt;In other words, bumpversion was “too clever by half”.&lt;/p&gt;

&lt;p&gt;So, what to do? Well, rewrite from scratch of course! (It turned out to be a good idea after all — otherwise, I would not brag about it here :P)&lt;/p&gt;

&lt;h2&gt;
  
  
  The birth of tbump
&lt;/h2&gt;

&lt;p&gt;On December 7, 2017, I started working on a rewrite called &lt;a href="https://github.com/TankerHQ/tbump"&gt;tbump&lt;/a&gt;. My goal was to keep bumpversion’s good ideas and to fix its shortcomings.&lt;/p&gt;

&lt;p&gt;Here are the main differences between tbump and bumpversion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;There are no hard-coded defaults: you must specify how the git message and the tag name will be formatted, and you also need to specify a regular expression to define the version scheme.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of specifying what part of the current version you want to update, you need to pass the whole new version.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Back to our example — to go from 2.1.3 to 2.1.4 you run &lt;code&gt;tbump 2.1.4&lt;/code&gt; instead of &lt;code&gt;bumpversion patch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those differences come with a price.&lt;/p&gt;

&lt;p&gt;First, since there is no hard-coded default it’s harder to use tbump out of the box.&lt;/p&gt;

&lt;p&gt;However, this one was easy to fix : I added an &lt;code&gt;init&lt;/code&gt; command to generate a &lt;code&gt;tbump.toml&lt;/code&gt; file automatically. Instead of having to read the docs, users can read the generated file and get started quickly.&lt;/p&gt;

&lt;p&gt;Secondly, since one has to specify the new version instead of a segment one wants to bump it’s easy to make mistakes, like going from 1.0.3 to 1.0.5 instead of 1.0.4.&lt;/p&gt;

&lt;p&gt;That’s where it gets interesting.&lt;/p&gt;

&lt;p&gt;You see, I was pretty annoyed by some aspects of the bumpversion UX, especially when trying to tweak the configuration file.&lt;/p&gt;

&lt;p&gt;Just watch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;bumpversion patch &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&amp;lt;nothing&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bumpversion patch &lt;span class="nt"&gt;--verbose&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;span class="nv"&gt;current_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0.2
&lt;span class="nv"&gt;commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True
&lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True
&lt;span class="nv"&gt;new_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now look at what &lt;code&gt;tbump --dry-run&lt;/code&gt; does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tbump &lt;span class="nt"&gt;--dry-run&lt;/span&gt; 1.0.3
:: Bumping from 1.0.2 to 1.0.3
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Would patch these files
- setup.py:3 &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0.2"&lt;/span&gt;,
+ setup.py:3 &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0.3"&lt;/span&gt;,
- foo.py:1 __version__ &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.2"&lt;/span&gt;
+ foo.py:1 __version__ &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.3"&lt;/span&gt;
- tbump.toml:2 current &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.2"&lt;/span&gt;
+ tbump.toml:2 current &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.0.3"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Would run these git commands
&lt;span class="nv"&gt;$ &lt;/span&gt;git add &lt;span class="nt"&gt;--update&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;--message&lt;/span&gt; Bump to 1.0.3
&lt;span class="nv"&gt;$ &lt;/span&gt;git tag &lt;span class="nt"&gt;--annotate&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; v1.0.3 v1.0.3
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin master
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin v1.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new version is all over the place, you can’t miss it, and at the same time, you see exactly what is going on.&lt;/p&gt;

&lt;p&gt;The output is similar without the — dry-run option, except changes are actually applied and git commands are run.&lt;/p&gt;

&lt;p&gt;And then I realized that every time I was bumping something, I would be following the same pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;tbump $NEW_VERSION --dry-run&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if everything was OK by reading the output&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not, back to Step 1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Otherwise, run &lt;code&gt;tbump $NEW_VERSION&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This looks like an algorithm — and what do we do with algorithms? We implement them!&lt;/p&gt;

&lt;p&gt;So here’s what the entry point of tbump looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# (simplified)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;bump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dry_run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Looking good (y/n)?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Canceled by user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;bump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dry_run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way you get a chance to catch any mistake in the new version just before it’s too late :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Early adoption
&lt;/h2&gt;

&lt;p&gt;In January 2018 tbump 1.0 was out (and I had been using tbump to bump itself since the 0.2 release).&lt;/p&gt;

&lt;p&gt;I published the package on pypi.org and I started using it both for Tanker projects and my own ones.&lt;/p&gt;

&lt;p&gt;It worked great! My fellow developers told me they liked the UX so I was pretty happy.&lt;/p&gt;

&lt;p&gt;But there was a critical flaw in tbump’s design that would come back to bite us pretty hard in the future. Let’s see how.&lt;/p&gt;

&lt;h2&gt;
  
  
  yarn workspaces
&lt;/h2&gt;

&lt;p&gt;Quite early in the development of our Javascript SDK, we knew we’d be using a mono-repo.&lt;/p&gt;

&lt;p&gt;For instance, we wanted to support both Node.JS (for fast-running tests) and the browser (which is the primary use of the SDK).&lt;/p&gt;

&lt;p&gt;So right off, we knew we would have at least three packages: &lt;code&gt;@tanker/core&lt;/code&gt;, &lt;code&gt;@tanker/client-browser&lt;/code&gt;, and &lt;code&gt;@tanker/client-node&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It did not make sense to have different version numbers for those three packages, so here’s what we ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In core/package.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanker/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;libsodium-wrappers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^0.5.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In client-browser/package.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanker/client-browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanker/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In client-node/package.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanker/client-node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanker/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When bumping to a new version, we needed to patch the line that contains the “version” key in the top metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/core/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"version": "{current_version}"'&lt;/span&gt;

&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/client-node/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"version": "{current_version}"'&lt;/span&gt;

&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/client-node/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/crypto": "{current_version}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;search&lt;/code&gt; line: we did not want to blindly replace &lt;strong&gt;all&lt;/strong&gt; occurrences of the new version in packages.json — if a line declares a third-party dependency that happens to have the same version number as the current one, it should not get patched!&lt;/p&gt;

&lt;p&gt;Speaking of dependencies, we also needed to patch the line that specifies the version of &lt;code&gt;@tanker/core&lt;/code&gt; used by &lt;code&gt;@tanker/client-browser&lt;/code&gt; and &lt;code&gt;@tanker/client-node&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/client-browser/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/core": "{current_version}"'&lt;/span&gt;

&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/client-node/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/core": "{current_version}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s a total of four blocks of configuration. Then we extracted a &lt;code&gt;@tanker/crypto&lt;/code&gt; package from &lt;code&gt;@tanker/core&lt;/code&gt;, and added two new blocks of configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="c"&gt;# Bump version of @tanker/crypto&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/crypto/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/core": "{current_version}"'&lt;/span&gt;

&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="c"&gt;# Bump version for @tanker/core too — it depends on @tanker/crypto!&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/core/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/crypto": "{current_version}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unbeknownst to us, that was the start of a slippery slope: every time we added a new package in the workspace, we’d have to add two blocks of configuration for this package, &lt;em&gt;and&lt;/em&gt; one for every package that depended on it.&lt;/p&gt;

&lt;p&gt;This is a famous anti-pattern, and before you can say “I see a polynomial complexity!”, we ended up with &lt;a href="https://github.com/TankerHQ/sdk-js/blob/10ca3874b1ef62dd82482e1b69ed868a280aef43/tbump.toml"&gt;this kind of monstrosity&lt;/a&gt;: a 200 hundred lines configuration file!&lt;/p&gt;

&lt;h2&gt;
  
  
  Saved by my co-workers
&lt;/h2&gt;

&lt;p&gt;Luckily I’m working with a team whose members never hesitate to share (constructive) criticism — and who often take matters into their own hands.&lt;/p&gt;

&lt;p&gt;So, when faced with the task of trying to edit this gigantic configuration file just to add a new package, one of them decided to fix the root cause of the problem and submitted a &lt;a href="https://github.com/TankerHQ/tbump/pull/39"&gt;pull request for tbump&lt;/a&gt; named “Stars and Dots”. Here are the two main changes it implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Allow regular expressions in search strings: we could now use &lt;code&gt;@tanker/.*&lt;/code&gt; instead of specifying the whole name of the dependency (&lt;code&gt;@tanker/core&lt;/code&gt;, &lt;code&gt;@tanker/crypto&lt;/code&gt;, and so on)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow using glob patterns to specify file names. Instead of having one block per file, we could now specify a whole bunch of files using &lt;code&gt;packages/*/packages.json&lt;/code&gt; as a &lt;em&gt;glob pattern.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clever name for a clever pull request, don’t you think?&lt;/p&gt;

&lt;p&gt;This pull request was of, course, quickly reviewed and merged by yours truly, and all the nasty blocks in the &lt;code&gt;tbump.toml&lt;/code&gt; file were replaced with just two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/**/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"version": "{current_version}"'&lt;/span&gt;

&lt;span class="nn"&gt;[[file]]&lt;/span&gt;
&lt;span class="py"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"packages/**/package.json"&lt;/span&gt;
&lt;span class="py"&gt;search&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'"@tanker/[^"]+": "{current_version}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that it remains more or less the same to this day, even if the &lt;a href="https://github.com/tankerhq/sdk-js/"&gt;sdk-js git repository&lt;/a&gt; now contains about 30 different packages :)&lt;/p&gt;

&lt;h2&gt;
  
  
  The cherry on the cake
&lt;/h2&gt;

&lt;p&gt;Before I conclude, I’d like to mention a feature of tbump called hooks. It’s the ability to run arbitrary commands before patching the files or after the commit is made and pushed.&lt;/p&gt;

&lt;p&gt;Hooks can run before the files are patched and the commit is made, or after the new commit and new tag have been pushed and are defined in the &lt;code&gt;tbump.toml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;You can find examples of this in &lt;a href="https://github.com/TankerHQ/tbump/blob/master/tbump.toml#L26-L36"&gt;tbump’s own configuration file&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[before_commit]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Check Changelog"&lt;/span&gt;
&lt;span class="py"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"grep -q {new_version} Changelog.rst"&lt;/span&gt;

&lt;span class="nn"&gt;[[after_push]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Publish to pypi"&lt;/span&gt;
&lt;span class="py"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tools/publish.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;tbump is now &lt;em&gt;the&lt;/em&gt; deployment tool for Tanker.&lt;/p&gt;

&lt;p&gt;We often use “before commit” hooks to perform various checks (like verifying that the version to be published is mentioned in the changelog), or to make sure that generated files are up-to-date (like &lt;code&gt;yarn.lock&lt;/code&gt; for instance).&lt;/p&gt;

&lt;p&gt;We also use “after push” hooks as “executable documentation” to specify how to publish new releases (like using &lt;code&gt;poetry publish&lt;/code&gt; in case of a Python package).&lt;/p&gt;

&lt;p&gt;So, what did we learn?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, pay attention to algorithmic complexity, even in configuration files, take care in designing a good UX, even for command-line tools,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Second, if you can afford an extra pair of eyes to a given problem, don’t hesitate to ask.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, there’s a battle-tested rewrite of bumpversion with a pretty good UX and nice features waiting for you on pypi.org. Feel free to &lt;a href="https://github.com/TankerHQ/tbump#installation"&gt;try it out&lt;/a&gt; and tell us what you think.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;p&gt;PS: We’re hiring software engineers. If the way we work sounds interesting, have a look at the hack challenge available on &lt;a href="https://www.tanker.io/rabbit/"&gt;https://www.tanker.io/rabbit/&lt;/a&gt; and don’t hesitate to reach out!&lt;/p&gt;

</description>
      <category>automation</category>
      <category>development</category>
      <category>workflow</category>
      <category>commandline</category>
    </item>
    <item>
      <title>"It's a waste of time!"</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Sun, 31 May 2020 09:01:53 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/it-s-a-waste-of-time-513p</link>
      <guid>https://dev.to/dmerejkowsky/it-s-a-waste-of-time-513p</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dmerej.info/blog/post/its-not-a-waste-of-time/"&gt;my blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I have a confession to make : every time I heard the phrase "It's a waste of time!", I get a little angry.&lt;/p&gt;

&lt;p&gt;I've heard this answer countless times, often after suggestions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Let's write tests before writing production code"&lt;/li&gt;
&lt;li&gt;"Let's write documentation before writing code"&lt;/li&gt;
&lt;li&gt;"Let's refactor before implementing the new feature"&lt;/li&gt;
&lt;li&gt;"Let's discuss the API once more before shipping the next release."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several reasons for why I don't like this answer.&lt;/p&gt;

&lt;p&gt;Why? It's all about knowledge.&lt;/p&gt;

&lt;h1&gt;
  
  
  Time spent vs time saved
&lt;/h1&gt;

&lt;p&gt;First, it's easy to see the time &lt;em&gt;spent&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Yes, we spent one week writing and reviewing the new documentation. Yes, we spent three days refactoring the code and we did not implement any feature. Yes, we had two one-hour meetings before getting consensus on the new API and shipping the next release.&lt;/p&gt;

&lt;p&gt;But it's really hard to see the time &lt;em&gt;saved&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Who knows what horrible bug we would have to fix in production if we skipped writing those tests? Who knows how much breaking changes we would have made because the API was not good enough? Who knows how much time we would have spent implementing the &lt;em&gt;next&lt;/em&gt; big feature if we skipped the refactoring?&lt;/p&gt;

&lt;h1&gt;
  
  
  Learning about the problem
&lt;/h1&gt;

&lt;p&gt;Second, you may have noticed that in all the situations I mentioned above, it's all about gaining more knowledge about the problem &lt;em&gt;before&lt;/em&gt; jumping to the implementation.&lt;/p&gt;

&lt;p&gt;In other terms, by saying "It's a waste of time", you assume you know enough about the problem to start working on the implementation right away.&lt;/p&gt;

&lt;h1&gt;
  
  
  Trying to convince others
&lt;/h1&gt;

&lt;p&gt;Most of the time, I can't convince people who tell me that I'm suggesting is a waste of time.&lt;/p&gt;

&lt;p&gt;Instead, they skip what I think was a critical preparatory step, and then I watch them spend a ton of time rectifying the situation afterwards - it's frustrating.&lt;/p&gt;

&lt;p&gt;Fortunately, after they've repeated the same mistake enough times, they tend to finally listen to me. But I'd very much like to not have to go through this painful process every time.&lt;/p&gt;

&lt;p&gt;I'm not sure what to do about it, though. Maybe it's just human nature?&lt;/p&gt;

&lt;p&gt;To make matters worse sometimes writing the code is actually a good way to gain this precious knowledge in a way that neither tests nor documentation nor brainstorms could have done.&lt;/p&gt;

&lt;h1&gt;
  
  
  Questions for you
&lt;/h1&gt;

&lt;p&gt;What do you think about all this? How do you react when confronted to the "It' a waste of time!" argument?&lt;/p&gt;

&lt;p&gt;Feel free to share your ideas below, and see you next time!&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Tips for better Python tests</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Sat, 25 Apr 2020 14:20:47 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/tips-for-better-python-tests-1ime</link>
      <guid>https://dev.to/dmerejkowsky/tips-for-better-python-tests-1ime</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dmerej.info/blog/post/python-tests-tips/"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, I'd like to share with you a list of small tips to help you write better tests when using Python. Note that the tips often improve both the readability of the test implementation, &lt;em&gt;and&lt;/em&gt; of the failure messages (which is pretty important too).&lt;/p&gt;

&lt;h1&gt;
  
  
  Use pytest
&lt;/h1&gt;

&lt;p&gt;Pytest does a great job in generating very good error messages from your assertions, without the need for anything other than the &lt;code&gt;assert&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Take this assertion for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the failure will look like when using &lt;code&gt;pytest&lt;/code&gt; if the assertion fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def test_foobar():
        x = 2
&amp;gt; assert foo(x) == bar(x)
E assert 4 == 5
E + where 4 = foo(2)
E + and 5 = bar(2)

test.py:11: AssertionError
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how much information you get about what went wrong, like the entire body of the function up to the assertion that failed, or when the values that were compared came from.&lt;/p&gt;

&lt;p&gt;It gets even better - take this other assertion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"this is what I expected"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And note the nice, detailed diff between the two strings that gets printed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def test_get_message():
&amp;gt;         assert get_message() == "this is what I expected"

E AssertionError: assert 'this is what I expected' == 'this is what I got'
E - this is what I expected
E ? ^^^^^ --
E + this is what I got
E ? ^^^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A more complex example
&lt;/h1&gt;

&lt;p&gt;For the rest of this post, let's assume you are testing a &lt;code&gt;sync_folders()&lt;/code&gt; function that can synchronize a remote folder with a local one.&lt;/p&gt;

&lt;p&gt;Here's one of the tests you wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_can_update_local_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;local_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s"&gt;"a.txt"&lt;/span&gt;
    &lt;span class="n"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"old contents"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"new contents"&lt;/span&gt;
    &lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sync_folders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;actual_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;actual_contents&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;new_contents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Quick tip: I usually split my tests into three arrange /act / assert parts and I visualize them using vertical space&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Add context to assertions
&lt;/h1&gt;

&lt;p&gt;Did you know you can add a string at the end of the &lt;code&gt;assert&lt;/code&gt; statement?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;def test_sync(remote, local):
&lt;/span&gt;    ...

    actual_contents = local_file.read_text()
&lt;span class="gd"&gt;-   assert actual_contents == new_contents"
&lt;/span&gt;&lt;span class="gi"&gt;+   assert actual_contents == new_contents, "a.txt should have been updated"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, instead of having to read this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E AssertionError: assert 'old contents\n' == 'new contents'
E - old contents
E + new contents

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you get this message, which contains a clue about where the diff actually comes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E AssertionError: a.txt should have been updated
E assert 'old contents\n' == 'new contents'
E - old contents
E + new contents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Reduce noise
&lt;/h1&gt;

&lt;p&gt;You can also improve the signal over noise ratio by using &lt;code&gt;pytest.fail&lt;/code&gt; instead of the &lt;code&gt;assert&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- assert actual_contents == new_contents", "a.txt should have been updated"
&lt;/span&gt;&lt;span class="gi"&gt;+ if actual_contents != new_contents:
+     pytest.fail("a.txt should have been updated")
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if actual_contents != new_contents:
&amp;gt;        pytest.fail("a.txt should have been updated")
E        Failed: a.txt should have been updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful when using this technique, because it may hinder the debugging of failing tests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Use custom assertion helpers
&lt;/h1&gt;

&lt;p&gt;Finally, don't hesitate to factorize code about assertions, for instance in a &lt;code&gt;test/helpers.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_was_updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_was_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# and so on
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Use docstrings to describe tests scenarios
&lt;/h1&gt;

&lt;p&gt;This is my favorite tip ever: if you are testing something complex, &lt;em&gt;add a human-readable description&lt;/em&gt; of the test inside the docstring.&lt;/p&gt;

&lt;p&gt;Still using our &lt;code&gt;test_can_update_local_file&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_can_update_local_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="s"&gt;""" Scenario:
    * Create a file named a.txt in the local folder with
      "old" contents
    * Add a new version of the `a.txt` file in the remote folder
    * Synchronize the remote and local folders
    * Check that `a.txt` has been updated
    """&lt;/span&gt;
    &lt;span class="p"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two advantages to this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can be helpful to have a human-readable description of what the test is supposed to be doing when reading the implementation of the code - like any docstring&lt;/li&gt;
&lt;li&gt;Since pytest displays the entire block of the function block that caused the assertion to fail, you get a reminder of what the test was about when reading the failure message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Reflections on the last tip
&lt;/h1&gt;

&lt;p&gt;You can stop reading there if you want, but I thought it would be interesting to know how I end up using docstrings in my test code - especially since for a long time, I was convinced that docstrings were useless if the test implementation was clear enough&lt;sup id="fnref1"&gt;1&lt;/sup&gt;!&lt;/p&gt;

&lt;p&gt;What changed my mind? In two words: &lt;strong&gt;code review&lt;/strong&gt;. let me elaborate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting feedback
&lt;/h2&gt;

&lt;p&gt;I've had the chance to get my Python test code reviewed by some teammates who did not know pytest very well but were used to frameworks like Mocha or Cucumber. They help me realize this simple truth: using only function names and implementation (in other words, &lt;em&gt;code&lt;/em&gt;) to express all the subtlety of what the tests are about &lt;em&gt;cannot&lt;/em&gt; be enough - kind of obvious when you say it like that, right?&lt;/p&gt;

&lt;p&gt;But in this case code review can only see you &lt;em&gt;what&lt;/em&gt; needs to be improved, but not always &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So I did what I had to: I took a closer look at those other frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting to know other frameworks
&lt;/h2&gt;

&lt;p&gt;Here's an implementation of our test using &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;syncs a remote file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newContents&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;syncFolders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualContents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;readText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actualContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newContents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's an implementation using &lt;a href="https://cucumber.io"&gt;Cucumber&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="c"&gt;# in synchronization.feature&lt;/span&gt;
&lt;span class="kd"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Synchronization

&lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; file updated remotely
  &lt;span class="nf"&gt;Given &lt;/span&gt;there is a local file 'a.txt' containing &lt;span class="s"&gt;"old_contents"&lt;/span&gt;
  &lt;span class="nf"&gt;Given &lt;/span&gt;there is a remote file 'a.txt' containing &lt;span class="s"&gt;"new_contents"&lt;/span&gt;
  &lt;span class="nf"&gt;When &lt;/span&gt;I synchronize the folders
  &lt;span class="nf"&gt;Then &lt;/span&gt;the local file a.txt contains &lt;span class="s"&gt;"new_contents"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# in synchronization.rb&lt;/span&gt;
&lt;span class="no"&gt;Given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/there is a local file '{word}' containing "{string}"/&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/I synchronize the folder/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;sync_folders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@remote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quite different styles, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a middle ground
&lt;/h2&gt;

&lt;p&gt;And there you have it: I came up with using docstrings with pytest because it was a nice middle ground between those two approaches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With Mocha, you write short descriptions in &lt;code&gt;it()&lt;/code&gt; and &lt;code&gt;describe()&lt;/code&gt; and there's no such thing as a "test name".&lt;/li&gt;
&lt;li&gt;With Cucumber, you write long and detailed text using the English natural languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I prefer the docstring solution to the ones above because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docstrings can be as long as you want (you rarely see descriptions larger than one line in Mocha tests)&lt;/li&gt;
&lt;li&gt;contrary to Cucumber, you are not forced to use any formatting or syntax in your description, and they live right next to the accompanying code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So what did we learn?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being reviewed helps to keep your code readable - but you already knew that, right?&lt;/li&gt;
&lt;li&gt;Exploring new languages and frameworks gives you insights on how to improve your code&lt;/li&gt;
&lt;li&gt;If you are in a team that contains people who use different languages and frameworks than yours and you get them to review your code, it will lead to even better code because you'll be combining the two effects from above!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other terms, consider increasing the diversity of your teams, and don't hesitate to explore new things 😎.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;my contact page&lt;/a&gt; for more ways to get in touch with me.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I also believed good code did not need comments - fortunately, I read  &lt;a href="https://hackaday.com/2019/03/05/good-code-documents-itself-and-other-hilarious-jokes-you-shouldnt-tell-yourself"&gt;this article&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>pyhon</category>
      <category>testing</category>
      <category>pytest</category>
    </item>
    <item>
      <title>symlinks and .so files on linux - what you need to know</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Sat, 18 Apr 2020 14:03:19 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/symlinks-and-so-files-on-linux-what-you-need-to-know-4p95</link>
      <guid>https://dev.to/dmerejkowsky/symlinks-and-so-files-on-linux-what-you-need-to-know-4p95</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dmerej.info/blog/post/symlinks-and-so-files-on-linux/"&gt;my blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The issue
&lt;/h1&gt;

&lt;p&gt;I've seen this happen countless times.&lt;/p&gt;

&lt;p&gt;First, a Linux user asks for help about an error looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error when running `bar`: `libfoo.so.5: no such file or directory`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, someone who's had the same issue suggests creating a symlink from &lt;code&gt;libfoo.so.6&lt;/code&gt; to &lt;code&gt;libfoo.so.5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And then I come across the dialog some time later and I start crying.&lt;/p&gt;

&lt;p&gt;Why do I cry - it looks like the problem is solved, right?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;bar&lt;/code&gt; program seems to run just fine;&lt;/li&gt;
&lt;li&gt;the change was made with just one command (&lt;code&gt;sudo ln&lt;/code&gt;) and can be undone easily (just remove the symlink);&lt;/li&gt;
&lt;li&gt;there was already a symlink from &lt;code&gt;libfoo.so&lt;/code&gt; to &lt;code&gt;libfoo.so.6&lt;/code&gt; anyway!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, despite the appearances, the problem is &lt;em&gt;not&lt;/em&gt; solved, and doing this is a &lt;em&gt;terrible idea&lt;/em&gt; in general - like planting a ticking time bomb in the street you live or shooting yourself in the foot because you've got a plantar wart.&lt;/p&gt;

&lt;p&gt;But to understand why we need to talk about the language C, shared libraries, soname bumps, Linux distributions, and package management.&lt;/p&gt;

&lt;p&gt;And because I love telling stories, &lt;em&gt;you&lt;/em&gt;, dear reader, will be the heroin this one.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solving the Ultimate Question
&lt;/h1&gt;

&lt;p&gt;Let's assume a group of people you don't really know (let's called them_The Experts_), wrote a piece of C code than can get the answer to the Ultimate Question of Life, the Universe, and Everything.&lt;/p&gt;

&lt;p&gt;For obvious reasons, they want to keep the source code private, so here'swhat they did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They wrote a &lt;em&gt;header file&lt;/em&gt; containing the &lt;code&gt;get_answer&lt;/code&gt; declaration named &lt;code&gt;answer.h&lt;/code&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in answer.h&lt;/span&gt;
&lt;span class="cp"&gt;#pragma once
#ifdef __cplusplus
&lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef __cplusplus
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;They created a &lt;em&gt;shared library&lt;/em&gt; called &lt;code&gt;libanswer.so&lt;/code&gt; from their source file (&lt;code&gt;answer.c&lt;/code&gt;in this case):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcc &lt;span class="nt"&gt;-shared&lt;/span&gt; libanswer.so answer.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way, everyone who needs to get the answer to the Ultimate Question of Life, the Universe, and Everything can buy the &lt;code&gt;libanswer.so&lt;/code&gt; compiled library and the &lt;code&gt;answer.h&lt;/code&gt; header and call the &lt;code&gt;get_answer()&lt;/code&gt; function - let's see how.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using the library from The Experts
&lt;/h1&gt;

&lt;p&gt;You've bought the &lt;code&gt;libanswer.so&lt;/code&gt; and &lt;code&gt;answer.h&lt;/code&gt; files from The Experts and have put them next to a file you've wrote named &lt;code&gt;print-answer.c&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in print-answer.c&lt;/span&gt;
&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;answer.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The answer is %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You were told that &lt;code&gt;gcc&lt;/code&gt; can compile C code and &lt;em&gt;link against&lt;/em&gt; shared libraries if you put them on the command line, so you try and run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcc libanswer.so print-answer.c &lt;span class="nt"&gt;-o&lt;/span&gt; print-answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it does not work, and you get the following error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print-answer.c:2:10: fatal error: answer.h: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a minute - the &lt;code&gt;answer.h&lt;/code&gt; file is &lt;em&gt;right there&lt;/em&gt; - what does that mean, “No such file or directory”?.&lt;/p&gt;

&lt;p&gt;After a bit of research, you discover that &lt;code&gt;gcc&lt;/code&gt; uses a list of paths called the “include path” where it looks for headers. You check on your machine, and sure enough, &lt;code&gt;/usr/include/&lt;/code&gt; is one of the elements of this list, and &lt;code&gt;stdio.h&lt;/code&gt;is in &lt;code&gt;/usr/include/stdio.h&lt;/code&gt;, which explains why &lt;code&gt;gcc&lt;/code&gt; did not complain about the first include.&lt;/p&gt;

&lt;p&gt;To fix the compilation error, you add the &lt;code&gt;-I .&lt;/code&gt; option to the command line so that current directory is added to the list of include paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcc &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; libanswer.so print-answer.c &lt;span class="nt"&gt;-o&lt;/span&gt; print-answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then it compiles.&lt;/p&gt;

&lt;p&gt;Now you try and run the &lt;code&gt;print-answer&lt;/code&gt; program, but you get a new error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./print-answer
 error while loading shared libraries: libanswer.so
 cannot open shared object file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's the same error message as in the introduction and &lt;strong&gt;the operating system is lying to us&lt;/strong&gt;. The file &lt;code&gt;libanswer.so&lt;/code&gt; is right there! What's happening there?&lt;/p&gt;

&lt;p&gt;After more investigation, you figure it out: when you compiled the &lt;code&gt;print-answer&lt;/code&gt; executable, there was a small piece of binary inside it that recorded the &lt;em&gt;name&lt;/em&gt; of the &lt;code&gt;.so&lt;/code&gt; file it was linked against. You can check it by running the &lt;code&gt;readelf&lt;/code&gt; command and display the &lt;em&gt;dynamic section&lt;/em&gt; of your program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ readelf -d ./print-answer
Dynamic section at offset 0x2de8 contains 27 entries:
  Tag Type Name/Value
 0x0000000000000001 (NEEDED) Shared library: [libanswer.so]
 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a way, the &lt;code&gt;print-answer&lt;/code&gt; program “knows” that it &lt;em&gt;needs&lt;/em&gt; &lt;code&gt;libanswer.so&lt;/code&gt; to run. Crucially, it does not know nor cares about where &lt;code&gt;libanswer.so&lt;/code&gt; really &lt;em&gt;is&lt;/em&gt;.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Then, when you run &lt;code&gt;./print-answer&lt;/code&gt;, the operating system sees the name of the shared library in the dynamic section and tries to locate it. Like &lt;code&gt;gcc&lt;/code&gt;, it finds &lt;code&gt;libc.so.6&lt;/code&gt; by itself (in&lt;code&gt;/usr/lib/libc.so.6&lt;/code&gt; for instance) - but it's unable to find the &lt;code&gt;libanswer.so&lt;/code&gt; shared library in the current directory.&lt;/p&gt;

&lt;p&gt;Fortunately, you can fix that by using a special environment variable called &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ LD_LIBRARY_PATH=. ./print-answer
The answer is 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the operating system looks for &lt;code&gt;libanswer.so&lt;/code&gt; in the current directory, finds it, and when required, invokes the code for the &lt;code&gt;get_answer()&lt;/code&gt; function from the shared library.&lt;/p&gt;

&lt;p&gt;That gets you thinking - you did not have to do any of this for &lt;code&gt;print-answer&lt;/code&gt; to find the&lt;code&gt;libc.so&lt;/code&gt; library and the &lt;code&gt;stdio.h&lt;/code&gt; header.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if it was possible to compile &lt;code&gt;libanswer.so&lt;/code&gt; once and for all?&lt;/li&gt;
&lt;li&gt;And what if there was a way to compile the &lt;code&gt;print-answer.c&lt;/code&gt; source file without having to copy/paste the header and the shared library, and remember all the various &lt;code&gt;gcc&lt;/code&gt; options?&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Becoming a packager
&lt;/h1&gt;

&lt;p&gt;Let's assume The Experts realized that their business model was not going to work and decided to publish their source files for free instead. Yay open source!&lt;/p&gt;

&lt;p&gt;Here's the contents of their &lt;code&gt;answer.c&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include &amp;lt;answer.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Disappointing, I know&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;snprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you are an Arch Linux user, you decide to lookup documentation about the &lt;code&gt;pacman&lt;/code&gt; package managerand how generate and publish Arch Linux packages&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;After a while, you manage to write a working &lt;code&gt;PGKBUILD&lt;/code&gt; for &lt;code&gt;libanswer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;pkgname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libanswer
&lt;span class="nv"&gt;pkgver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0
&lt;span class="nv"&gt;pkgrel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;pkgdesc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Answer the Ultimate Question"&lt;/span&gt;
&lt;span class="nb"&gt;arch&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s1"&gt;'x86_64'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
...

build&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  gcc &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-shared&lt;/span&gt; answer.c &lt;span class="nt"&gt;-o&lt;/span&gt; libanswer.so
&lt;span class="o"&gt;}&lt;/span&gt;

package &lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$pkgdir&lt;/span&gt;/usr/&lt;span class="o"&gt;{&lt;/span&gt;include,lib&lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;install &lt;/span&gt;answer.h &lt;span class="nv"&gt;$pkgdir&lt;/span&gt;/usr/include/
  &lt;span class="nb"&gt;install &lt;/span&gt;libanswer.so &lt;span class="nv"&gt;$pkgdir&lt;/span&gt;/usr/lib
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you build the package with &lt;code&gt;makepkg&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ makepkg
==&amp;gt; Making package: libanswer 1.0-1
...
==&amp;gt; Finished making: libanswer 1.0-1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything goes well, and you now have a file named &lt;code&gt;libanswer-1.0-1-x86_64.pkg.tar.xz&lt;/code&gt;next to your &lt;code&gt;PGKBUILD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You install the package with &lt;code&gt;pacman&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo pacman -U libanswer-1.0-1-x86_64.pkg.tar.xz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you find out that you can compile and run &lt;code&gt;print-answer.c&lt;/code&gt; from anywhere in the system using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcc print-answer.c -lanswer -o ./print-answer
$ ./print-answer
The answer is 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time you need only the &lt;em&gt;name&lt;/em&gt; of the library (the part without the&lt;code&gt;lib&lt;/code&gt; prefix and the &lt;code&gt;.so&lt;/code&gt; suffix) after the &lt;code&gt;-l&lt;/code&gt; option. You do &lt;em&gt;not&lt;/em&gt; have to worry about include paths or use the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; environment variable - neat!&lt;/p&gt;

&lt;p&gt;What you've accomplished is called &lt;em&gt;packaging the &lt;code&gt;answer&lt;/code&gt; library&lt;/em&gt;, and its what &lt;em&gt;package maintainers&lt;/em&gt; do - well done.&lt;/p&gt;

&lt;p&gt;Impressed by your packaging skills&lt;sup id="fnref4"&gt;4&lt;/sup&gt;, the Arch Linux maintainers allow you to publish your package in official repositories, which means everyone using Arch Linux is now able to install the &lt;code&gt;libanswer&lt;/code&gt; package in just one command!&lt;/p&gt;

&lt;h1&gt;
  
  
  A simple change
&lt;/h1&gt;

&lt;p&gt;A few days later, The Experts release a new version of their library (1.1), containing a nice optimization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strdup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you are the maintainer of the &lt;code&gt;libanswer&lt;/code&gt; package, you quickly update the PKGBUILD and publish a new release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-pkgver=1.0
&lt;/span&gt;&lt;span class="gi"&gt;+pkgver=1.1
&lt;/span&gt; pkgrel=1
 pkgdesc="Answers the Ultimate Question"
 arch=('x86_64')
 source=(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You build and install the package on your machine and check it still works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ makepg
$ sudo pacman -U libanswer-1.1-1-x86_64.pkg.tar.xz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you publish the new version of the &lt;code&gt;libanswer&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;That's where Linux distributions really shine (and the whole reason we use shared libraries in the first place). Once the new package is published, &lt;em&gt;any&lt;/em&gt; Arch Linux user who installs it will get the latest version of &lt;code&gt;libanswer.so&lt;/code&gt; in their system, and all the programs that were linked against it will use the latest version - this is especially important if the new version contains a security bug fix for instance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Another change
&lt;/h1&gt;

&lt;p&gt;A week later, The Experts realize they don't really need to return a string from the &lt;code&gt;get_answer()&lt;/code&gt; function, and that a simple &lt;code&gt;int&lt;/code&gt; would suffice.&lt;/p&gt;

&lt;p&gt;So they modify both their header and source files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- char* get_answer();
&lt;/span&gt;&lt;span class="gi"&gt;+ int get_answer();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include &amp;lt;answer.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And they publish a release note:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# The answer project&lt;/span&gt;

&lt;span class="gu"&gt;## Version 2.0&lt;/span&gt;
&lt;span class="p"&gt;
*&lt;/span&gt; Change the return type of the &lt;span class="sb"&gt;`get_answer()`&lt;/span&gt; function from &lt;span class="sb"&gt;`char*`&lt;/span&gt; to &lt;span class="sb"&gt;`int`&lt;/span&gt;.

&lt;span class="gu"&gt;## Version 1.1&lt;/span&gt;
&lt;span class="p"&gt;
*&lt;/span&gt; This release implements performance optimizations.

&lt;span class="gu"&gt;## Version 1.0&lt;/span&gt;
&lt;span class="p"&gt;
*&lt;/span&gt; First public release!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon hearing the good news, you download the latest sources of the project, and you modify your &lt;code&gt;print-answer.c&lt;/code&gt; source file to use the latest version of the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;answer.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The answer is %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You compile everything and check the code still works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcc -I . -shared answer.c -o libanswer.so
$ gcc -I . libanswer.so print-answer.c -o ./print-answer
$ LD_LIBRARY_PATH=. ./print-answer
The answer is 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to publish the v2!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-pkgver=1.0
&lt;/span&gt;&lt;span class="gi"&gt;+pkgver=2.0
&lt;/span&gt; pkgrel=1
 pkgdesc="Answers the Ultimate Question"
 arch=('x86_64')
 source=(...)


&lt;span class="err"&gt;$&lt;/span&gt; makepg
&lt;span class="err"&gt;$&lt;/span&gt; sudo pacman -U libanswer-2.0-1-x86_64.pkg.tar.xz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy as pie - satisfied, you publish the new package and go to bed.&lt;/p&gt;

&lt;h1&gt;
  
  
  When shit hits the fan
&lt;/h1&gt;

&lt;p&gt;The next morning you receive the following e-mail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subject : latest libanswer package update broke the display-answer-pp program

Hello,

I'm using `display-answer-pp` version 0.4, and when I updated
`libanswer` to the version 2.0, I got the following error:

./display-answer-pp
zsh: segmentation fault (core dumped) ./display-answer-pp

Downgrading the `libanswer` package fixes the problem. Please advise.

Signed: Bob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Welcome to the joys of packaging!&lt;/p&gt;

&lt;p&gt;You've never heard of the &lt;code&gt;display-answer-pp&lt;/code&gt; package - what is going on?&lt;/p&gt;

&lt;p&gt;After a bit of research, you find out that someone wrote a &lt;code&gt;display-answer-pp&lt;/code&gt; program using your &lt;code&gt;libanswer&lt;/code&gt; package and published it on the official Arch Linux repositories a few days ago.&lt;/p&gt;

&lt;p&gt;Here's what the code for &lt;code&gt;display-answer-pp&lt;/code&gt; looks like - it's a single &lt;code&gt;C++&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include &amp;lt;answer.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_answer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"The answer is: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what happened?&lt;/p&gt;

&lt;p&gt;Well, the problem is that you forgot to coordinate with your fellow packagers!&lt;/p&gt;

&lt;p&gt;You see, when &lt;code&gt;display-answer-pp&lt;/code&gt; was being packaged, the &lt;code&gt;get_answer()&lt;/code&gt; function was returning a &lt;code&gt;char*&lt;/code&gt;. When you published &lt;code&gt;answer&lt;/code&gt; version 2.0, the code for the function &lt;code&gt;get_answer()&lt;/code&gt; started returning an&lt;code&gt;int&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;So when Bob updated &lt;code&gt;libanswer&lt;/code&gt; after having installed &lt;code&gt;display-answer-pp&lt;/code&gt;, and tried to re-run the program, all hell break loose, because the compiled C++ code expected a  &lt;em&gt;pointer&lt;/em&gt; to a string and got an &lt;em&gt;int&lt;/em&gt; instead.&lt;/p&gt;

&lt;p&gt;In other terms, the &lt;em&gt;application binary interface&lt;/em&gt; (or ABI for short) of the &lt;code&gt;libanswer&lt;/code&gt; library broke.&lt;/p&gt;

&lt;h1&gt;
  
  
  You break it, you fix it
&lt;/h1&gt;

&lt;p&gt;Unfortunately, there's only one way to fix an ABI breakage: you need to &lt;em&gt;recompile&lt;/em&gt; everything that was linked against the old version of the library.&lt;/p&gt;

&lt;p&gt;That's one of the main issues package maintainers have to solve. They need two very different features when it comes to libraries updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the new version of the library is ABI-compatible with the previous one, end-users should be able to get it by simply updating &lt;em&gt;the one package&lt;/em&gt; that contains it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not, they need to make sure &lt;em&gt;none of the programs that depend on the library&lt;/em&gt; break when the update is made.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Arch Way
&lt;/h2&gt;

&lt;p&gt;Here's how Arch maintainers solve this problem. In our example, they would have published &lt;code&gt;libanswer&lt;/code&gt; v2 in a special repository named “staging”. Then, they would have rebuild every package that depends on &lt;code&gt;libanswer&lt;/code&gt; (so both &lt;code&gt;print-answer&lt;/code&gt; and &lt;code&gt;display-answer-pp&lt;/code&gt;) and pushed those to the staging repository.&lt;/p&gt;

&lt;p&gt;Finally, after a period of testing, they would have moved &lt;code&gt;libanswer&lt;/code&gt;, &lt;code&gt;print-answer&lt;/code&gt; and &lt;code&gt;display-answer-pp&lt;/code&gt; in the official repositories in one swift update. They would have used a &lt;em&gt;to do list&lt;/em&gt; like &lt;a href="https://www.archlinux.org/todo/hdf5-1120-release/"&gt;this one&lt;/a&gt; to coordinate the packaging tasks.&lt;/p&gt;

&lt;p&gt;This means that if you try and update &lt;code&gt;libanswer&lt;/code&gt; without upgrading &lt;em&gt;every other package&lt;/em&gt; that depends on it, you risk breaking your installation - and that's why partial updates are not supported on Arch Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debian Way
&lt;/h2&gt;

&lt;p&gt;Debian maintainers use another strategy. When they package a library, they include its version number in the name of the package. What's more, they have a separate &lt;em&gt;development&lt;/em&gt; package that contains the files required for compiling programs that use the library. They also use a compilation trick called the &lt;code&gt;soname&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Let's see how this works.&lt;/p&gt;

&lt;p&gt;First, they tell &lt;code&gt;gcc&lt;/code&gt; to use the correct soname option when linking the library&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcc -I . answer.c -shared -Wl,-soname=libanswer.so.1 -o libanswer.so.1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They use the outcome of the build to generate two packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;libanswer-dev&lt;/code&gt;, that contains a symlink &lt;code&gt;libanswer.so -&amp;gt; libanswer.so.1&lt;/code&gt; and the &lt;code&gt;answer.h&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;libanswer1&lt;/code&gt;, that contains &lt;strong&gt;only&lt;/strong&gt; the &lt;code&gt;libanswer.so.1&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, they build &lt;code&gt;display-answer-pp&lt;/code&gt; for the first time, using the &lt;code&gt;libanswer-dev&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -lanswer display-answer.cpp -o ./display-answer-pp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;libanswer.so&lt;/code&gt; was built with the &lt;code&gt;-soname=libanswer.so.1&lt;/code&gt; option, the &lt;code&gt;display-answer-pp&lt;/code&gt; binary now &lt;em&gt;knows&lt;/em&gt; that it needs &lt;code&gt;libanswer.so.1&lt;/code&gt; at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ readelf -d display-answer-pp
Tag Type Name/value
0x0000000000000001 (NEEDED) Shared library: [libanswer.so.1]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the v2 version of &lt;code&gt;libanswer&lt;/code&gt; is published by The Experts, they rebuild the shared library with an appropriate soname:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcc -I . answer.c -shared -Wl,-soname=libanswer.so.2 -o libanswer.so.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They use the outcome of the build to &lt;em&gt;update&lt;/em&gt; the &lt;code&gt;libanswer-dev&lt;/code&gt; package and to create a &lt;em&gt;brand new&lt;/em&gt; &lt;code&gt;libanswer2&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;At this point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;libanswer-dev&lt;/code&gt; contains a symlink &lt;code&gt;libanswer.so -&amp;gt; libanswer.so.2&lt;/code&gt; and the updated &lt;code&gt;answer.h&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;libanswer2&lt;/code&gt; contains only the &lt;code&gt;libanswer.so.2&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, they build &lt;code&gt;display-answer-pp&lt;/code&gt; for the second time, using the updated &lt;code&gt;libanswer-dev&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ g++ -lanswer display-answer.cpp -o ./display-answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, &lt;code&gt;display-answer-pp&lt;/code&gt; knows that it needs &lt;code&gt;libanswer.so.2&lt;/code&gt; at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ readelf -d display-answer-pp
Tag Type Name/value
0x0000000000000001 (NEEDED) Shared library: [libanswer.so.2]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's sum up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both &lt;code&gt;libanswer1&lt;/code&gt; and &lt;code&gt;libanswer2&lt;/code&gt; packages can co-exist in the same system since they contain different filenames&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;libanswer-dev&lt;/code&gt; package does not need to be installed for&lt;code&gt;display-answer-pp&lt;/code&gt; to run since the program contain the versioned soname in its dynamic section&lt;/li&gt;
&lt;li&gt;If you want to rebuild a program when a new version of the library is out, you just install the latest &lt;code&gt;libanswer-dev&lt;/code&gt; package - the command used for compilation does not have to change at all!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clever, right?&lt;/p&gt;

&lt;p&gt;Because of this technique, the update from &lt;code&gt;libanswer&lt;/code&gt; v1 to &lt;code&gt;libanswer&lt;/code&gt;v2 is often called &lt;em&gt;a soname bump&lt;/em&gt; - the update from &lt;code&gt;v1&lt;/code&gt; to &lt;code&gt;v1.1&lt;/code&gt; is just a regular update.&lt;/p&gt;

&lt;p&gt;Quite often, a backward incompatible update correspond to the first digit of the soname to be updated. &lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Now you know - different versions of a library have various sonames, and the symlinks are carefully crafted by distribution maintainers.&lt;/p&gt;

&lt;p&gt;So, when you create a symlink yourself, you are taking a huge risk, especially when creating links between libraries that have a different leading digit in their soname!&lt;/p&gt;

&lt;p&gt;As we saw, using the incorrect library at runtime can cause crashes, and if you break an essential binary (like &lt;code&gt;bash&lt;/code&gt; for instance), you may no longer be able to log in, which means your only choice may be to re-install your whole system from scratch. This is not theoretical by the way: it happened to me &lt;em&gt;years ago&lt;/em&gt;, and I still remember it to this day.&lt;/p&gt;

&lt;p&gt;So, what to do if you get this error?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error when running `bar`: `libfoo.so.5: no such file or directory`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;bar&lt;/code&gt; comes from a package on your distribution, update all packages and then open a bug report if the problem persists&lt;/li&gt;
&lt;li&gt;If not, try and re-compile &lt;code&gt;bar&lt;/code&gt; - possibly after updating the &lt;code&gt;libfoo-dev&lt;/code&gt; package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But &lt;em&gt;please&lt;/em&gt;, &lt;em&gt;please&lt;/em&gt; do not create a symlink from &lt;code&gt;libfoo.so.6&lt;/code&gt; to&lt;code&gt;libfoo.so.5&lt;/code&gt; or suggest this solution to someone else. Together, let'sput an end to this bad, bad, advice. Thank you!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'd love to hear what you have to say, so please feel free to leave a comment below, or check out &lt;a href="https://dmerej.info/blog/pages/contact/"&gt;my contact page&lt;/a&gt; for more ways to get in touch with me.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If you're puzzled by the &lt;code&gt;__cplusplus&lt;/code&gt; stuff don't worry - it's just so that the answer library is usable from C++ too. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;By the way, it needs &lt;code&gt;libc.so&lt;/code&gt; because the compiled code for &lt;code&gt;printf&lt;/code&gt; lives there. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;You're using Arch Linux because you're cool and you want to learn - good for you! ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Reminder: it's a story I made up! ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Actually, this is often done automatically by whatever build system the library is using ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Sadly it's not always the case: &lt;code&gt;lua&lt;/code&gt; and &lt;code&gt;libpng&lt;/code&gt; are notable exceptions. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>linux</category>
      <category>packaging</category>
    </item>
    <item>
      <title>Taking Mastodon's security to the next level - part 2: Exchange encrypted messages</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Tue, 19 Nov 2019 10:36:16 +0000</pubDate>
      <link>https://dev.to/tanker/taking-mastodon-s-security-to-the-next-level-part-2-exchange-encrypted-messages-o4c</link>
      <guid>https://dev.to/tanker/taking-mastodon-s-security-to-the-next-level-part-2-exchange-encrypted-messages-o4c</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;This is the second article in a 2-part series of blog posts that describe our endeavor to add end-to-end encryption to Mastodon: if you haven’t already, please read &lt;a href="https://dev.to/tanker/taking-mastodon-security-to-the-next-level-part-1-encrypt-your-toots-2p00"&gt;Part 1: Encrypt your toots&lt;/a&gt; first.&lt;br&gt;
In the rest of this article, we’ll refer to the Javascript code responsible for managing the UI as the &lt;em&gt;client&lt;/em&gt;, and the Ruby on Rails code as the &lt;em&gt;server&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We left on a bit of a cliffhanger - we’d managed to encrypt direct messages in the client, but hadn’t yet sent them to the server. &lt;/p&gt;

&lt;p&gt;Actually, sending encrypted messages to the server instead of plain text messages will lead to all sorts of interesting challenges and we’ll learn even more about Mastodon’s internals than we did in the first post.&lt;/p&gt;
&lt;h1&gt;
  
  
  Adding an encrypted field in the database
&lt;/h1&gt;

&lt;p&gt;Since we are encrypting only direct messages, it seems like a good idea to add an &lt;code&gt;encrypted&lt;/code&gt; boolean in the database. That way, we’ll know whether statuses are encrypted or not before attempting to decrypt them.&lt;/p&gt;

&lt;p&gt;So here’s the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client should send an encrypted boolean to the server when calling the &lt;code&gt;api/v1/statuses&lt;/code&gt; route during the composition of direct messages&lt;/li&gt;
&lt;li&gt;The server should store the encrypted status contents in the database, along with an &lt;code&gt;encrypted&lt;/code&gt; boolean&lt;/li&gt;
&lt;li&gt;The server should send the encrypted text along with the &lt;code&gt;encrypted&lt;/code&gt; boolean back to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s write a new migration and migrate the db:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# db/migrate/20190913090225_add_encrypted_to_statuses.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddEncryptedToStatuses&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
      &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:statuses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bool&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rails db:setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fix the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/api/v1/statuses_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Api::V1::StatusesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PostStatusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;# ...&lt;/span&gt;
                &lt;span class="ss"&gt;encrypted: &lt;/span&gt;&lt;span class="n"&gt;status_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;status_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="c1"&gt;# ...&lt;/span&gt;
       &lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the controller deals only with validating the JSON request; the actual work of saving the statuses in the database is done by a service instead, so we need to patch this class as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/post_status_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostStatusService&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseService&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="vi"&gt;@encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="c1"&gt;# …&lt;/span&gt;
    &lt;span class="n"&gt;process_status!&lt;/span&gt;


  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_status!&lt;/span&gt;
      &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;status_attributes&lt;/span&gt;
    &lt;span class="c1"&gt;# Map attributes to a list of kwargs suitable for create!&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;# …&lt;/span&gt;
       &lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="vi"&gt;@encrypted&lt;/span&gt;
   &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s write a test to make sure the &lt;code&gt;PostStatus&lt;/code&gt; service properly persists encrypted messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# spec/services/post_status_service_spec.rb&lt;/span&gt;
&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'can create a new encrypted status'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fabricate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test status update"&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;encrypted: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_persisted&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_truthy&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, it passes!&lt;/p&gt;

&lt;p&gt;We can now use the new PostStatus API from the client code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/mastodon/actions/compose.js&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submitCompose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;shouldEncrypt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getIn&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;compose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shouldEncrypt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getIn&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;compose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldEncrypt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tankerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/statuses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shouldEncrypt&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can check that this works by composing a direct message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xDV2I6En--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vj87tzvvwm61r16ycfbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xDV2I6En--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vj87tzvvwm61r16ycfbz.png" alt="Alice sends a direct message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then checking in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db
# select encrypted, text from statuses order by id desc;
encrypted | text
----------+---------------------------------
 t        | A4qYtb2RBWs4vTvF8Z4fpEYy402IvfMZQqBckhOaC7DLHzw…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like it’s working as expected, so it’s time to go the other way around - sending the encrypted boolean from the server to the client.&lt;/p&gt;

&lt;h1&gt;
  
  
  Displaying encrypted messages in the UI
&lt;/h1&gt;

&lt;p&gt;This time we need to change the status serializer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/serializers/rest/status_serializer.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;REST::StatusSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:in_reply_to_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:in_reply_to_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="c1"&gt;# ...&lt;/span&gt;
             &lt;span class="ss"&gt;:encrypted&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Javascript code that fetches the status from the Rails API does not have to change.&lt;/p&gt;

&lt;p&gt;That being said, we still want to make it clear in the UI whether the message is encrypted or not - this is useful for debugging.&lt;/p&gt;

&lt;p&gt;So let’s update the &lt;code&gt;StatusContent&lt;/code&gt; component to display a padlock icon next to any encrypted message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/mastodon/components/status_content.js&lt;/span&gt;
&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;encrypted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;contentHtml&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;contentHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;i class="fa fa-lock" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
      &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contentHtml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;contentHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contentHtml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentHtml&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hPmsEFZ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6txwxrnfwvsp8drivhr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hPmsEFZ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6txwxrnfwvsp8drivhr1.png" alt="Encrypted message with padlock"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hooray, it works! We’re ready to call &lt;code&gt;decrypt&lt;/code&gt; now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Decrypt messages
&lt;/h1&gt;

&lt;p&gt;First things first, let’s patch the &lt;code&gt;TankerService&lt;/code&gt; to deal with decryption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/mastodon/tanker/index.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TankerService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;decrypt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lazyStart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fromBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tanker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clearText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’re faced with a choice. There are indeed several ways to decrypt statuses in the client code. For simplicity’s sake, we’ll patch the &lt;code&gt;processStatus&lt;/code&gt; function which is called for each message returned from the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/mastodon/actions/importer/index.js&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;processStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// `content` as returned by the server has a &amp;lt;p&amp;gt; around it, so&lt;/span&gt;
    &lt;span class="c1"&gt;// clean that first&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encryptedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tankerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;clearText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;
    &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateStatusContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clearText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clearHtml&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we call an &lt;code&gt;udpateStatusContent&lt;/code&gt; action to update the status after it has been decrypted.&lt;/p&gt;

&lt;p&gt;I won’t go through the implementation of the &lt;code&gt;updateStatusContent&lt;/code&gt; action and reducers as they’re pretty standard. &lt;/p&gt;

&lt;p&gt;Anyway, we can check that our patch works by logging in as Alice, and then sending a message to ourselves:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGOwSm1v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hv5crikyilsfoyy92btu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGOwSm1v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hv5crikyilsfoyy92btu.png" alt="Alice sees her own encrypted direct message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Exchanging private messages
&lt;/h1&gt;

&lt;p&gt;Being able to send encrypted messages to oneself is quite impressive, but I don’t think we should stop there :)&lt;/p&gt;

&lt;p&gt;Let’s create a new account for Bob, and look at what happens when Alice sends a message containing &lt;code&gt;@bob&lt;/code&gt; - this is known as a &lt;em&gt;mention&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KZsi0STK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/spwo5debkgbevjilehh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KZsi0STK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/spwo5debkgbevjilehh0.png" alt="Alice to Bob"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Normally, Bob should get a notification because he was sent a direct message, but this is not the case.&lt;/p&gt;

&lt;p&gt;Clearly there is something to fix there.&lt;/p&gt;

&lt;p&gt;After digging into the code, here's what I found out: notifications about direct messages are generated by a class named &lt;code&gt;ProcessMentionsService&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Here’s the relevant part of the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessMentionsService&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MENTION_RE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
         &lt;span class="n"&gt;mentionned_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
         &lt;span class="c1"&gt;# …&lt;/span&gt;
         &lt;span class="n"&gt;mentions&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class="p"&gt;\\&lt;/span&gt;
           &lt;span class="n"&gt;mentionned_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mentions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;

       &lt;span class="n"&gt;mentions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the server looks for &lt;code&gt;@&lt;/code&gt; mentions in the status text using regular expression matches and then builds a list of Mention instances.&lt;/p&gt;

&lt;p&gt;Then something interesting happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/process_mentions_services.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessMentionsService&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseService&lt;/span&gt;
   &lt;span class="c1"&gt;# …&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mentioned_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mentioned_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;local?&lt;/span&gt;
      &lt;span class="no"&gt;LocalNotificationWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;mentioned_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;mentioned_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;activitypub?&lt;/span&gt;
       &lt;span class="no"&gt;ActivityPub&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DeliveryWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="n"&gt;activitypub_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="n"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="n"&gt;mentioned_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inbox_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the server triggers a task from the &lt;code&gt;LocalNotificationWorker&lt;/code&gt; if the mentioned account is local to the instance. It turns out this will later use the websocket server we discovered in Part 1 to send a notification to the client.&lt;/p&gt;

&lt;p&gt;Side note here: if the mentioned account is &lt;em&gt;not&lt;/em&gt; local to the instance, an Activity Pub delivery worker is involved. This is at the heart of the Mastodon mechanism: each instance can either send messages across local users, or they can use the ActivityPub protocol to send notifications across to another instance.&lt;/p&gt;

&lt;p&gt;Back to the task at hand: it’s clear now that if the status is encrypted by the time it’s processed by the server, nothing will match and no notification will be created. That’s why Bob didn’t get any notification when we tried sending a direct message from Alice to Bob earlier.&lt;/p&gt;

&lt;p&gt;Thus we need to process the &lt;code&gt;@&lt;/code&gt; mentions client-side, then send a list of mentions next to the encrypted status to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//app/javascript/mastodon/actions/compose.js&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submitCompose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mentionsSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldEncrypt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Parse mentions from the status&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;(\S&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// We want the first group, without the leading '@'&lt;/span&gt;
      &lt;span class="nx"&gt;mentionsSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mentions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mentionsSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/statuses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;mentions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we did for the &lt;code&gt;encrypted&lt;/code&gt; boolean, we have to allow the &lt;code&gt;mentions&lt;/code&gt; key in the statuses controller and forward the &lt;code&gt;mentions&lt;/code&gt; array to the &lt;code&gt;PostStatus&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Api::v1::StatusesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BaseController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;status_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
      &lt;span class="ss"&gt;:encypted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;mentions: &lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PostStatusService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                                         
                &lt;span class="ss"&gt;encrypted: &lt;/span&gt;&lt;span class="n"&gt;status_param&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:encrypted&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="ss"&gt;mentions: &lt;/span&gt;&lt;span class="n"&gt;status_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:mentions&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;PostStatus&lt;/code&gt; service we forward the mentions to the &lt;code&gt;ProcessMentions&lt;/code&gt; service using a &lt;code&gt;username&lt;/code&gt; key in an option hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/post_status_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostStatusService&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_status!&lt;/span&gt;
    &lt;span class="n"&gt;process_mentions_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;usernames: &lt;/span&gt;&lt;span class="vi"&gt;@mentions&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, finally, in the &lt;code&gt;ProcessMentions&lt;/code&gt; service, we convert usernames into real accounts and create the appropriate mentions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/services/process_mentions_service.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessMentionsService&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypted?&lt;/span&gt;
      &lt;span class="n"&gt;usernames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:usernames&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="n"&gt;usernames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mentions&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Mention&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="vi"&gt;@status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;else&lt;/span&gt;
     &lt;span class="c1"&gt;# same code as before&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can try encrypting the following status: &lt;code&gt;@bob I have a secret message for you&lt;/code&gt; and check that Bob gets the notification.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--42tQlJsD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/es5ki83vcjivadekopyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--42tQlJsD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/es5ki83vcjivadekopyh.png" alt="Notification for Bob"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when Bob tries to decrypt Alice’s message, it fails with a &lt;code&gt;resource ID not found&lt;/code&gt; error message: this is because Alice never told &lt;em&gt;Tanker&lt;/em&gt; that Bob had access to the encrypted message. &lt;/p&gt;

&lt;p&gt;For Bob to see a message encrypted by Alice, Alice must provide Bob’s public identity when encrypting the status. We still have some code to write, because in Part 1 we created and stored only private tanker identities. Luckily, the &lt;code&gt;tanker-identity&lt;/code&gt; Ruby gem contains a &lt;code&gt;get_public_identity&lt;/code&gt; function to convert private identities to public ones.&lt;/p&gt;

&lt;p&gt;So the plan becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a helper function to access public identities from rails&lt;/li&gt;
&lt;li&gt;When rendering the initial-state from the server, add public identities to the serialized accounts.&lt;/li&gt;
&lt;li&gt;In the client code, fetch public identities of the recipients of the encrypted statuses&lt;/li&gt;
&lt;li&gt;Instead of calling &lt;code&gt;encrypt&lt;/code&gt; with no options, call &lt;code&gt;tanker.encrypt( resource, { shareWithUsers: identities })&lt;/code&gt; where &lt;code&gt;identities&lt;/code&gt; is an array of public identities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good thing we are already parsing the &lt;code&gt;@&lt;/code&gt; mentions client-side :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Sending public identities in the initial state
&lt;/h1&gt;

&lt;p&gt;First we adapt our &lt;code&gt;TankerIdentity&lt;/code&gt; class so we can convert a private identity to a public one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/lib/tanker_identity.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_public_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Tanker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_public_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we add the &lt;code&gt;tanker_public_identity&lt;/code&gt; attribute to the &lt;code&gt;User&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tanker_public_identity&lt;/span&gt;
    &lt;span class="no"&gt;TankerIdentity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_public_identity&lt;/span&gt; &lt;span class="n"&gt;tanker_identity&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We tell the &lt;code&gt;Account&lt;/code&gt; class to delegate the &lt;code&gt;tanker_public_identity&lt;/code&gt; method to the inner &lt;code&gt;user&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/use.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;:unconfirmed_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;:current_sign_in_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;:current_sign_in_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="o"&gt;...&lt;/span&gt;
           &lt;span class="ss"&gt;:tanker_public_identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We adapt the account serializer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/serializers/rest/account_serializer.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;REST::AccountSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt; 
   &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
              &lt;span class="c1"&gt;# ...:&lt;/span&gt;
              &lt;span class="ss"&gt;:tanker_public_identity&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tanker_public_identity&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_tanker_public_identity&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the client can access the Tanker public identities of the mentioned accounts in the initial state.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sharing encrypted messages
&lt;/h1&gt;

&lt;p&gt;We can now collect the identities from the state and use them in the call to &lt;code&gt;tanker.encrypt()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submitCompose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;identities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;knownAccounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getIn&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;knownAccounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;knownAccounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mentionsSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;identities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tanker_public_identity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// …&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tankerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="nx"&gt;clearText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shareWithUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;identities&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/statuses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see what happens after this code change. This time, when Bob clicks on the notification, he sees Alice's decrypted message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p0p_NsAs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sj36o25jkqeitn7rdx41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p0p_NsAs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sj36o25jkqeitn7rdx41.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done!&lt;/p&gt;

&lt;h1&gt;
  
  
  What did we learn?
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;We discovered how notifications are handled in Mastodon&lt;/li&gt;
&lt;li&gt;We found out that some server-side processing needed to be moved client-side, as is expected when client-side encryption is used.&lt;/li&gt;
&lt;li&gt;We implemented a fully working end-to-end encryption feature for Mastodon’s direct messages, making sure direct message can be read only by the intended recipients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are curious, here are some statistics about the number of changes we had to write, excluding generated files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git diff --stat \
   :(exclude)yarn.lock \
  :(exclude)Gemfile.lock \
  :(exclude)db/schema.rb
 41 files changed, 360 insertions(+), 40 deletions(-)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Future Work
&lt;/h1&gt;

&lt;p&gt;Reminder: this is a proof of concept, and many things could be improved. Here’s a list of problems and hints about their solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve status decryption
&lt;/h2&gt;

&lt;p&gt;We are violating an implicit property of the messages in Mastodon: they are supposed to be immutable, as shown by the fact that until our patch, no action was able to change the contents of the statuses.&lt;/p&gt;

&lt;p&gt;We probably would have to refactor the client code a bit to not violate this property, with the added benefit that the UI will no longer “flicker” when statuses go from encrypted base64 strings to clear text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the identity verification flow
&lt;/h2&gt;

&lt;p&gt;We should remove the  &lt;code&gt;@tanker/verification-ui&lt;/code&gt; package and instead introduce tanker identity verification inside the existing authentication flow.&lt;/p&gt;

&lt;p&gt;You can check out the &lt;a href="https://docs.tanker.io/core/latest/guide/start/"&gt;Starting a Tanker session&lt;/a&gt; section of Tanker’s documentation for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provide alternative verification methods
&lt;/h2&gt;

&lt;p&gt;You may have noticed that the identity verification currently works by having Tanker and Mastodon servers holding some secrets. Also, the email provider of the users can, in theory, intercept the email containing the verification code.&lt;/p&gt;

&lt;p&gt;If this concerns you, please note that instead of using email-based verification, we could use another verification method called the verification key. You can read more about that in the &lt;a href="https://docs.tanker.io/core/latest/tutorials/alternative-verification-methods/"&gt;Alternative verification methods&lt;/a&gt; section of the Tanker documentation.&lt;/p&gt;

&lt;p&gt;Please do note that in this case, users are in charge of their verification key and will not be able to access any of their encrypted resources if they lose it. &lt;/p&gt;

&lt;p&gt;We could implement both verification methods and let users choose between the two during onboarding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement pre-registration sharing
&lt;/h2&gt;

&lt;p&gt;The code assumes all users sending or receiving direct messages already have a Tanker identity registered. This can also be solved by using a Tanker feature called &lt;a href="https://docs.tanker.io/core/latest/tutorials/pre-registration-sharing/"&gt;Pre-registration sharing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make encryption work across instances
&lt;/h2&gt;

&lt;p&gt;Finally, our implementation works only if the sender and receiver of the direct messages are on the same instance - we need to make encryption work with the ActivityPub protocol.&lt;/p&gt;

&lt;p&gt;I have a few ideas but fixing it seems non-trivial. Still, it would make for a pretty nice challenge :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Thanks for reading this far. Writing the patch was a nice experience: Mastodon’s source code is clean and well-organized. You can browse the changes on the &lt;a href="https://github.com/tootsuite/mastodon/pull/12428"&gt;pull request on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this gave you an idea of the possibilities offered by Tanker. If you’d like to use Tanker in your own application, please &lt;a href="https://tanker.io/"&gt;get in touch with us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to leave a comment below and give us your feedback!&lt;/p&gt;

</description>
      <category>mastodon</category>
      <category>javascript</category>
      <category>endtoend</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Classes Rock</title>
      <dc:creator>Dimitri Merejkowsky</dc:creator>
      <pubDate>Sat, 09 Nov 2019 19:26:27 +0000</pubDate>
      <link>https://dev.to/dmerejkowsky/classes-rock-1o4o</link>
      <guid>https://dev.to/dmerejkowsky/classes-rock-1o4o</guid>
      <description>&lt;p&gt;This is the second article in a 2-part series of blog posts that examine the complicated relationships between the real world and object-oriented code. If you haven’t already, you should the first part: &lt;a href="https://dev.to/dmerejkowsky/classes-suck-53m6"&gt;classes suck&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;Let’s recap: we used a &lt;code&gt;Robot&lt;/code&gt; class to implement the client specifications. It worked well, until we tried to model the&lt;code&gt;start&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt; methods with a mutable state. We concluded that blindly using classes (with methods and mutable attributes)to represent real-world objects was a mistake.&lt;/p&gt;

&lt;h1&gt;
  
  
  Taking a second look at the code
&lt;/h1&gt;

&lt;p&gt;Let’s look at the code again (minus the &lt;code&gt;stop&lt;/code&gt; method), along with the spec:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;When robots come off the factory floor, they have no name:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The first time you boot them up, a random name is generated:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;When a robot is reset, its name gets wiped:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we &lt;em&gt;were&lt;/em&gt; modeling real-world objects with our class after all: the mistake we made was trying to model the &lt;em&gt;robot&lt;/em&gt;. But using code to model &lt;em&gt;the specification&lt;/em&gt; worked!&lt;/p&gt;

&lt;p&gt;If you’re not convinced, consider this: the &lt;code&gt;stop()&lt;/code&gt; method is empty because the specification said &lt;em&gt;nothing&lt;/em&gt; about what happens when the robot is stopped.&lt;/p&gt;

&lt;p&gt;If the specification said something like &lt;em&gt;the robot cannot be reset when it isstarted&lt;/em&gt;, then using a &lt;code&gt;stopped&lt;/code&gt; attribute may have been a good idea.&lt;/p&gt;

&lt;h1&gt;
  
  
  Encapsulation
&lt;/h1&gt;

&lt;p&gt;So far we talked only about methods and attributes. There’s another aspect of programming with classes I’d like to examine next: the concept of &lt;em&gt;encapsulation&lt;/em&gt;, or how to separate “private” implementation and “public” interface.&lt;/p&gt;

&lt;p&gt;Basically, there are some details of a class that matter to the people who &lt;em&gt;wrote&lt;/em&gt; the class but that &lt;em&gt;users&lt;/em&gt; of said class should not know about.&lt;/p&gt;

&lt;p&gt;What does this have to do with the real world? &lt;strong&gt;Everything!&lt;/strong&gt; Let me show you.&lt;/p&gt;

&lt;h2&gt;
  
  
  A single line telling a whole story
&lt;/h2&gt;

&lt;p&gt;In Python, to signify to users of a class that an attribute is not part of the “public” interface, you prefix it with an underscore, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Robot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, there are already two pieces of information here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;robot names are empty at first&lt;/li&gt;
&lt;li&gt;the name likely changes over time (otherwise it would probably have been passed as a parameter in the constructor)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that’s again about the specification. What does it mean to have &lt;code&gt;_name&lt;/code&gt; as a private attribute?&lt;/p&gt;

&lt;h2&gt;
  
  
  Working in teams
&lt;/h2&gt;

&lt;p&gt;Let’s imagine we are writing software inside a big company with several teams. Reading the specification again, it’s clear that the client is &lt;em&gt;extremely&lt;/em&gt; worried about robot names: each and every line of the specification talks about it! If there is a bug affecting the robot name, the client is going to get pissed.&lt;/p&gt;

&lt;p&gt;And that’s where using &lt;em&gt;encapsulation&lt;/em&gt; makes sense: by “hiding” the &lt;code&gt;name&lt;/code&gt; attribute from the users of the Robot class, we are actually covering our bases. If someone from another team writes &lt;code&gt;robot._name = "something-else"&lt;/code&gt;,the client will get mad (the robot name no longer has the correct format), but we can tell him it’s not our fault: the other team should not have broken the encapsulation without asking us first!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;So, which is it? Do classes rock or suck at modeling the real world?&lt;/p&gt;

&lt;p&gt;Well, it all depend of what you mean by “the real world”. My advice to you is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clarify specifications as much as possible&lt;/li&gt;
&lt;li&gt;write code that closely resemble the specification&lt;/li&gt;
&lt;li&gt;use encapsulation when necessary to protect yourself against misuses of your code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When used correctly, classes are a great way to accomplish all of the above, butthey are also easy to misuse.&lt;/p&gt;

&lt;p&gt;Finally, let’s go back at our original question: “is modeling the real worldin code worth it?“.&lt;/p&gt;

&lt;p&gt;Well, if it allows you to implement the specifications correctly and makesthings easier for you and your team mates, then yes, it’s definitely worth it.&lt;/p&gt;

&lt;p&gt;It’s hard, but trying and solve this challenge is what I love about programming.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>python</category>
      <category>objectorientedprogramming</category>
      <category>controversy</category>
    </item>
  </channel>
</rss>
