<?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>Forem: Kryštof Řeháček</title>
    <description>The latest articles on Forem by Kryštof Řeháček (@krystofee).</description>
    <link>https://forem.com/krystofee</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%2F192087%2F20b3590b-186b-4e46-9e2e-795c73a66eb0.jpg</url>
      <title>Forem: Kryštof Řeháček</title>
      <link>https://forem.com/krystofee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/krystofee"/>
    <language>en</language>
    <item>
      <title>How does 10x programmer test code?</title>
      <dc:creator>Kryštof Řeháček</dc:creator>
      <pubDate>Mon, 10 Jun 2024 12:59:30 +0000</pubDate>
      <link>https://forem.com/krystofee/how-does-10x-programmer-test-code-3lc9</link>
      <guid>https://forem.com/krystofee/how-does-10x-programmer-test-code-3lc9</guid>
      <description>&lt;p&gt;I would like to share a pattern for unit testing that I discovered while reading through the repository of one of our dependencies. It's about testing through object representation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problems with Testing Code
&lt;/h1&gt;

&lt;p&gt;I perceive many problems with testing, but two main ones stand out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s difficult to write unit tests that test what they should and don't degrade over time.&lt;/li&gt;
&lt;li&gt;It’s hard to write unit tests quickly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our mindset is to develop features as quickly as possible, even at the cost of sometimes breaking things. We don't have the capacity or appetite for 100% test coverage. This post is for similarly-minded programmers.&lt;/p&gt;

&lt;p&gt;Senior developers are here to create well-structured designs and deliver features. Therefore, they don't have time to write good tests and delegate such work to junior colleagues.&lt;/p&gt;

&lt;p&gt;Juniors don't know how to properly test code, so they test everything they can think of, as they were taught in school. &lt;strong&gt;What they do is just cover the code in concrete.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tests become unreadable in half a year, making it hard to understand what they test. If changes are made to the "concreted code" later, tests break, requiring fixes. If they aren't readable, they can't be fixed, and the test rots – it gets deleted or modified just to pass, and the problem grows.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to Write Simple Tests?
&lt;/h1&gt;

&lt;p&gt;Let's look at the test below. A simple test, checking that the items of the following invoice will be as expected. A very simple example, but it takes a moment to decode what exactly it tests.&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_subscription_with_usage_first_tier&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&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="n"&gt;usage_summary_group&lt;/span&gt; &lt;span class="o"&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;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_upcoming_invoice_item_groups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&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="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.5&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CZK&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CZK&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_discounted_price&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.5&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;discount_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Discount 10%&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;discount_percent_off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An alternative I offer as a solution is to define a &lt;code&gt;__repr__&lt;/code&gt; method for such an object that includes all relevant values.&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_subscription_with_usage_first_tier&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&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="n"&gt;usage_summary_group&lt;/span&gt; &lt;span class="o"&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;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_upcoming_invoice_item_groups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&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="nf"&gt;aware_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_summary_group&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;UpcomingInvoiceItemGroup 4.50 CZK: Product - Tier 1 (x5) 5.00 CZK (Discount 10% = 0.50 CZK)&amp;gt;&lt;/span&gt;&lt;span class="sh"&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 test below tests the same thing as the first test. The difference is that the second test tests the string representation of the object instead of checking all the attributes.&lt;/p&gt;

&lt;p&gt;The second test is much faster to write and significantly easier to read. Writing readable tests is one of the key factors in ensuring that a test doesn't rot over time.&lt;/p&gt;

&lt;p&gt;But there's a catch. By not testing the object's attributes, there can be an error in the definition of the &lt;code&gt;__repr__&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;This means that such a solution is a tradeoff. By trusting the &lt;code&gt;__repr__&lt;/code&gt; method, I've written a test that is easy to read and faster to write. However, this could be the difference between having tested code and code for which no test exists.&lt;/p&gt;

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

&lt;p&gt;If you test your code and have no problems with it, you're probably doing it right. However, if you don't have time to write tests, this solution could provide simple, readable, and maintainable tests.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>python</category>
    </item>
    <item>
      <title>Pull Request Checklist</title>
      <dc:creator>Kryštof Řeháček</dc:creator>
      <pubDate>Mon, 31 May 2021 12:19:45 +0000</pubDate>
      <link>https://forem.com/krystofee/ultimate-pull-request-checklist-4aon</link>
      <guid>https://forem.com/krystofee/ultimate-pull-request-checklist-4aon</guid>
      <description>&lt;p&gt;Hello there 👋 I've created an ultimate pull request checklist which summarises some of the &lt;strong&gt;best practises&lt;/strong&gt; we pursue in every pull request.&lt;/p&gt;

&lt;p&gt;There are many blogposts about merge review checklists, but none of them covered everything I wanted so I've created my own.&lt;/p&gt;

&lt;p&gt;Authors should memorise it and apply these rules when making a pull request. Otherwise you'll burn reviewer's time and later your time fixing the threads (that you could prevent).&lt;/p&gt;

&lt;p&gt;We are using &lt;strong&gt;Gitlab&lt;/strong&gt; for vcs, &lt;strong&gt;Django&lt;/strong&gt; on BE and &lt;strong&gt;React w/ typescript&lt;/strong&gt; on FE. So few points may be related to them.&lt;/p&gt;

&lt;p&gt;There it is:&lt;/p&gt;




&lt;h1&gt;
  
  
  Guide to merge requests
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Review your code outside the editor you've written the code in and review the code once more in VCS.&lt;/li&gt;
&lt;li&gt;Have a clear understanding of the problem and set the expectations on what it should do and how before you start to code. Evaluate this once more when you do a self-review.&lt;/li&gt;
&lt;li&gt;You should be able to draw a simple diagram and explain your solution to some colleague. This will help you find any possible problems before you start to code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Code &amp;amp; Design
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Does it do what it is supposed to do?&lt;/li&gt;
&lt;li&gt;Did you remove everything that is not supposed to be there?&lt;/li&gt;
&lt;li&gt;Aren't there any debug prints or &lt;code&gt;TODO&lt;/code&gt; comments you want to do or remove?&lt;/li&gt;
&lt;li&gt;Is the branch up-to-date with master?&lt;/li&gt;
&lt;li&gt;Isn't there any conceivable way this could break other parts of the system?&lt;/li&gt;
&lt;li&gt;Check all of the possible edge cases and have a solution for them.&lt;/li&gt;
&lt;li&gt;Is the code easy to read and understand? Did you choose the best names and organise things as cleanly as possible?

&lt;ul&gt;
&lt;li&gt;name should not involve any historical context&lt;/li&gt;
&lt;li&gt;name should be clear to any developer who passes by or sees the code for the first time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Isn't the functionality you've added already implemented somewhere else? If it's duplicated, reconsider the design.

&lt;ul&gt;
&lt;li&gt;if you cannot figure out how to deduplicate the code, try drawing a diagram of the system and find the pure underlying abstraction - there is always an answer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Is there a single source of truth to every piece of logic?&lt;/li&gt;
&lt;li&gt;Ask yourself - How much expertise and context will anybody reading this code need to understand?&lt;/li&gt;
&lt;li&gt;Did you introduce any new abstractions?

&lt;ul&gt;
&lt;li&gt;Don't they cause additional complexity that can be achieved without the abstraction?&lt;/li&gt;
&lt;li&gt;Are you able to draw a simple diagram of the system?&lt;/li&gt;
&lt;li&gt;Did you ever see similar abstraction in some other software?&lt;/li&gt;
&lt;li&gt;Is it as simple as possible?&lt;/li&gt;
&lt;li&gt;Will it be easy to expand with more features? Have you thought about what other features we could add there and if it's possible?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Is there anything you are not sure about? Just ask.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Frontend
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Did you use already implemented ui components?&lt;/li&gt;
&lt;li&gt;Is UI visually consistent? (font size, line height, margin, padding)&lt;/li&gt;
&lt;li&gt;Can you find similar UI in other software?

&lt;ul&gt;
&lt;li&gt;Our's shouldn't be different, else explain why it is so.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;If you added any complex multi-step flow, then you should be able to draw a diagram of it and explain it including possible branches and error validation.&lt;/li&gt;
&lt;li&gt;Does it look OK on phone?&lt;/li&gt;
&lt;li&gt;How is the copy? Are sentences well-formed and clear? Any spelling error or typos?&lt;/li&gt;
&lt;li&gt;What if the request to the backend fails?&lt;/li&gt;
&lt;li&gt;Did you make any changes to the component in the &lt;code&gt;ui&lt;/code&gt; or &lt;code&gt;components&lt;/code&gt;? Check if all usages of the component still work as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Backend
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Aren't there any possible security threats?&lt;/li&gt;
&lt;li&gt;Isn't any private data exposed to the public? Is the public part of the app available to the public?&lt;/li&gt;
&lt;li&gt;Aren't there any circular module dependencies?&lt;/li&gt;
&lt;li&gt;Did you add any inline import?

&lt;ul&gt;
&lt;li&gt;Why it's so? Are you able to draw a simple diagram of the system and explain?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Models should not have any methods which doesn't really belong there.&lt;/li&gt;
&lt;li&gt;Are there any issues that will become problems in the next months or years?

&lt;ul&gt;
&lt;li&gt;Thing them out and discuss them with the reviewer in the merge request.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Does the new classes or modules have only single responsibility?&lt;/li&gt;
&lt;li&gt;Is it possible to write tests for this functionality?&lt;/li&gt;
&lt;li&gt;What if anything fails? Prevent side-effects before changes are committed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migrations checklist
&lt;/h2&gt;

&lt;p&gt;Understand that a migration can corrupt data and it is extremely difficult (sometimes impossible) to fix it. A single wrong migration can result in several man-days burnt in vain. We've been through this. It's not nice. Be careful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrations should be always, reversible. When it's data transformation, you must write reverse transformation.&lt;/li&gt;
&lt;li&gt;Never delete a column that was previously used, instead keep the field in the model and mark it with comment &lt;code&gt;# DEPRECATED in version vX.Y.Z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Never delete data that is used – if you need to transform data, create a new column and do the transformation there, keep the original data in the original column, use deprecation comment or prepend &lt;code&gt;old_&lt;/code&gt; to the column&lt;/li&gt;
&lt;li&gt;If you have experimented with database schema before settling for a final solution, squash your migrations&lt;/li&gt;
&lt;li&gt;Be extra careful about migrating large tables – there may be millions of rows in that tables and not all operations are safe to run, because it would take too long and fail

&lt;ul&gt;
&lt;li&gt;in that case, create async task which should be run after the code is deployed. Pay attention on that the task should be resumable, because it can be interrupted by failure or another deploy.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Always use &lt;code&gt;queryset.iterator()&lt;/code&gt;, which will prevent memory issues&lt;/li&gt;
&lt;li&gt;Log migration progress by step of reasonable size&lt;/li&gt;
&lt;li&gt;When doing data transformation, create some sample data, so you can correctly handle the edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Peer review
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://chrome.google.com/webstore/detail/gitlab-unresolved-threads/dhmmedpdnmhmdehopnihlpiapdgakkld"&gt;this chrome extension&lt;/a&gt; to easily see unresolved threads on merge requests. &lt;em&gt;(Gitlab only)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Check all of the points above and create merge request threads for any violations.&lt;/li&gt;
&lt;li&gt;Merge request threads should not be resolved without any comment, because then the reviewer needs to check the resolution by himself and it's a waste of time.&lt;/li&gt;
&lt;li&gt;You should ask the reviewer to review your merge request, it will be merged faster. If it takes too long, the author will lose context of the issue and it will take more time to him and the reviewer fix and review it again.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Feedback is much appreciated.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>codequality</category>
      <category>django</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Project idea: An app measuring time spent waiting for code changes</title>
      <dc:creator>Kryštof Řeháček</dc:creator>
      <pubDate>Fri, 04 Sep 2020 19:56:00 +0000</pubDate>
      <link>https://forem.com/krystofee/project-idea-an-app-measuring-time-spent-waiting-for-code-changes-3gf6</link>
      <guid>https://forem.com/krystofee/project-idea-an-app-measuring-time-spent-waiting-for-code-changes-3gf6</guid>
      <description>&lt;p&gt;Have you ever thought how much time do you spend waiting on your code to compile? On your local development server to reload or refreshing your browser for some UI change in your code?&lt;/p&gt;

&lt;p&gt;I've been thinking about it a lot for past few days, because it bothers me to spend time just waiting and looking on spinning loaders.&lt;/p&gt;

&lt;p&gt;Yeah, sure, there are things like hot module replacement to boost your productivity, but that's NOT what I want to talk about.&lt;/p&gt;

&lt;p&gt;I want to talk about the question - &lt;strong&gt;How much time do I actually spend waiting for these things every day.&lt;/strong&gt; And sure, I've been thinking how to measure it. &lt;/p&gt;

&lt;p&gt;What if I made an app to measure these reload times, on the server-side (your local server) and client-side (in the browser). Which could show you at the end of the day that you spend 38 minutes just waiting for your code to compile? &lt;strong&gt;Would you be interested in using it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are some other interesting things you could get from the data, like automatically measuring your time in work, which is another thing I would like to know.&lt;/p&gt;

&lt;p&gt;You might be wondering about the technical implementation. I can explain it in another post if it has shown to be interesting.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>timemanagement</category>
      <category>projectidea</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>I'm learning Go the hard way</title>
      <dc:creator>Kryštof Řeháček</dc:creator>
      <pubDate>Mon, 08 Jul 2019 22:21:31 +0000</pubDate>
      <link>https://forem.com/krystofee/i-m-learning-go-the-hard-way-507h</link>
      <guid>https://forem.com/krystofee/i-m-learning-go-the-hard-way-507h</guid>
      <description>&lt;p&gt;This is my first blog post &lt;em&gt;ever&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is not tutorial to Golang&lt;/strong&gt;, but my story why I chosen to start learning Go and how I do it.&lt;/p&gt;

&lt;p&gt;Few days ago, I've decided to implement rest api client for RegioJet (which is one of biggest Czech private carriers, &lt;a href="https://www.regiojet.com"&gt;link&lt;/a&gt;). My first plan was to write it in Python, which I think I'm getting pretty good at it, but I've already done many rest api integrations and... there is nothing new to be surprised of when doing it again in the same environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals of this project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Learning how to make web app in Golang.&lt;/li&gt;
&lt;li&gt;Having web app which will unify seat and ticket reservation across many transport carriers. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine that you want to take busy train connection at afternoon which is full at the morning. I know many people that are buying tickets a month before. I'm not really that kind of guy. I like to decide when I commute at the earliest one day at the advance or, even better, on that day. If you ever experienced this, you may noticed that trains are sold out that the time you've decided. And if so, then you may notice that some people are likely to sold their tickets few hours before. I would rather say it &lt;em&gt;is a rule than an exception that some people sell their tickets on the day of the trip&lt;/em&gt; and if you check the website of the carrier often, you can be lucky to get their seat. This is what I do and ahis also does many other people and if you are not fast enough you won't be the lucky guy. &lt;br&gt;
My vision is that I will choose when and where I want to go and it will handle the reservation of the ticket for me. Yes, it is not 100% reliable that I would be able to get a seat, but it can be handled by some other carriers which do not require seat reservation (means you can ride even if the train is completely sold out).&lt;/p&gt;
&lt;h2&gt;
  
  
  Why in Go?
&lt;/h2&gt;

&lt;p&gt;I think I'm getting pretty good at web development using Python and Django. Nowadays I have 2 years of experience with Django and DRF on a daily basis (to boast, I recently made a contribution to DRF :) ). &lt;br&gt;
I could definitely make this project in Django without learning anything new, just to have the final product. Getting things done is hard, and the output will be just an app, without learning much new. &lt;br&gt;
So more exciting way to do, will be writing it in a language that I don't know at the time I'm writing this post (except some basics). The development will be much more interesting, and even if I do not finish this project, I will learn a lot. &lt;/p&gt;
&lt;h2&gt;
  
  
  Let's jump into it
&lt;/h2&gt;

&lt;p&gt;I'will start with building the backend (as expected from this post about my go). My plan is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;write api client package using mainly standard library&lt;/li&gt;
&lt;li&gt;choose framework and create web REST backend (Beego or Gin)&lt;/li&gt;
&lt;li&gt;create simple frontend in React &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most interesting thing will be first point in my oppinion. Second point will be just creating some endpoints that will interact with my client. And the third point will be just writing a simple frontend in React, which I already know.&lt;/p&gt;
&lt;h2&gt;
  
  
  My first HTTP requests in Go
&lt;/h2&gt;

&lt;p&gt;I've found &lt;a href="https://medium.com/@masnun/making-http-requests-in-golang-dd123379efe7"&gt;this&lt;/a&gt; article from Abu Ashraf Masnun which clearly explains how to create simple GET, POST and other types of HTTP requests.&lt;br&gt;
With that knowledge I can try interacting with the RegioJet API for the first time. There is their public api &lt;a href="https://app.swaggerhub.com/apis/regiojet/ybus/1.0.0"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main.go file is our app entrypoint for now. When running &lt;code&gt;go run main.go locations.go&lt;/code&gt;, the &lt;code&gt;func main()&lt;/code&gt; is called. We call there &lt;code&gt;GetCountries&lt;/code&gt; exported from &lt;code&gt;locations.go&lt;/code&gt; which will be explained later. And then I loop over each country in returned countries and print their code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.go&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running RegioJet API client"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetCountries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;countries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later in &lt;code&gt;location.go&lt;/code&gt; are defined location structs (&lt;code&gt;Country&lt;/code&gt;, &lt;code&gt;City&lt;/code&gt;, &lt;code&gt;Station&lt;/code&gt;)  according to the RegioJet models. The &lt;strong&gt;supercool&lt;/strong&gt; thing about parsing json in golang is that you can define structs and then use &lt;code&gt;json.Unmarshall(data []byte, v interface{}) error&lt;/code&gt; to parse the json. I find is super useful, because the result is already typed and you immidiately know what you are working with. (I'm not used to this since I work primarily with Python and Javascript, where both of them are loosely typed languages)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;locations.go&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;endpointUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://brn-ybus-pubapi.sa.cz/restapi/consts/locations"&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Station&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;ID&lt;/span&gt;                 &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"id"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;               &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;Fullname&lt;/span&gt;           &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"fullname"`&lt;/span&gt;
    &lt;span class="n"&gt;Aliases&lt;/span&gt;            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"aliases"`&lt;/span&gt;
    &lt;span class="n"&gt;Address&lt;/span&gt;            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"address"`&lt;/span&gt;
    &lt;span class="n"&gt;StationTypes&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"stationTypes"`&lt;/span&gt;
    &lt;span class="n"&gt;IataCode&lt;/span&gt;           &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"iataCode"`&lt;/span&gt;
    &lt;span class="n"&gt;StationURL&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"stationUrl"`&lt;/span&gt;
    &lt;span class="n"&gt;WheelChairPlatform&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"wheelChairPlatform"`&lt;/span&gt;
    &lt;span class="n"&gt;Significance&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"significance"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;City&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;ID&lt;/span&gt;            &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"id"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;          &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;Aliases&lt;/span&gt;       &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;  &lt;span class="s"&gt;`json:"aliases"`&lt;/span&gt;
    &lt;span class="n"&gt;StationsTypes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;  &lt;span class="s"&gt;`json:"stationTypes"`&lt;/span&gt;
    &lt;span class="n"&gt;Stations&lt;/span&gt;      &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Station&lt;/span&gt; &lt;span class="s"&gt;`json:"stations"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Country&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;Country&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"country"`&lt;/span&gt;
    &lt;span class="n"&gt;Code&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"code"`&lt;/span&gt;
    &lt;span class="n"&gt;Cities&lt;/span&gt;  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;City&lt;/span&gt; &lt;span class="s"&gt;`json:"cities"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Countries&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetCountries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Countries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"... getting countries"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpointUrl&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;byteBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;countries&lt;/span&gt; &lt;span class="n"&gt;Countries&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;byteBody&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;countries&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalln&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;countries&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 notice that I name all struct members explicitly for json even If I'm not supposed to, because it's taken by convention by the struct member name, so for example &lt;code&gt;Country.Code&lt;/code&gt; without explicit &lt;code&gt;json:"code"&lt;/code&gt; will match &lt;code&gt;code&lt;/code&gt; or even &lt;code&gt;cOdE&lt;/code&gt;. But I consider naming it this way as good practise and prevention from large refactoring, because all struct members are not dependent on the keys received in the JSON response.&lt;/p&gt;

&lt;p&gt;Last but not least, let's run the code I've shown above...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&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;$ &lt;/span&gt;go run locations.go main.go
2019/07/08 22:19:50 Running RegioJet API client
2019/07/08 22:19:50 ... getting countries
DE
BE
CH
LU
HR
IT
FR
UA
HU
AT
UK
CZ
SK
PL
RO
NL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result is as expected country code per line. &lt;/p&gt;




&lt;p&gt;Many new things to learn and many things learned. My first experience with golang was more than pleasing. I thought of it as more C-like language without any knowledge of it. This was really smooth. &lt;/p&gt;

&lt;p&gt;I will skip the implementation of other endpoints and directly jump into how I query fetched data and I will try to think about the core functionality like getting free seats and trying to make seat reservation. &lt;/p&gt;

</description>
      <category>go</category>
      <category>sideprojects</category>
      <category>transport</category>
      <category>api</category>
    </item>
  </channel>
</rss>
