<?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: Sergey Kibish</title>
    <description>The latest articles on DEV Community by Sergey Kibish (@skibish).</description>
    <link>https://dev.to/skibish</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%2F376632%2F54539d32-fc00-4735-b78b-fdd73371cdb3.jpeg</url>
      <title>DEV Community: Sergey Kibish</title>
      <link>https://dev.to/skibish</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skibish"/>
    <language>en</language>
    <item>
      <title>How to Backup to Backblaze B2 with Rclone and keep the network alive</title>
      <dc:creator>Sergey Kibish</dc:creator>
      <pubDate>Sat, 27 Nov 2021 15:11:53 +0000</pubDate>
      <link>https://dev.to/skibish/how-to-backup-to-backblaze-b2-with-rclone-and-keep-the-network-alive-i90</link>
      <guid>https://dev.to/skibish/how-to-backup-to-backblaze-b2-with-rclone-and-keep-the-network-alive-i90</guid>
      <description>&lt;p&gt;Some time ago I concluded that it would be nice to have an offsite copy of the important data if something will go completely wrong.&lt;/p&gt;

&lt;p&gt;I'm living in a place with not the fastest internet download/upload (39/12 Mbps) speeds. It took some time to tune all the parameters of the Rclone&lt;sup id="fnref1"&gt;1&lt;/sup&gt; to successfully create an initial backup (around 400GB) and keep the network alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why to backup
&lt;/h2&gt;

&lt;p&gt;When you work more and more in IT you understand the importance of a good level of redundancy. Everything breaks at some point (accidentally or on purpose), you need to have backups.&lt;/p&gt;

&lt;p&gt;There is a 3-2-1 backup rule&lt;sup id="fnref2"&gt;2&lt;/sup&gt; which is nice to follow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There should be at least 3 copies of the data, stored on 2 different types of storage media, and one copy should be kept offsite, in a remote location (this can include cloud storage).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I already had two pieces of the formula (3 copies, 2 of them on different external hard drives). One was missing: offsite backup.&lt;/p&gt;

&lt;p&gt;When I was young I had not a pleasant experience of losing hard drive and all family photos, videos, etc... After this, I'm a little bit paranoid about where such information should be stored and how it should be backed up.&lt;/p&gt;

&lt;p&gt;Currently, my storage environment looks as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-hosted instance of Nextcloud&lt;sup id="fnref3"&gt;3&lt;/sup&gt; as a main source of storage&lt;/li&gt;
&lt;li&gt;Backups to two external hard drives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the time came to introduce cloud backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to backup
&lt;/h2&gt;

&lt;p&gt;I've wanted something simple, cheap, secure which I can execute in the terminal and it would just work.&lt;/p&gt;

&lt;p&gt;One of the options was to go with some VPS and rsync&lt;sup id="fnref4"&gt;4&lt;/sup&gt; classic setup. This setup is not so cheap. Each cloud provider has a cost for bandwidth, storage and compute. Price is very different, but with 400GB you might end up spending from $5 to $10, or more per month. I wanted something cheaper.&lt;/p&gt;

&lt;p&gt;I've chosen Backblaze B2&lt;sup id="fnref5"&gt;5&lt;/sup&gt;. It's less than $2 ($0.0005 * 400) per month for me. Which is very good for me. If I will need to extract the full backup, it will cost me $4 ($0.01 * 400).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to backup
&lt;/h2&gt;

&lt;p&gt;Okay, how to backup?&lt;/p&gt;

&lt;p&gt;rsync would not work in this case because it works with file system but with B2 you would need to use API, b2 CLI, S3 compatible tools (because it is compatible with S3 API) or something else.&lt;/p&gt;

&lt;p&gt;And I've stumbled across Rclone&lt;sup id="fnref1"&gt;1&lt;/sup&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rclone is a command line program to manage files on cloud storage. It is a feature rich alternative to cloud vendors' web storage interfaces. Over 40 cloud storage products support rclone including S3 object stores, business &amp;amp; consumer file storage services, as well as standard transfer protocols.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What a beast is this tool. It can not only backup and restore, but also encrypt and decrypt the data, mount, analyse and other stuff. Worth checking out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring rclone
&lt;/h3&gt;

&lt;p&gt;All configuration is stored in the &lt;code&gt;rclone.conf&lt;/code&gt; file which can also be encrypted and stored in the git repo.&lt;/p&gt;

&lt;p&gt;The configuration looks similar to the below:&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;[local]&lt;/span&gt;
&lt;span class="py"&gt;remote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/storage&lt;/span&gt;

&lt;span class="nn"&gt;[b2]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b2&lt;/span&gt;
&lt;span class="py"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;BACKBLAZE_ACCOUNT_ID&lt;/span&gt;
&lt;span class="py"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;BACKBLAZE_ACCOUNT_KEY&lt;/span&gt;
&lt;span class="py"&gt;hard_delete&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;[b2-crypt]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;crypt&lt;/span&gt;
&lt;span class="py"&gt;remote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b2:backup-bucket&lt;/span&gt;
&lt;span class="py"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;CRYPT_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are three remotes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;local&lt;/code&gt; is my local folder which should be backed up&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b2&lt;/code&gt; is a connection to my B2 account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b2-crypt&lt;/code&gt; is a special remote to save files in b2 bucket encrypted.
It wraps &lt;code&gt;b2&lt;/code&gt; remote.
More details can be found in the &lt;a href="https://rclone.org/crypt/"&gt;very nice documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do the encrypted backup, you do the following in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rclone &lt;span class="nt"&gt;--config&lt;/span&gt; rclone.conf &lt;span class="nb"&gt;sync local&lt;/span&gt;: b2-crypt &lt;span class="nt"&gt;--progress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will start to backup. Slowly.&lt;br&gt;
And remembering my network speeds, it will start to die because &lt;code&gt;rclone&lt;/code&gt; will eat all the bandwidth. You will not be able to watch anything on YouTube or Netflix. Even loading some blogs will take a longer time than usual. Then rclone will tell you that it starts to drop connections sometimes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rclone crashing the internet
&lt;/h3&gt;

&lt;p&gt;Search will tell that you are not the only one with such a problem&lt;sup id="fnref6"&gt;6&lt;/sup&gt;. If you don't have a very fancy router, you might end up with a phenomenon named bufferbloat&lt;sup id="fnref7"&gt;7&lt;/sup&gt;. The question is, what to do? We still need to backup our data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Time for some tuning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;.&lt;br&gt;
Take measurements of your WAN download/upload speeds with Speedtest or embedded in the router speed test tool.&lt;/p&gt;

&lt;p&gt;Let's take my numbers from the above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download: 39 Mbps&lt;/li&gt;
&lt;li&gt;Upload: 12 Mbps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;.&lt;br&gt;
If you have some QoS settings in the router, you might want to manually set the limits for upload and download bandwidth 10% less than measured, e.g. 35/10,8 Mbps. This will help the router to have room to breathe.&lt;/p&gt;

&lt;p&gt;This step is optional, but if you had some problems with the internet connection in general, this setting might help you solve the issue too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;.&lt;br&gt;
Now we need to configure the &lt;code&gt;rclone&lt;/code&gt; parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rclone &lt;span class="nt"&gt;--config&lt;/span&gt; rclone.conf &lt;span class="nb"&gt;sync local&lt;/span&gt;: b2-crypt: &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transfers&lt;/span&gt; 20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--b2-chunk-size&lt;/span&gt; 48M &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--b2-hard-delete&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--fast-list&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bwlimit&lt;/span&gt; &lt;span class="s2"&gt;"08:00,512k 12:00,10M 13:00,512k 23:00,10M"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--progress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--transfers&lt;/code&gt; — how many files to transfer simultaneously.
Backblaze can handle a lot of simultaneous transfers and works better when there are many of them. According to the &lt;code&gt;rclone&lt;/code&gt; backblaze module documentation&lt;sup id="fnref8"&gt;8&lt;/sup&gt; the recommended size is 32. I've ended up with 20, experimentally.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--b2-chunk-size&lt;/code&gt; — the default is 96M which is must be fine, but because speed is not fantastic and I wanted to put many files online, sliced it in half.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--b2-hard-delete&lt;/code&gt; and &lt;code&gt;--fast-list&lt;/code&gt; are optional but I've set them in my script because they are valuable during next backups. By default, B2 does not delete files when you delete them. In my case, I want to delete them and not store shadow copies. Fast list helps to execute fewer commands against the service (e.g. list all files in one request and not many).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--bwlimit&lt;/code&gt;.
This one is interesting. Because I needed to work, I've limited &lt;code&gt;rclone&lt;/code&gt; on the usage of the network. In the example above I've instructed &lt;code&gt;rclone&lt;/code&gt; to use only 512kbps starting 08:00 in the morning, a little bit more during lunch, back to 512 after and back to the maximum at night.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If in Step 2 you enabled QoS settings on your router, then you can change &lt;code&gt;10M&lt;/code&gt; to &lt;code&gt;off&lt;/code&gt; (use all that is available).&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;With the above recommendations, everyone in your household will be able to use the internet normally and backup will happen... but slowly. In my case, it took around 4-5 days. So, if you have access to the data centre or fibre network, you will have more luck!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://rclone.org"&gt;https://rclone.org&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Backup#3-2-1_rule"&gt;https://en.wikipedia.org/wiki/Backup#3-2-1_rule&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://nextcloud.com"&gt;https://nextcloud.com&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://linux.die.net/man/1/rsync"&gt;https://linux.die.net/man/1/rsync&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://www.backblaze.com/b2/cloud-storage.html"&gt;https://www.backblaze.com/b2/cloud-storage.html&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;a href="https://forum.rclone.org/t/rclone-crashing-internet/4593"&gt;https://forum.rclone.org/t/rclone-crashing-internet/4593&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Bufferbloat"&gt;https://en.wikipedia.org/wiki/Bufferbloat&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;a href="https://rclone.org/b2/#transfers"&gt;https://rclone.org/b2/#transfers&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>tutorial</category>
    </item>
    <item>
      <title>Git Sweet Git</title>
      <dc:creator>Sergey Kibish</dc:creator>
      <pubDate>Mon, 20 Jul 2020 16:47:56 +0000</pubDate>
      <link>https://dev.to/skibish/git-sweet-git-46p3</link>
      <guid>https://dev.to/skibish/git-sweet-git-46p3</guid>
      <description>&lt;p&gt;Your Git repository is like your home. You spent time there.&lt;br&gt;
You move things. You want it to be a nice and comfortable place for you. And you want to keep it clean and ready for guests. Yes, sometimes it's a mess after a party but still, you know how to clean everything and get back a shiny house.&lt;/p&gt;
&lt;h2&gt;
  
  
  README
&lt;/h2&gt;

&lt;p&gt;When guests arrive to you what is the first thing they see? Door and a rug with "WELCOME" word printed on it.&lt;/p&gt;

&lt;p&gt;This rug is a &lt;code&gt;README.md&lt;/code&gt; file in your Git repository. This is a first place where users try to familiarize with your project and start to look around. Like in real life.&lt;/p&gt;

&lt;p&gt;Usually your guests will take off shoes on this rug while inspecting what they can see.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;README.md&lt;/code&gt; you want to answer following main questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is this?&lt;/li&gt;
&lt;li&gt;How to install this?&lt;/li&gt;
&lt;li&gt;How to use this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you would like your guests to contribute to today's dinner, then additional questions should be answered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to contribute?&lt;/li&gt;
&lt;li&gt;What help is needed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are different homes. But Git repository should be a minimalist house.&lt;/p&gt;

&lt;p&gt;What does it mean - "minimalist house"? My definition is following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Minimalist house is a house that has only what is needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  .gitignore
&lt;/h2&gt;

&lt;p&gt;You know, when guests enter your room you don't want them to see socks scattered all around. You need to put everything in a locker. In the Git repository "socks" can be hidden with help of &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What are those "socks"? Something that adds mess to your repository and should not be shown to your guests. Something like &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;vendor&lt;/code&gt;, &lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;.vscode&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;In Git there are two types of &lt;code&gt;.gitignore&lt;/code&gt; files. Global and local. Global is used to ignore files that expose your working environment. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files that are generated by the OS, e.g. &lt;code&gt;.DS_Store&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Files that somehow show which editor you are using, e.g. &lt;code&gt;.idea&lt;/code&gt;, &lt;code&gt;.vscode&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Local is used to hide irrelevant parts of your project. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vendor&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those files and folders that are not mandatory to save in the repository. &lt;code&gt;node_modules&lt;/code&gt; are populated after &lt;code&gt;npm install&lt;/code&gt;, thus are not needed. &lt;code&gt;*.log&lt;/code&gt; files are added when you debug something. &lt;code&gt;.env&lt;/code&gt; file is your specific environment configuration that should not be shared with others. I think you've got an idea.&lt;/p&gt;

&lt;p&gt;Good collection of global and local &lt;code&gt;.gitignore&lt;/code&gt; files can be found in &lt;a href="https://github.com/github/gitignore"&gt;github/gitignore&lt;/a&gt; repository.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good repository should only consist of things that are needed for the project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  .gitattributes
&lt;/h2&gt;

&lt;p&gt;While you cook guests wait for you in the main room and start to walk around touch things and then somebody asks a question: "Is this a picture or a book?" pointing their finger on a shelf. "This is a picture", you answer. "Looks so real", one of your friends whispers.&lt;/p&gt;

&lt;p&gt;Sometimes you need to help Git the same way as you helped your friend to recognize what it is in front. With this helps &lt;code&gt;.giattributes&lt;/code&gt; file. This file helps Git to understand the contents of other files to better diff / render them.&lt;/p&gt;

&lt;p&gt;Nice collection of &lt;code&gt;.gitattributes&lt;/code&gt; files can be found in &lt;a href="https://github.com/alexkaratarakis/gitattributes"&gt;alexkaratarakis/gitattributes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some web project &lt;code&gt;.gitattributes&lt;/code&gt; file might look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Graphics
*.gif    binary
*.jpg    binary
*.png    binary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want to help Git to identify images as images and not text, for example.&lt;/p&gt;

&lt;p&gt;At this point repository looks 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;README.md
.gitignore
.gitattributes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a good start for any project that should go with an initial commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commit message
&lt;/h2&gt;

&lt;p&gt;Somebody found your family photo album. They start to look through, year by year, event by event. Childhood, youth, adulthood. What were the times... What is good about this album is that on each page there is a title and some description of what happened.&lt;/p&gt;

&lt;p&gt;Git commit history is similar to this photo album. Event by event you can go through it. You can go from beginning till the end viewing commits with title and description.&lt;/p&gt;

&lt;p&gt;Yes, commits have title and description. And it is nice to have them both. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add view counter to each post

Added view counters to each post
to understand what posts are popular
and which are not.

Closes: 906
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When somebody in the future will open this commit, they can understand why this was done. In the commit description you can have some meta information to help find related content. In this case you see &lt;code&gt;Closes&lt;/code&gt; &lt;a href="https://git-scm.com/docs/git-interpret-trailers"&gt;trailer&lt;/a&gt; which point to an issue in some ticket system that can be closed by this commit. This also might be a link to some internal wiki, etc.&lt;/p&gt;

&lt;p&gt;There are many different conventions on how to write commits and it is mandatory to agree on the approach within your team. Otherwise a beautiful story might be lost and you will need to do a lot of explanation to your guests. For the convention example you can take a look at &lt;a href="https://git.wiki.kernel.org/index.php/CommitMessageConventions"&gt;Git Wiki&lt;/a&gt; and &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits Specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Treat Git commit as emails. There is a good summary on Git Wiki about how to write nice Git commits:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The "one-line summary plus body of the message" has a strong correlation with how we communicate [..] via e-mail.&lt;br&gt;
You do not start a sentence on the "Subject: " header and continue on to the body of the message, starting the body halfway of the sentence.&lt;br&gt;
Instead, you try to make sure you write something sensible by itself on the "Subject: " header to help the recipient when later scanning for it among bunch of messages, and you write a full paragraph that you can understand without reading the subject line first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Commit history
&lt;/h2&gt;

&lt;p&gt;Now you enter the room with 12 plates in your hands. Everyone looks at you shocked (because you handled so many). And then they fall...&lt;/p&gt;

&lt;p&gt;It's better to serve plates one by one or have a few in hands. Stable and more control.&lt;/p&gt;

&lt;p&gt;Same applies for Git commits. Smaller and granular ones are better.&lt;/p&gt;

&lt;p&gt;Personally I would recommend to have commits on each move while you fix bugs or develop features. This will help reviewers to understand thinking behind final implementation. Your commit history might look like this in a feature branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* c911ba2 (HEAD) simplified method according to provided feedback
* 4692d22 added integration tests
* 7b84532 added unit tests
* 47b8452 implemented message parsing logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When everything is reviewed and you are ready to merge - &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#_squashing"&gt;squash commits&lt;/a&gt; into one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* c911ba2 (HEAD) feat: add message parser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With body having context of work done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add message parser

Implemented message parser according to #42 document.
Unit and integration tests cover all cases described
in the linked document.

Reviewed-By: D
Closes: 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rebase and Merge
&lt;/h2&gt;

&lt;p&gt;While you make a beautiful dessert in the kitchen a lot of things are flying around. Many ingredients lie on a table, jars are open, everything smells tasty. Process is going on. When you are almost finished, you clean everything up and add final touch with a cherry on top.&lt;/p&gt;

&lt;p&gt;It is good to do a rebase when you are almost finished and would like to present your beautiful work. Merge - to signal that work is completed.&lt;/p&gt;

&lt;p&gt;Use both. The approach I prefer is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rebase on feature / fix branches&lt;/li&gt;
&lt;li&gt;Merge to main branch when everything is finished&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rebase is a nice helper for cleanup after work. Highly recommend to read about &lt;a href="https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing"&gt;"The Golden Rule of Rebasing"&lt;/a&gt; before using it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Merge is a completion indicator. When work is finished and a milestone is achieved, it can be marked with a merge commit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  .gitconfig
&lt;/h2&gt;

&lt;p&gt;When conversations are finished, you and your friend go toward the corner with piano and guitar and start jamming, having fun and singing songs together.&lt;/p&gt;

&lt;p&gt;It is possible to start playing straight away because all instruments are in tune and fingers know how to play these instruments. To play even better on guitar some time ago you gave it to master which made it even better to play. Less movement for hands, more precise sound because strings were changed.&lt;/p&gt;

&lt;p&gt;Git can also be treated as an instrument. You can play on it right now or you can tune it up a little bit to play even better.&lt;/p&gt;

&lt;p&gt;Such tuning is done with help of configuration and aliases. For example, you would like to list tags and branches in your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tags
git branches

&lt;span class="c"&gt;# Without configuration&lt;/span&gt;
&lt;span class="c"&gt;# git tag -l&lt;/span&gt;
&lt;span class="c"&gt;# git branch -a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aliases hide complexity. Better to read and less possible mistakes to make.&lt;/p&gt;

&lt;p&gt;Push branch and added release tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push

&lt;span class="c"&gt;# Without configuration:&lt;/span&gt;
&lt;span class="c"&gt;#  git push --follow-tags or git push &amp;amp;&amp;amp; git push --tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With help of &lt;code&gt;.gitconfig&lt;/code&gt; you can set this as default behavior (push tags on &lt;code&gt;git push&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;My recommendation is to share the same &lt;code&gt;.gitconfig&lt;/code&gt; across your team. Good configuration will ensure that team members are on the same page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nice configuration can simplify work with Git and make it more predictable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a reference you can take a look at my &lt;code&gt;.gitconfig&lt;/code&gt; in &lt;a href="https://github.com/skibish/dotfiles/blob/master/.gitconfig"&gt;skibish/dotfiles&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  rm -rf . &amp;amp;&amp;amp; git init
&lt;/h2&gt;

&lt;p&gt;If you did some home repairs you know that sometimes it is better to start from scratch.&lt;/p&gt;

&lt;p&gt;Sometimes this happens to the Git repositories too. Even though ideally Git should store all the history of your repo, sometimes it makes sense to drop everything and start recording history of changes from scratch.&lt;br&gt;
When this might happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You build an MVP or PoC of a product.
It would be better to start the history of a "real" production product fresh&lt;/li&gt;
&lt;li&gt;Repository is not in a good shape.
A lot of artifacts are not in their places, some burden around, not easy to follow history, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't be afraid to throw things away.&lt;br&gt;
It just means that you are growing and freeing up space for future endeavors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;These little things helped me to simplify my Git workflow. Hope that something from suggestions will work for you too.&lt;/p&gt;

&lt;p&gt;What are your approaches to keep Git repository clean and work with it easier?&lt;/p&gt;

</description>
      <category>git</category>
      <category>codenewbie</category>
      <category>codequality</category>
    </item>
    <item>
      <title>About Those Behind The Curtains</title>
      <dc:creator>Sergey Kibish</dc:creator>
      <pubDate>Wed, 24 Jun 2020 17:36:22 +0000</pubDate>
      <link>https://dev.to/skibish/about-those-behind-the-curtains-3l8f</link>
      <guid>https://dev.to/skibish/about-those-behind-the-curtains-3l8f</guid>
      <description>&lt;p&gt;I want all involved in development to think about other users. Not about those who use your application to solve their problems. But those who extend your app, run it in production and integrate with other systems. About developers, operations, SREs and many others who make your business running. Those behind the curtains. I want you to think about the Developers Experience (DX).&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Working with different businesses and in outsource companies I've spot following tendency: The main focus is &lt;em&gt;always&lt;/em&gt; on business requirements and UI features of built application. Because this is the first thing that real users will see. A client who ordered a new feature would like to see a pretty demo and understand that money invested is working. Such priority looks logical and right. But problems are hiding behind such thinking.&lt;/p&gt;

&lt;p&gt;There are many other users behind the curtains. Almost nobody thinks about developer experience (except IT-focused products). Giving priority only to business features and UI is not enough for some solutions.&lt;/p&gt;

&lt;p&gt;Nowadays all businesses are digital. They integrate with many systems, chats, banks, you name it. There is always some development work happening and many of them don't focus on DX. Which is bad.&lt;/p&gt;

&lt;p&gt;It is bad because engineers also should feel happy using your product. If they struggle, you as a business might have loss of trust or reputation. All users of your product are special. Yes, they all have different needs, but meet those needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Culture
&lt;/h2&gt;

&lt;p&gt;DX is also about product vision. It is something that should be in your development team. It should become part of a culture. Understand what you create. Not from specifications but from the real users perspective.&lt;/p&gt;

&lt;p&gt;Frontend developers are more product-oriented than backend developers. They focus on users and their experience. Backend developers focus on algorithms and data structures. I know that there are backend developers who also think about their users. This article is not about them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The product team and people who have vision should teach others to see problems from the real users perspective.&lt;br&gt;
If you would like to bring innovations and raise the quality of the product - such thinking should become part of you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bad DX examples
&lt;/h2&gt;

&lt;p&gt;What experience should engineers avoid trying to use your application? This is an opinionated short list of bad DX examples. If you have something to add - welcome to the comment section below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bad API design
&lt;/h3&gt;

&lt;p&gt;Take a look at the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET     /users
GET     /users?id={userID}
NEW     /users/create
POST    /users/update?id={userID}
DROP    /user/{userID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you feel pain looking at this?&lt;br&gt;
I do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a mix of different things&lt;/li&gt;
&lt;li&gt;Semantics are not followed&lt;/li&gt;
&lt;li&gt;Not easy to understand straight away what is happening&lt;/li&gt;
&lt;li&gt;Not standard HTTP methods are in use (but this is fine according to &lt;a href="https://tools.ietf.org/html/rfc2616#section-9"&gt;RFC 2616&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is good is that it works. But it would be a lot better if it looked 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;GET       /users
GET       /users/{userID}
POST      /users
PUT       /users/{userID}
DELETE    /user/{userID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Without looking at documentation such API already starts to give clues.&lt;/p&gt;

&lt;p&gt;Many frameworks (&lt;a href="https://guides.rubyonrails.org/getting_started.html#getting-up-and-running"&gt;Ruby on Rails&lt;/a&gt;, &lt;a href="https://laravel.com/docs/7.x/controllers#resource-controllers"&gt;Laravel&lt;/a&gt;, &lt;a href="https://www.playframework.com/documentation/2.8.x/JavaRouting#HTTP-routing"&gt;Play&lt;/a&gt;, etc.) suggest following design of REST APIs by default. Actually, Roy Thomas Fielding was the first who &lt;a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm"&gt;proposed REST&lt;/a&gt;. To start developing taste in API design take a look at &lt;a href="https://restfulapi.net/rest-api-design-tutorial-with-example/"&gt;this resource&lt;/a&gt; or &lt;a href="https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/"&gt;this article&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;Another thing that I would like to mention is deep nested objects in API responses. Objects are trees and sometimes those trees grow like real trees. Very tall and dense. It is not good when your response object looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"another value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get needed values you need to traverse a graph. Algorithmic complexity to parse a graph is &lt;code&gt;O(V+E)&lt;/code&gt; (&lt;a href="https://en.wikipedia.org/wiki/Depth-first_search"&gt;DFS&lt;/a&gt;). And it's a lot. There might be too many nodes to traverse. It would be better to flatten such object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some value"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001:002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"another value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001:002:xxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and another value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better. And this might be a final variant if the return object is small. Parsing the following structure will take &lt;code&gt;O(n)&lt;/code&gt; (because of one loop). Nice. And reading such structure is easier. Is it possible to lower search to &lt;code&gt;O(1)&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"001"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some value"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"001:002"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001:002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"another value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"001:002:xxx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"001:002:xxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and another value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it is &lt;code&gt;O(1)&lt;/code&gt;. You can specify a key and extract value without iterating an object. Some may recall Redux &lt;a href="https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape"&gt;State Shape Normalization&lt;/a&gt; by looking at the last example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Use the following approach to store objects under keys in cache.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Solutions provided here are not silver bullets. Key takeaway:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep your objects as flat as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at your APIs. Can somebody's life be simplified?&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration properties chaos
&lt;/h3&gt;

&lt;p&gt;Sometimes configuration properties are treated not seriously. Take a look at this snippet:&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;base.url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1313&lt;/span&gt;
&lt;span class="py"&gt;jobs.disabled&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;extract.values.and.save.period.hours&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;send.email.each.12.hours&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Naming is strange and hard to read&lt;/li&gt;
&lt;li&gt;Hard to follow logic (because there is no logic)&lt;/li&gt;
&lt;li&gt;Time unit is big.
It might be that there are properties set in other time units than hours.&lt;/li&gt;
&lt;li&gt;It's hard to figure out the meaning of some properties by their names&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat configuration properties as first-class citizens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers and Ops interact with your application using properties. If it is not clear how to use them - mistakes may arise. Additionally it is a sign about the bad quality of your product.&lt;/p&gt;

&lt;p&gt;How to fix 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="c"&gt;# Network address (server.address) to which the server should bind
# and listen port for incoming HTTP requests (server.port).
# Default:
&lt;/span&gt;&lt;span class="py"&gt;server.address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;80&lt;/span&gt;

&lt;span class="c"&gt;# Job to extract and save reports to storage.
# If enabled (jobs.extract_reports.enabled) job will
# run every 5 hours (jobs.extract_reports.interval in seconds) and save
# reports to defined destination (jobs.extract_reports.destination).
#
# Enable email notifications
# (jobs.extract_reports.email_notifications) to send summary
# of jobs done during the day.
# Default:
&lt;/span&gt;&lt;span class="py"&gt;jobs.extract_reports.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;span class="py"&gt;jobs.extract_reports.interval&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;18000&lt;/span&gt;
&lt;span class="py"&gt;jobs.extract_reports.destination&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/tmp/reports&lt;/span&gt;
&lt;span class="py"&gt;jobs.extract_reports.email_notifications&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Comments added - more context given&lt;/li&gt;
&lt;li&gt;Sensible defaults are set&lt;/li&gt;
&lt;li&gt;All properties are logically grouped&lt;/li&gt;
&lt;li&gt;Application with set defaults will be able to start without extra configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For inspiration I would recommend to take a look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://download.redis.io/redis-stable/redis.conf"&gt;redis.conf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/postgres/postgres/blob/master/src/backend/utils/misc/postgresql.conf.sample"&gt;postgresql.conf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Logs with no meaning and action
&lt;/h3&gt;

&lt;p&gt;Logs are the main interface of your app for the Ops team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If logs lack context it might lead to long investigations of a problem.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[12:40:45] ERROR Exception happened
com.ekiras.exception.BaseException: Base Exception
at com.ekiras.controller.HomeController.ex1(HomeController.java:21) ~[bin/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
...
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:860) [spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [tomcat-embed-core-8.0.30.jar:8.0.30]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such a log only states that something bad happened. For those who don't have access to the code of your app this is almost zero information. Stacktrace will not give information on how to solve the issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[12:40:45] ERROR Connection to the database (db.internal/app) failed. Check datasource property in application configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message above is clearer because it states directly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is the problem - DB connection failed&lt;/li&gt;
&lt;li&gt;How this error can be fixed - check &lt;code&gt;datasource&lt;/code&gt; property&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, machines collect logs first and after humans read them. If this is the case, write log in JSON or structured text formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time="2020-06-14T01:27:38-04:00" level=error msg="Connection to the database (db.internal/app) failed. Check datasource property in application configuration" status_code=500 path="/about"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the example above, logs can have more context like response status code and path that failed. The following format is readable by machines and by humans. Rule of thumb:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One event equals one log line.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  No comments in code
&lt;/h3&gt;

&lt;p&gt;When you want to use some method from the library and it doesn't have a description - it's a problem.&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="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bool&lt;/span&gt; &lt;span class="n"&gt;limit&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;group&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;sort&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guess games, anyone? The assumption is that it will return the list of users. But what is &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;group&lt;/code&gt; and &lt;code&gt;sort&lt;/code&gt;? What are their values? What can be set there?&lt;/p&gt;

&lt;p&gt;Without comments we need to read code to understand what is happening there. After reading open Pull Request with the following comment:&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="cm"&gt;/**
 * Return list of users.
 *
 * @param limit specifies to get users with limited access (default: true)
 * @param group user group to lookup (default: "regular")
 * @param sort  how to sort users (default: "asc"; possible values: "asc", "desc")
 * @return      list of users
 */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;limit&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;group&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;sort&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You've got angry developers who spent a lot of time understanding code that they should not even know.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://golang.org"&gt;Go&lt;/a&gt; is playing this game by &lt;a href="https://golang.org/doc/effective_go.html#commentary"&gt;making comments mandatory&lt;/a&gt;.&lt;br&gt;
And another player is &lt;a href="https://www.rust-lang.org"&gt;Rust&lt;/a&gt;, who go with &lt;a&gt;documentation comments as tests&lt;/a&gt; approach.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Self-documenting code is a myth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Document your code. Users and the future self will be very grateful for such effort.&lt;/p&gt;
&lt;h3&gt;
  
  
  Unpredictable responses
&lt;/h3&gt;

&lt;p&gt;For example, you want to get a list of prices set for some item in the shop during the specified day. API response might look following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"78956745"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"8,72"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"9,01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"8,02"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if there were no price changes during the day? Item ID and day are valid. The only thing missing is the list of prices. Consider the following options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Prices are returned as an empty list:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"78956745"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prices are not returned:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"78956745"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prices are set to null:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"78956745"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two last options are not predictable. Option 2 modifies response by removing &lt;code&gt;prices&lt;/code&gt; object from the response. Not good. Option 3 makes it worse. You saw that in response there is a list and now you observe different type.&lt;/p&gt;

&lt;p&gt;The best approach is to go with Option 1. You know that &lt;code&gt;prices&lt;/code&gt; is a list. In future, you also expect that there will be a list. If there is no information, then return an empty list. Less confusion and meet expectations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Only once executed configuration properties
&lt;/h3&gt;

&lt;p&gt;You can give an option to the user to configure your service via configuration files or APIs. In some scenarios, this does make sense. But sometimes there is a case when configuration in a file is used only once to bootstrap an app. And then only the API is in use.&lt;/p&gt;

&lt;p&gt;Don’t do that. If you provide both options - both of them should work the same way. Do change in file - changes apply. Do changes via API - changes apply.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Configuration operations that users do should be &lt;a href="https://en.wikipedia.org/wiki/Idempotence"&gt;idempotent&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn’t matter how you set configuration - it should always work the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to level-up DX
&lt;/h2&gt;

&lt;p&gt;It's all about thinking. And thinking is something not easy to change. I would suggest to start with &lt;a href="https://12factor.net/"&gt;The Twelve-Factor methodology&lt;/a&gt;. It is a set of good practices to apply. It's more oriented towards backend applications, but ideas can apply to other areas as well. Take a look at &lt;a href="https://stripe.com/"&gt;Stripe&lt;/a&gt; for inspiration. This is one of the companies that is known for great DX. Another company that I like and it inspires is &lt;a href="https://www.digitalocean.com"&gt;Digital Ocean&lt;/a&gt;. Also suggest to read fresh and nice article from Chris Coyer &lt;a href="https://css-tricks.com/what-is-developer-experience-dx/"&gt;about meaning of DX for different people&lt;/a&gt;. Are you building CLI? Get inspiration from Carolyn Van Slyck talk on &lt;a href="https://www.youtube.com/watch?v=eMz0vni6PAw"&gt;how to design Command-Line tools that people love&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And ask yourself questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who are end users / stakeholders? Is it another team? Other developers who perform integrations?&lt;/li&gt;
&lt;li&gt;How will this library be used?&lt;/li&gt;
&lt;li&gt;Can this be simplified?&lt;/li&gt;
&lt;li&gt;Is this readable and understandable?&lt;/li&gt;
&lt;li&gt;Does it look simple?&lt;/li&gt;
&lt;li&gt;Will my colleagues understand this?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;And ask for feedback to become even better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hope you enjoyed it. Happy coding and nice mood!&lt;/p&gt;

</description>
      <category>learning</category>
      <category>tips</category>
      <category>development</category>
    </item>
  </channel>
</rss>
